mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 21:13:31 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into received-message
This commit is contained in:
commit
2a416ebc61
733 changed files with 40834 additions and 20287 deletions
.gitattributesBUILD.mdBUILD_ANDROID.mdBUILD_OSX.mdBUILD_WIN.mdCMakeLists.txt
assignment-client
CMakeLists.txt
src
cmake
externals
boostconfig
bullet
faceshift
glew
openvr
sdl2
sixense
zlib
macros
AddDependencyExternalProjects.cmakeCopyDllsBesideWindowsExecutable.cmakeSetupHifiPlugin.cmakeTargetFaceshift.cmakeTargetSDL2.cmakeTargetSixense.cmakeTargetZlib.cmake
modules
templates
domain-server
resources
src
examples
acScripts
avatarMover
away.jsbreakdanceCore.jsbreakdanceToy.jsclap.jscontrollers
defaultScripts.jsdrylake
edit.jsentityScripts
breakdanceEntity.jscreateParamsEntity.jscreateRecorder.jsparamsEntity.jsrecordingEntityScript.jsrecordingMaster.jssynchronizerEntityScript.jssynchronizerMaster.js
example
27
.gitattributes
vendored
Normal file
27
.gitattributes
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
*.cfg text
|
||||
*.cpp text
|
||||
*.css text
|
||||
*.c text
|
||||
*.frag text
|
||||
*.fst text
|
||||
*.h text
|
||||
*.html text
|
||||
*.ini text
|
||||
*.json text
|
||||
*.js text
|
||||
*.qml text
|
||||
*.slf text
|
||||
*.slh text
|
||||
*.slv text
|
||||
*.ts text
|
||||
*.txt text
|
||||
*.vert text
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.dds binary
|
||||
*.fbx binary
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.svg binary
|
||||
*.ttf binary
|
||||
*.wav binary
|
40
BUILD.md
40
BUILD.md
|
@ -1,29 +1,34 @@
|
|||
###Dependencies
|
||||
|
||||
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2
|
||||
* [Qt](http://www.qt.io/download-open-source) ~> 5.4.1
|
||||
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
|
||||
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2
|
||||
* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1
|
||||
* [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m
|
||||
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
|
||||
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
|
||||
* [zlib](http://www.zlib.net/)
|
||||
|
||||
####CMake External Project Dependencies
|
||||
|
||||
* [boostconfig](https://github.com/boostorg/config) ~> 1.58
|
||||
* [Bullet Physics Engine](https://code.google.com/p/bullet/downloads/list) ~> 2.82
|
||||
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
|
||||
* [Faceshift](http://www.faceshift.com/) ~> 4.3
|
||||
* [GLEW](http://glew.sourceforge.net/)
|
||||
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
|
||||
* [gverb](https://github.com/highfidelity/gverb)
|
||||
|
||||
The following external projects are optional dependencies. You can indicate to CMake that you would like to include them by passing -DGET_$NAME=1 when running a clean CMake build. For example, to get CMake to download and compile SDL2 you would pass -DGET_SDL2=1.
|
||||
|
||||
* [Oculus SDK](https://developer.oculus.com/downloads/) ~> 0.6 (Win32) / 0.5 (Mac / Linux)
|
||||
* [oglplus](http://oglplus.org/) ~> 0.63
|
||||
* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only)
|
||||
* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1
|
||||
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
|
||||
* Enables game controller support in Interface
|
||||
* [soxr](http://soxr.sourceforge.net) ~> 0.1.1
|
||||
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
|
||||
* [Sixense](http://sixense.com/) ~> 071615
|
||||
* [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only)
|
||||
|
||||
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project.
|
||||
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project.
|
||||
|
||||
These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder.
|
||||
|
||||
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DGET_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
|
||||
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
|
||||
|
||||
###OS Specific Build Guides
|
||||
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
|
||||
|
@ -37,12 +42,12 @@ Hifi uses CMake to generate build files and project files for your platform.
|
|||
####Qt
|
||||
In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation.
|
||||
|
||||
For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
For example, a Qt5 5.5.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
|
||||
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
####Generating build files
|
||||
|
@ -59,11 +64,11 @@ Any variables that need to be set for CMake to find dependencies can be set as E
|
|||
|
||||
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
|
||||
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/lib/cmake
|
||||
|
||||
####Finding Dependencies
|
||||
|
||||
The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DGET_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
|
||||
The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
|
||||
|
||||
You can point our [Cmake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency.
|
||||
|
||||
|
@ -77,5 +82,4 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
|
||||
####Devices
|
||||
|
||||
You can support external input/output devices such as Oculus Rift, Leap Motion, Faceshift, MIDI, Razr Hydra and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
|
||||
|
||||
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
|
||||
|
|
|
@ -14,7 +14,7 @@ You will need the following tools to build our Android targets.
|
|||
* Install the latest Platform-tools
|
||||
* Install the latest Build-tools
|
||||
* Install the SDK Platform for API Level 19
|
||||
* Install Sources for Android SDK for API Level 19
|
||||
* Install Sources for Android SDK for API Level 19
|
||||
* Install the ARM EABI v7a System Image if you want to run an emulator.
|
||||
|
||||
You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.
|
||||
|
@ -25,7 +25,7 @@ You will also need to cross-compile the dependencies required for all platforms
|
|||
|
||||
####ANDROID_LIB_DIR
|
||||
|
||||
Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies.
|
||||
Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies.
|
||||
|
||||
This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake.
|
||||
|
||||
|
@ -45,7 +45,7 @@ The original instructions to compile OpenSSL for Android from your host environm
|
|||
|
||||
Download the [OpenSSL source](https://www.openssl.org/source/) and extract the tarball inside your `ANDROID_LIB_DIR`. Rename the extracted folder to `openssl`.
|
||||
|
||||
You will need the [setenv-android.sh script](http://wiki.openssl.org/index.php/File:Setenv-android.sh) from the OpenSSL wiki.
|
||||
You will need the [setenv-android.sh script](http://wiki.openssl.org/index.php/File:Setenv-android.sh) from the OpenSSL wiki.
|
||||
|
||||
You must change three values at the top of the `setenv-android.sh` script - `_ANDROID_NDK`, `_ANDROID_EABI` and `_ANDROID_API`.
|
||||
`_ANDROID_NDK` should be `android-ndk-r10`, `_ANDROID_EABI` should be `arm-linux-androidebi-4.9` and `_ANDROID_API` should be `19`.
|
||||
|
@ -62,8 +62,8 @@ source setenv-android.sh
|
|||
Then, from the OpenSSL directory, run the following commands.
|
||||
|
||||
```
|
||||
perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org
|
||||
./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir=/usr/local/ssl/$ANDROID_API
|
||||
perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org
|
||||
./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir=/usr/local/ssl/$ANDROID_API
|
||||
make depend
|
||||
make all
|
||||
```
|
||||
|
@ -78,7 +78,7 @@ The Oculus Mobile SDK is optional, for Gear VR support. It is not required to co
|
|||
|
||||
Download the [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) and extract the archive inside your `ANDROID_LIB_DIR` folder. Rename the extracted folder to `libovr`.
|
||||
|
||||
From the VRLib directory, use ndk-build to build VrLib.
|
||||
From the VRLib directory, use ndk-build to build VrLib.
|
||||
|
||||
```
|
||||
cd VRLib
|
||||
|
@ -107,4 +107,4 @@ The following must be set in your environment:
|
|||
|
||||
The following must be passed to CMake when it is run:
|
||||
|
||||
* USE_ANDROID_TOOLCHAIN - set to true to build for Android
|
||||
* USE_ANDROID_TOOLCHAIN - set to true to build for Android
|
||||
|
|
13
BUILD_OSX.md
13
BUILD_OSX.md
|
@ -3,20 +3,15 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
###Homebrew
|
||||
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple.
|
||||
|
||||
brew tap highfidelity/homebrew-formulas
|
||||
brew install cmake openssl
|
||||
brew install highfidelity/formulas/qt5
|
||||
brew link qt5 --force
|
||||
brew install cmake openssl qt5
|
||||
|
||||
We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository.
|
||||
|
||||
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
|
||||
We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for.
|
||||
|
||||
###Qt
|
||||
|
||||
Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.4.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
||||
Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
|
||||
|
||||
###Xcode
|
||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
||||
|
|
20
BUILD_WIN.md
20
BUILD_WIN.md
|
@ -16,7 +16,7 @@ If using Visual Studio 2013 and building as a Visual Studio 2013 project you nee
|
|||
|
||||
####nmake
|
||||
|
||||
Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required.
|
||||
Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required.
|
||||
|
||||
We expect nmake.exe to be located at the following path.
|
||||
|
||||
|
@ -29,19 +29,19 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit
|
|||
|
||||
* [Download the online installer](http://qt-project.org/downloads)
|
||||
* When it asks you to select components, ONLY select the following:
|
||||
* Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL**
|
||||
* Qt > Qt 5.5.1 > **msvc2013 32-bit**
|
||||
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe)
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
|
||||
|
||||
Once Qt is installed, you need to manually configure the following:
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory.
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` directory.
|
||||
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
|
||||
|
||||
###External Libraries
|
||||
|
||||
As it stands, Hifi/Interface is a 32-bit application, so all libraries should also be 32-bit.
|
||||
|
||||
CMake will need to know where the headers and libraries for required external dependencies are.
|
||||
CMake will need to know where the headers and libraries for required external dependencies are.
|
||||
|
||||
We use CMake's `fixup_bundle` to find the DLLs all of our exectuable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required.
|
||||
|
||||
|
@ -75,16 +75,6 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
|
|||
|
||||
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
|
||||
|
||||
####zlib
|
||||
|
||||
Install zlib from
|
||||
|
||||
[Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
|
||||
and fix a header file, as described here:
|
||||
|
||||
[zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/)
|
||||
|
||||
###Build High Fidelity using Visual Studio
|
||||
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.
|
||||
|
||||
|
|
|
@ -185,22 +185,8 @@ set(EXTERNAL_PROJECT_PREFIX "project")
|
|||
set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
|
||||
setup_externals_binary_dir()
|
||||
|
||||
# setup options to grab external project dependencies
|
||||
option(GET_BULLET "Get Bullet library automatically as external project" 1)
|
||||
option(GET_GLM "Get GLM library automatically as external project" 1)
|
||||
option(GET_GVERB "Get Gverb library automatically as external project" 1)
|
||||
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
|
||||
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
|
||||
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
|
||||
option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
|
||||
option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
|
||||
option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
|
||||
option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
|
||||
option(GET_GLEW "Get GLEW library automatically as external project" 1)
|
||||
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
|
||||
option(GET_SDL2 "Get SDL2 library automatically as external project" 0)
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs("${QT_DIR}/bin")
|
||||
|
@ -217,6 +203,7 @@ if (NOT ANDROID)
|
|||
add_subdirectory(interface)
|
||||
set_target_properties(interface PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(plugins)
|
||||
add_subdirectory(tools)
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
set(TARGET_NAME assignment-client)
|
||||
|
||||
setup_hifi_project(Core Gui Network Script Widgets WebSockets)
|
||||
setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree environment gpu model fbx entities
|
||||
networking animation shared script-engine embedded-webserver
|
||||
physics
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics
|
||||
)
|
||||
|
||||
include_application_version()
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AvatarHashMap.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -24,10 +25,14 @@
|
|||
#include <SoundCache.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
|
||||
#include <WebSocketServerClass.h>
|
||||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
|
@ -45,6 +50,9 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<recording::Deck>();
|
||||
DependencyManager::set<recording::Recorder>();
|
||||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
||||
|
@ -93,7 +101,7 @@ void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, Sh
|
|||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
_receivedAudioStream.parseData(*message);
|
||||
|
@ -104,22 +112,27 @@ void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
|||
}
|
||||
|
||||
const QString AGENT_LOGGING_NAME = "agent";
|
||||
const int PING_INTERVAL = 1000;
|
||||
|
||||
void Agent::run() {
|
||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||
|
||||
// Setup MessagesClient
|
||||
auto messagesClient = DependencyManager::set<MessagesClient>();
|
||||
QThread* messagesThread = new QThread;
|
||||
messagesThread->setObjectName("Messages Client Thread");
|
||||
messagesClient->moveToThread(messagesThread);
|
||||
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||
messagesThread->start();
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet()
|
||||
<< NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer
|
||||
<< NodeType::MessagesMixer
|
||||
);
|
||||
|
||||
_pingTimer = new QTimer(this);
|
||||
connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests()));
|
||||
_pingTimer->start(PING_INTERVAL);
|
||||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QUrl scriptURL;
|
||||
if (_payload.isEmpty()) {
|
||||
|
@ -166,7 +179,7 @@ void Agent::run() {
|
|||
|
||||
// give this AvatarData object to the script engine
|
||||
setAvatarData(&scriptedAvatar, "Avatar");
|
||||
|
||||
|
||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
_scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data());
|
||||
|
||||
|
@ -226,6 +239,8 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
}
|
||||
|
||||
if (!_isAvatar) {
|
||||
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(nullptr);
|
||||
|
||||
if (_avatarIdentityTimer) {
|
||||
_avatarIdentityTimer->stop();
|
||||
delete _avatarIdentityTimer;
|
||||
|
@ -243,6 +258,7 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
void Agent::setAvatarData(AvatarData* avatarData, const QString& objectName) {
|
||||
_avatarData = avatarData;
|
||||
_scriptEngine->registerGlobalObject(objectName, avatarData);
|
||||
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(avatarData);
|
||||
}
|
||||
|
||||
void Agent::sendAvatarIdentityPacket() {
|
||||
|
@ -267,7 +283,10 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
|
||||
QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
|
||||
_avatarData->doneEncoding(true);
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
|
||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
|
||||
avatarPacket->writePrimitive(sequenceNumber++);
|
||||
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
||||
|
@ -369,28 +388,6 @@ void Agent::aboutToFinish() {
|
|||
_scriptEngine->stop();
|
||||
}
|
||||
|
||||
if (_pingTimer) {
|
||||
_pingTimer->stop();
|
||||
delete _pingTimer;
|
||||
}
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);
|
||||
}
|
||||
|
||||
void Agent::sendPingRequests() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool {
|
||||
switch (node->getType()) {
|
||||
case NodeType::AvatarMixer:
|
||||
case NodeType::AudioMixer:
|
||||
case NodeType::EntityServer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}, [nodeList](const SharedNodePointer& node) {
|
||||
nodeList->sendPacket(nodeList->constructPingPacket(), *node);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -58,14 +58,12 @@ private slots:
|
|||
void handleAudioPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void sendPingRequests();
|
||||
void processAgentAvatarAndAudio(float deltaTime);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ScriptEngine> _scriptEngine;
|
||||
EntityEditPacketSender _entityEditSender;
|
||||
EntityTreeHeadlessViewer _entityViewer;
|
||||
QTimer* _pingTimer;
|
||||
|
||||
MixedAudioStream _receivedAudioStream;
|
||||
float _lastReceivedAudioLoudness;
|
||||
|
|
|
@ -24,15 +24,24 @@ AssignmentAction::~AssignmentAction() {
|
|||
}
|
||||
|
||||
void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const {
|
||||
simulation->removeAction(_id);
|
||||
withReadLock([&]{
|
||||
simulation->removeAction(_id);
|
||||
simulation->applyActionChanges();
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AssignmentAction::serialize() const {
|
||||
return _data;
|
||||
QByteArray result;
|
||||
withReadLock([&]{
|
||||
result = _data;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssignmentAction::deserialize(QByteArray serializedArguments) {
|
||||
_data = serializedArguments;
|
||||
withWriteLock([&]{
|
||||
_data = serializedArguments;
|
||||
});
|
||||
}
|
||||
|
||||
bool AssignmentAction::updateArguments(QVariantMap arguments) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
class AssignmentAction : public EntityActionInterface {
|
||||
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
|
||||
public:
|
||||
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
|
|
|
@ -74,7 +74,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
// make sure we output process IDs for a child AC otherwise it's insane to parse
|
||||
LogHandler::getInstance().setShouldOutputPID(true);
|
||||
LogHandler::getInstance().setShouldOutputProcessID(true);
|
||||
|
||||
// setup our _requestAssignment member variable from the passed arguments
|
||||
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
|
||||
|
@ -198,7 +198,7 @@ void AssignmentClient::sendStatusPacketToACM() {
|
|||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
if (!_currentAssignment) {
|
||||
if (!_currentAssignment && !_isAssigned) {
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -229,8 +229,9 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
||||
|
||||
if (_currentAssignment) {
|
||||
if (_currentAssignment && !_isAssigned) {
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||
_isAssigned = true;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -309,12 +310,11 @@ void AssignmentClient::handleAuthenticationRequest() {
|
|||
}
|
||||
|
||||
void AssignmentClient::assignmentCompleted() {
|
||||
|
||||
// we expect that to be here the previous assignment has completely cleaned up
|
||||
assert(_currentAssignment.isNull());
|
||||
|
||||
// reset our current assignment pointer to NULL now that it has been deleted
|
||||
_currentAssignment = NULL;
|
||||
// reset our current assignment pointer to null now that it has been deleted
|
||||
_currentAssignment = nullptr;
|
||||
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
@ -330,4 +330,6 @@ void AssignmentClient::assignmentCompleted() {
|
|||
nodeList->setOwnerType(NodeType::Unassigned);
|
||||
nodeList->reset();
|
||||
nodeList->resetNodeInterestSet();
|
||||
|
||||
_isAssigned = false;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ private:
|
|||
|
||||
Assignment _requestAssignment;
|
||||
QPointer<ThreadedAssignment> _currentAssignment;
|
||||
bool _isAssigned { false };
|
||||
QString _assignmentServerHostname;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
QTimer _requestTimer; // timer for requesting and assignment
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv)
|
||||
{
|
||||
// to work around the Qt constant wireless scanning, set the env for polling interval very high
|
||||
const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
|
||||
|
||||
# ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
# endif
|
||||
|
|
|
@ -44,7 +44,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
||||
|
||||
// make sure we output process IDs for a monitor otherwise it's insane to parse
|
||||
LogHandler::getInstance().setShouldOutputPID(true);
|
||||
LogHandler::getInstance().setShouldOutputProcessID(true);
|
||||
|
||||
// create a NodeList so we can receive stats from children
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "avatars/AvatarMixer.h"
|
||||
#include "entities/EntityServer.h"
|
||||
#include "assets/AssetServer.h"
|
||||
#include "messages/MessagesMixer.h"
|
||||
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
|
||||
|
||||
|
@ -36,6 +37,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message
|
|||
return new EntityServer(message);
|
||||
case Assignment::AssetServerType:
|
||||
return new AssetServer(message);
|
||||
case Assignment::MessagesMixerType:
|
||||
return new MessagesMixer(message);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,9 @@ AvatarMixer::~AvatarMixer() {
|
|||
_broadcastThread.wait();
|
||||
}
|
||||
|
||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
|
||||
// An 80% chance of sending a identity packet within a 5 second interval.
|
||||
// assuming 60 htz update rate.
|
||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
|
||||
|
||||
// NOTE: some additional optimizations to consider.
|
||||
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
||||
|
@ -243,6 +245,46 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
return;
|
||||
}
|
||||
|
||||
// make sure we send out identity and billboard packets to and from new arrivals.
|
||||
bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
|
||||
|
||||
// we will also force a send of billboard or identity packet
|
||||
// if either has changed in the last frame
|
||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
|
||||
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
|
||||
|
||||
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
|
||||
billboardPacket->write(rfcUUID);
|
||||
billboardPacket->write(billboard);
|
||||
|
||||
nodeList->sendPacket(std::move(billboardPacket), *node);
|
||||
|
||||
++_sumBillboardPackets;
|
||||
}
|
||||
|
||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
|
||||
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
|
||||
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||
|
||||
identityPacket->write(individualData);
|
||||
|
||||
nodeList->sendPacket(std::move(identityPacket), *node);
|
||||
|
||||
++_sumIdentityPackets;
|
||||
}
|
||||
|
||||
AvatarData& otherAvatar = otherNodeData->getAvatar();
|
||||
// Decide whether to send this avatar's data based on it's distance from us
|
||||
|
||||
|
@ -291,53 +333,11 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
||||
numAvatarDataBytes +=
|
||||
avatarPacketList->write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO));
|
||||
avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO));
|
||||
|
||||
avatarPacketList->endSegment();
|
||||
|
||||
// if the receiving avatar has just connected make sure we send out the mesh and billboard
|
||||
// for this avatar (assuming they exist)
|
||||
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
// we will also force a send of billboard or identity packet
|
||||
// if either has changed in the last frame
|
||||
|
||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
|
||||
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
|
||||
|
||||
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
|
||||
billboardPacket->write(rfcUUID);
|
||||
billboardPacket->write(billboard);
|
||||
|
||||
nodeList->sendPacket(std::move(billboardPacket), *node);
|
||||
|
||||
++_sumBillboardPackets;
|
||||
}
|
||||
|
||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
|
||||
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
|
||||
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||
|
||||
identityPacket->write(individualData);
|
||||
|
||||
nodeList->sendPacket(std::move(identityPacket), *node);
|
||||
|
||||
++_sumIdentityPackets;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// close the current packet so that we're always sending something
|
||||
avatarPacketList->closeCurrentPacket(true);
|
||||
|
||||
|
@ -484,7 +484,7 @@ void AvatarMixer::sendStatsPacket() {
|
|||
|
||||
// add the key to ask the domain-server for a username replacement, if it has it
|
||||
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||
|
||||
|
||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
|
||||
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth();
|
||||
|
||||
|
@ -537,7 +537,7 @@ void AvatarMixer::run() {
|
|||
qDebug() << "Waiting for domain settings from domain-server.";
|
||||
|
||||
// block until we get the settingsRequestComplete signal
|
||||
|
||||
|
||||
QEventLoop loop;
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
|
|
|
@ -21,10 +21,12 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
|||
return _avatar.parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
|
||||
}
|
||||
|
||||
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
|
||||
bool oldValue = _hasReceivedFirstPackets;
|
||||
_hasReceivedFirstPackets = true;
|
||||
return oldValue;
|
||||
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) {
|
||||
if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) {
|
||||
_hasReceivedFirstPacketsFrom.insert(uuid);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||
|
@ -45,9 +47,9 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond();
|
||||
jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
|
||||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||
|
||||
|
||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||
|
||||
|
||||
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
@ -34,25 +35,25 @@ class AvatarMixerClientData : public NodeData {
|
|||
public:
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
AvatarData& getAvatar() { return _avatar; }
|
||||
|
||||
bool checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||
|
||||
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
|
||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||
|
||||
|
||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||
|
||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||
|
||||
|
||||
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
||||
|
||||
|
||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||
float getFullRateDistance() const { return _fullRateDistance; }
|
||||
|
||||
|
||||
void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; }
|
||||
float getMaxAvatarDistance() const { return _maxAvatarDistance; }
|
||||
|
||||
|
@ -73,31 +74,31 @@ public:
|
|||
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
||||
|
||||
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
|
||||
|
||||
|
||||
float getOutboundAvatarDataKbps() const
|
||||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||
|
||||
|
||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
private:
|
||||
AvatarData _avatar;
|
||||
|
||||
|
||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||
|
||||
bool _hasReceivedFirstPackets = false;
|
||||
quint64 _billboardChangeTimestamp = 0;
|
||||
quint64 _identityChangeTimestamp = 0;
|
||||
|
||||
|
||||
float _fullRateDistance = FLT_MAX;
|
||||
float _maxAvatarDistance = FLT_MAX;
|
||||
|
||||
|
||||
int _numAvatarsSentLastFrame = 0;
|
||||
int _numFramesSinceAdjustment = 0;
|
||||
|
||||
SimpleMovingAverage _otherAvatarStarves;
|
||||
SimpleMovingAverage _otherAvatarSkips;
|
||||
int _numOutOfOrderSends = 0;
|
||||
|
||||
|
||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||
};
|
||||
|
||||
|
|
|
@ -73,24 +73,20 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
|
||||
const float frameFraction = glm::fract(currentFrame);
|
||||
|
||||
for (int i = 0; i < modelJoints.size(); i++) {
|
||||
int mapping = animationJoints.indexOf(modelJoints[i]);
|
||||
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
|
||||
JointData& data = _jointData[i];
|
||||
for (int i = 0; i < animationJoints.size(); i++) {
|
||||
const QString& name = animationJoints[i];
|
||||
int mapping = getJointIndex(name);
|
||||
if (mapping != -1 && !_maskedJoints.contains(name)) {
|
||||
JointData& data = _jointData[mapping];
|
||||
|
||||
auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
auto newTranslation = floorFrame.translations.at(i) * (1.0f - frameFraction) +
|
||||
ceilFrame.translations.at(i) * frameFraction;
|
||||
|
||||
// We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in),
|
||||
// but we don't do so for MyAvatar yet, so let's not be different here.
|
||||
if (data.rotation != newRotation) {
|
||||
data.rotation = newRotation;
|
||||
data.rotationSet = true;
|
||||
}
|
||||
if (data.translation != newTranslation) {
|
||||
data.translation = newTranslation;
|
||||
data.translationSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_animation.clear();
|
||||
|
|
|
@ -18,17 +18,13 @@
|
|||
|
||||
class EntityNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
EntityNodeData() :
|
||||
OctreeQueryNode(),
|
||||
_lastDeletedEntitiesSentAt(0) { }
|
||||
|
||||
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
|
||||
|
||||
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||
|
||||
private:
|
||||
quint64 _lastDeletedEntitiesSentAt;
|
||||
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityNodeData_h
|
||||
|
|
|
@ -82,52 +82,164 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) {
|
|||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt);
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
if (shouldSendDeletedEntities) {
|
||||
int elapsed = usecTimestampNow() - deletedEntitiesSentAt;
|
||||
qDebug() << "shouldSendDeletedEntities to node:" << node->getUUID() << "deletedEntitiesSentAt:" << deletedEntitiesSentAt << "elapsed:" << elapsed;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return shouldSendDeletedEntities;
|
||||
}
|
||||
|
||||
// FIXME - most of the old code for this was encapsulated in EntityTree, I liked that design from a data
|
||||
// hiding and object oriented perspective. But that didn't really allow us to handle the case of lots
|
||||
// of entities being deleted at the same time. I'd like to look to move this back into EntityTree but
|
||||
// for now this works and addresses the bug.
|
||||
int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
|
||||
int totalBytes = 0;
|
||||
|
||||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||
quint64 deletePacketSentAt = usecTimestampNow();
|
||||
|
||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||
quint64 considerEntitiesSince = EntityTree::getAdjustedConsiderSince(deletedEntitiesSentAt);
|
||||
|
||||
quint64 deletePacketSentAt = usecTimestampNow();
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
bool hasMoreToSend = true;
|
||||
auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs();
|
||||
|
||||
packetsSent = 0;
|
||||
|
||||
while (hasMoreToSend) {
|
||||
auto specialPacket = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt,
|
||||
hasMoreToSend);
|
||||
// create a new special packet
|
||||
std::unique_ptr<NLPacket> deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||
|
||||
queryNode->packetSent(*specialPacket);
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
deletesPacket->writePrimitive(flags);
|
||||
|
||||
totalBytes += specialPacket->getDataSize();
|
||||
packetsSent++;
|
||||
// pack in sequence number
|
||||
auto sequenceNumber = queryNode->getSequenceNumber();
|
||||
deletesPacket->writePrimitive(sequenceNumber);
|
||||
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(specialPacket), *node);
|
||||
}
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
deletesPacket->writePrimitive(now);
|
||||
|
||||
// figure out where we are now and pack a temporary number of IDs
|
||||
uint16_t numberOfIDs = 0;
|
||||
qint64 numberOfIDsPos = deletesPacket->pos();
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
|
||||
// deleted since we last sent to this node
|
||||
auto it = recentlyDeleted.constBegin();
|
||||
while (it != recentlyDeleted.constEnd()) {
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (it.key() > considerEntitiesSince) {
|
||||
|
||||
// get all the IDs for this timestamp
|
||||
const auto& entityIDsFromTime = recentlyDeleted.values(it.key());
|
||||
|
||||
for (const auto& entityID : entityIDsFromTime) {
|
||||
|
||||
// check to make sure we have room for one more ID, if we don't have more
|
||||
// room, then send out this packet and create another one
|
||||
if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) {
|
||||
|
||||
// replace the count for the number of included IDs
|
||||
deletesPacket->seek(numberOfIDsPos);
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// Send the current packet
|
||||
queryNode->packetSent(*deletesPacket);
|
||||
auto thisPacketSize = deletesPacket->getDataSize();
|
||||
totalBytes += thisPacketSize;
|
||||
packetsSent++;
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(deletesPacket), *node);
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize;
|
||||
#endif
|
||||
|
||||
|
||||
// create another packet
|
||||
deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||
|
||||
// pack in flags
|
||||
deletesPacket->writePrimitive(flags);
|
||||
|
||||
// pack in sequence number
|
||||
sequenceNumber = queryNode->getSequenceNumber();
|
||||
deletesPacket->writePrimitive(sequenceNumber);
|
||||
|
||||
// pack in timestamp
|
||||
deletesPacket->writePrimitive(now);
|
||||
|
||||
// figure out where we are now and pack a temporary number of IDs
|
||||
numberOfIDs = 0;
|
||||
numberOfIDsPos = deletesPacket->pos();
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
}
|
||||
|
||||
// FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server
|
||||
// to the client. These were causing "lost" entities like flashlights and laser pointers
|
||||
// now that we keep around some additional history of the erased entities and resend that
|
||||
// history for a longer time window, these entities are not "lost". But we haven't yet
|
||||
// found/fixed the underlying issue that caused bad UUIDs to be sent to some users.
|
||||
deletesPacket->write(entityID.toRfc4122());
|
||||
++numberOfIDs;
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID;
|
||||
#endif
|
||||
} // end for (ids)
|
||||
|
||||
} // end if (it.val > sinceLast)
|
||||
|
||||
|
||||
++it;
|
||||
} // end while
|
||||
|
||||
// replace the count for the number of included IDs
|
||||
deletesPacket->seek(numberOfIDsPos);
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// Send the current packet
|
||||
queryNode->packetSent(*deletesPacket);
|
||||
auto thisPacketSize = deletesPacket->getDataSize();
|
||||
totalBytes += thisPacketSize;
|
||||
packetsSent++;
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(deletesPacket), *node);
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize;
|
||||
#endif
|
||||
|
||||
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
|
||||
}
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
if (packetsSent > 0) {
|
||||
qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of "
|
||||
<< totalBytes << " total bytes to node:" << node->getUUID();
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
|
||||
void EntityServer::pruneDeletedEntities() {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
if (tree->hasAnyDeletedEntities()) {
|
||||
|
||||
quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&earliestLastDeletedEntitiesSent](const SharedNodePointer& node) {
|
||||
if (node->getLinkedData()) {
|
||||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||
|
@ -137,7 +249,6 @@ void EntityServer::pruneDeletedEntities() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent);
|
||||
}
|
||||
}
|
||||
|
@ -147,9 +258,13 @@ bool EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
|
|||
readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging);
|
||||
qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging));
|
||||
|
||||
bool wantTerseEditLogging = false;
|
||||
readOptionBool(QString("wantTerseEditLogging"), settingsSectionObject, wantTerseEditLogging);
|
||||
qDebug("wantTerseEditLogging=%s", debug::valueOf(wantTerseEditLogging));
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
tree->setWantEditLogging(wantEditLogging);
|
||||
tree->setWantTerseEditLogging(wantTerseEditLogging);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
134
assignment-client/src/messages/MessagesMixer.cpp
Normal file
134
assignment-client/src/messages/MessagesMixer.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// MessagesMixer.cpp
|
||||
// assignment-client/src/messages
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "MessagesMixer.h"
|
||||
|
||||
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
||||
|
||||
MessagesMixer::MessagesMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message)
|
||||
{
|
||||
// make sure we hear about node kills so we can tell the other nodes
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
}
|
||||
|
||||
MessagesMixer::~MessagesMixer() {
|
||||
}
|
||||
|
||||
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
for (auto& channel : _channelSubscribers) {
|
||||
channel.remove(killedNode->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(message->getType() == PacketType::MessagesData);
|
||||
|
||||
QByteArray packetData = message->getMessage();
|
||||
QBuffer packet{ &packetData };
|
||||
packet.open(QIODevice::ReadOnly);
|
||||
|
||||
quint16 channelLength;
|
||||
packet.read(reinterpret_cast<char*>(&channelLength), sizeof(channelLength));
|
||||
auto channelData = packet.read(channelLength);
|
||||
QString channel = QString::fromUtf8(channelData);
|
||||
|
||||
quint16 messageLength;
|
||||
packet.read(reinterpret_cast<char*>(&messageLength), sizeof(messageLength));
|
||||
auto messageData = packet.read(messageLength);
|
||||
QString messageRaw = QString::fromUtf8(messageData);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
|
||||
return node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
||||
_channelSubscribers[channel].contains(node->getUUID());
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
|
||||
auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true);
|
||||
|
||||
auto channelUtf8 = channel.toUtf8();
|
||||
quint16 channelLength = channelUtf8.length();
|
||||
packetList->writePrimitive(channelLength);
|
||||
packetList->write(channelUtf8);
|
||||
|
||||
auto messageUtf8 = messageRaw.toUtf8();
|
||||
quint16 messageLength = messageUtf8.length();
|
||||
packetList->writePrimitive(messageLength);
|
||||
packetList->write(messageUtf8);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *node);
|
||||
});
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(message->getType() == PacketType::MessagesSubscribe);
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
qDebug() << "Node [" << senderNode->getUUID() << "] subscribed to channel:" << channel;
|
||||
_channelSubscribers[channel] << senderNode->getUUID();
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(message->getType() == PacketType::MessagesUnsubscribe);
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
qDebug() << "Node [" << senderNode->getUUID() << "] unsubscribed from channel:" << channel;
|
||||
|
||||
if (_channelSubscribers.contains(channel)) {
|
||||
_channelSubscribers[channel].remove(senderNode->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - make these stats relevant
|
||||
void MessagesMixer::sendStatsPacket() {
|
||||
QJsonObject statsObject;
|
||||
QJsonObject messagesObject;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
// add stats for each listerner
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
QJsonObject messagesStats;
|
||||
|
||||
// add the key to ask the domain-server for a username replacement, if it has it
|
||||
messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||
messagesStats["outbound_kbps"] = node->getOutboundBandwidth();
|
||||
messagesStats["inbound_kbps"] = node->getInboundBandwidth();
|
||||
|
||||
messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats;
|
||||
});
|
||||
|
||||
statsObject["messages"] = messagesObject;
|
||||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||
}
|
||||
|
||||
void MessagesMixer::run() {
|
||||
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
// The messages-mixer currently does currently have any domain settings. If it did, they would be
|
||||
// synchronously grabbed here.
|
||||
}
|
41
assignment-client/src/messages/MessagesMixer.h
Normal file
41
assignment-client/src/messages/MessagesMixer.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// MessagesMixer.h
|
||||
// assignment-client/src/messages
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// The avatar mixer receives head, hand and positional data from all connected
|
||||
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MessagesMixer_h
|
||||
#define hifi_MessagesMixer_h
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
/// Handles assignments of type MessagesMixer - distribution of avatar data to various clients
|
||||
class MessagesMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessagesMixer(ReceivedMessage& message);
|
||||
~MessagesMixer();
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
void sendStatsPacket();
|
||||
|
||||
private slots:
|
||||
void handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
QHash<QString,QSet<QUuid>> _channelSubscribers;
|
||||
};
|
||||
|
||||
#endif // hifi_MessagesMixer_h
|
|
@ -240,6 +240,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
int packetsSent = 0;
|
||||
int totalBytesSent = 0;
|
||||
|
||||
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
|
||||
while (i != _singleSenderStats.end()) {
|
||||
|
@ -291,12 +292,15 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
packetsSent += nackPacketList->getNumPackets();
|
||||
|
||||
// send the list of nack packets
|
||||
nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
totalBytesSent += nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
OctreeSendThread::_totalPackets += packetsSent;
|
||||
OctreeSendThread::_totalBytes += totalBytesSent;
|
||||
|
||||
return packetsSent;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "OctreeSendThread.h"
|
||||
#include "OctreeServer.h"
|
||||
#include "OctreeServerConsts.h"
|
||||
#include "OctreeLogging.h"
|
||||
|
||||
quint64 startSceneSleepTime = 0;
|
||||
quint64 endSceneSleepTime = 0;
|
||||
|
@ -112,12 +113,15 @@ bool OctreeSendThread::process() {
|
|||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
||||
|
||||
quint64 OctreeSendThread::_usleepTime = 0;
|
||||
quint64 OctreeSendThread::_usleepCalls = 0;
|
||||
AtomicUIntStat OctreeSendThread::_usleepTime { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_usleepCalls { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalBytes { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalWastedBytes { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalPackets { 0 };
|
||||
|
||||
AtomicUIntStat OctreeSendThread::_totalSpecialBytes { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalSpecialPackets { 0 };
|
||||
|
||||
quint64 OctreeSendThread::_totalBytes = 0;
|
||||
quint64 OctreeSendThread::_totalWastedBytes = 0;
|
||||
quint64 OctreeSendThread::_totalPackets = 0;
|
||||
|
||||
int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
OctreeServer::didHandlePacketSend(this);
|
||||
|
@ -569,23 +573,27 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
OctreeServer::trackInsideTime((float)elapsedInsideUsecs);
|
||||
}
|
||||
|
||||
|
||||
if (somethingToSend) {
|
||||
qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
||||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
if (somethingToSend && _myServer->wantsVerboseDebug()) {
|
||||
qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
||||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
}
|
||||
|
||||
|
||||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) {
|
||||
int specialPacketsSent;
|
||||
int specialPacketsSent = 0;
|
||||
trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent);
|
||||
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
||||
truePacketsSent += specialPacketsSent;
|
||||
packetsSentThisInterval += specialPacketsSent;
|
||||
|
||||
_totalPackets += specialPacketsSent;
|
||||
_totalBytes += trueBytesSent;
|
||||
|
||||
_totalSpecialPackets += specialPacketsSent;
|
||||
_totalSpecialBytes += trueBytesSent;
|
||||
}
|
||||
|
||||
// Re-send packets that were nacked by the client
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#ifndef hifi_OctreeSendThread_h
|
||||
#define hifi_OctreeSendThread_h
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <OctreeElementBag.h>
|
||||
|
||||
|
@ -21,6 +23,8 @@
|
|||
|
||||
class OctreeServer;
|
||||
|
||||
using AtomicUIntStat = std::atomic<uintmax_t>;
|
||||
|
||||
/// Threaded processor for sending octree packets to a single client
|
||||
class OctreeSendThread : public GenericThread {
|
||||
Q_OBJECT
|
||||
|
@ -30,12 +34,15 @@ public:
|
|||
|
||||
void setIsShuttingDown();
|
||||
|
||||
static quint64 _totalBytes;
|
||||
static quint64 _totalWastedBytes;
|
||||
static quint64 _totalPackets;
|
||||
static AtomicUIntStat _totalBytes;
|
||||
static AtomicUIntStat _totalWastedBytes;
|
||||
static AtomicUIntStat _totalPackets;
|
||||
|
||||
static quint64 _usleepTime;
|
||||
static quint64 _usleepCalls;
|
||||
static AtomicUIntStat _totalSpecialBytes;
|
||||
static AtomicUIntStat _totalSpecialPackets;
|
||||
|
||||
static AtomicUIntStat _usleepTime;
|
||||
static AtomicUIntStat _usleepCalls;
|
||||
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
|
|
|
@ -415,6 +415,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
||||
quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
quint64 totalOutboundSpecialPackets = OctreeSendThread::_totalSpecialPackets;
|
||||
quint64 totalOutboundSpecialBytes = OctreeSendThread::_totalSpecialBytes;
|
||||
|
||||
statsString += QString(" Total Clients Connected: %1 clients\r\n")
|
||||
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
@ -606,6 +609,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Outbound Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString(" Total Outbound Special Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundSpecialPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Outbound Special Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundSpecialBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
statsString += QString(" Total Wasted Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString().sprintf(" Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
|
@ -625,6 +635,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString().sprintf("<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
|
||||
getMyServerName());
|
||||
quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount();
|
||||
float incomingPPS = _octreeInboundPacketProcessor->getIncomingPPS();
|
||||
float processedPPS = _octreeInboundPacketProcessor->getProcessedPPS();
|
||||
quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
|
||||
quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
|
||||
quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
|
||||
|
@ -639,11 +651,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
quint64 averageCreateTime = _tree->getAverageCreateTime();
|
||||
quint64 averageLoggingTime = _tree->getAverageLoggingTime();
|
||||
|
||||
int FLOAT_PRECISION = 3;
|
||||
|
||||
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n")
|
||||
statsString += QString(" Current Inbound Packets Queue: %1 packets \r\n")
|
||||
.arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Packets Queue Network IN: %1 PPS \r\n")
|
||||
.arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Packets Queue Processing OUT: %1 PPS \r\n")
|
||||
.arg(locale.toString(processedPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
@ -692,6 +709,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
totalElementsProcessed = senderStats.getTotalElementsProcessed();
|
||||
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
|
||||
|
||||
|
||||
auto received = senderStats._incomingEditSequenceNumberStats.getReceived();
|
||||
auto expected = senderStats._incomingEditSequenceNumberStats.getExpectedReceived();
|
||||
auto unreasonable = senderStats._incomingEditSequenceNumberStats.getUnreasonable();
|
||||
auto outOfOrder = senderStats._incomingEditSequenceNumberStats.getOutOfOrder();
|
||||
auto early = senderStats._incomingEditSequenceNumberStats.getEarly();
|
||||
auto late = senderStats._incomingEditSequenceNumberStats.getLate();
|
||||
auto lost = senderStats._incomingEditSequenceNumberStats.getLost();
|
||||
auto recovered = senderStats._incomingEditSequenceNumberStats.getRecovered();
|
||||
|
||||
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
|
@ -702,7 +729,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
(double)averageElementsPerPacket);
|
||||
statsString += QString(" Average Transit Time/Packet: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
|
||||
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Wait Lock Time/Packet: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
@ -711,6 +738,24 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString("\r\n Inbound Edit Packets --------------------------------\r\n");
|
||||
statsString += QString(" Received: %1\r\n")
|
||||
.arg(locale.toString(received).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Expected: %1\r\n")
|
||||
.arg(locale.toString(expected).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Unreasonable: %1\r\n")
|
||||
.arg(locale.toString(unreasonable).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Out of Order: %1\r\n")
|
||||
.arg(locale.toString(outOfOrder).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Early: %1\r\n")
|
||||
.arg(locale.toString(early).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Late: %1\r\n")
|
||||
.arg(locale.toString(late).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Lost: %1\r\n")
|
||||
.arg(locale.toString(lost).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Recovered: %1\r\n")
|
||||
.arg(locale.toString(recovered).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
}
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
|
@ -908,7 +953,6 @@ bool OctreeServer::readConfiguration() {
|
|||
|
||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||
setFinished(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1041,12 +1085,16 @@ void OctreeServer::run() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setOwnerType(getMyNodeType());
|
||||
|
||||
|
||||
// use common init to setup common timers and logging
|
||||
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
// read the configuration from either the payload or the domain server configuration
|
||||
if (!readConfiguration()) {
|
||||
qDebug() << "OctreeServer bailing on run since readConfiguration has failed.";
|
||||
setFinished(true);
|
||||
return; // bailing on run, because readConfiguration failed
|
||||
}
|
||||
|
||||
|
@ -1055,10 +1103,6 @@ void OctreeServer::run() {
|
|||
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
|
||||
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
|
@ -17,8 +17,14 @@
|
|||
#include <JurisdictionSender.h>
|
||||
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
const int INTERVALS_PER_SECOND = 60;
|
||||
|
||||
/// This is the frequency (hz) that we check the octree server for changes to determine if we need to
|
||||
/// send new "scene" information to the viewers. This will directly effect how quickly edits are
|
||||
/// sent down do viewers. By setting it to 90hz we allow edits happening at 90hz to be sent down
|
||||
/// to viewers at a rate more closely matching the edit rate. It would probably be better to allow
|
||||
/// clients to ask the server to send at a rate consistent with their current vsynch since clients
|
||||
/// can't render any faster than their vsynch even if the server sent them more acurate information
|
||||
const int INTERVALS_PER_SECOND = 90;
|
||||
const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
|
||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for sending octree elements
|
||||
|
||||
#endif // hifi_OctreeServerConsts_h
|
||||
|
|
3
cmake/externals/boostconfig/CMakeLists.txt
vendored
3
cmake/externals/boostconfig/CMakeLists.txt
vendored
|
@ -4,7 +4,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
#URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/config-boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
6
cmake/externals/bullet/CMakeLists.txt
vendored
6
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -17,7 +17,8 @@ include(ExternalProject)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
||||
# URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.zip
|
||||
URL_MD5 f5e8914fc9064ad32e0d62d19d33d977
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -28,7 +29,8 @@ if (WIN32)
|
|||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
||||
#URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.tgz
|
||||
URL_MD5 70b3c8d202dee91a0854b4cbc88173e8
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
30
cmake/externals/faceshift/CMakeLists.txt
vendored
Normal file
30
cmake/externals/faceshift/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
set(EXTERNAL_NAME faceshift)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory")
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/faceshift.lib CACHE FILEPATH "Faceshift libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/faceshift.lib CACHE FILEPATH "Faceshift libraries")
|
||||
elseif (APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
|
||||
endif()
|
5
cmake/externals/glew/CMakeLists.txt
vendored
5
cmake/externals/glew/CMakeLists.txt
vendored
|
@ -7,14 +7,15 @@ endif ()
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple.zip
|
||||
URL_MD5 0507dc08337a82a5e7ecbc5417f92cc1
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple2.zip
|
||||
URL_MD5 f05d858e8203c32b689da208ad8b39db
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
|
|
14
cmake/externals/openvr/CMakeLists.txt
vendored
14
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,7 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip
|
||||
#URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/openvr-0.9.1.zip
|
||||
URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -24,9 +25,14 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNA
|
|||
|
||||
if (WIN32)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
|
||||
# FIXME need to account for different architectures
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win64/openvr_api.lib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win64)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
|
||||
endif()
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
|
|
79
cmake/externals/sdl2/CMakeLists.txt
vendored
79
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -2,10 +2,12 @@ set(EXTERNAL_NAME sdl2)
|
|||
|
||||
include(ExternalProject)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-devel-2.0.3-VC.zip
|
||||
URL_MD5 30a333bcbe94bc5016e8799c73e86233
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -13,15 +15,33 @@ if (WIN32)
|
|||
LOG_DOWNLOAD 1
|
||||
)
|
||||
elseif (APPLE)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3-OSX.tar.gz
|
||||
URL_MD5 64f888886268bdf1656ef1b4b7d7756d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DVIDEO_OPENGL=OFF
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY "${INSTALL_DIR}/lib/libSDL2-2.0.dylib" CACHE STRING "Path to SDL2 library")
|
||||
|
||||
set(_SDL2_LIB_DIR "${INSTALL_DIR}/lib")
|
||||
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
COMMENT "Calling install_name_tool on SDL2 libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SDL2_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <INSTALL_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
else ()
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
|
@ -41,31 +61,22 @@ endif ()
|
|||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (APPLE)
|
||||
elseif (WIN32)
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library")
|
||||
else ()
|
||||
if (WIN32)
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
set(_ROOT_DIR ${SOURCE_DIR})
|
||||
set(_INCLUDE_DIR ${_ROOT_DIR}/include)
|
||||
set(_LIB_DIR "${SOURCE_DIR}/lib/x86")
|
||||
set(_LIB_EXT "lib")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of SDL2 DLL")
|
||||
else ()
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(_ROOT_DIR ${INSTALL_DIR})
|
||||
set(_INCLUDE_DIR ${_ROOT_DIR}/include/SDL2)
|
||||
|
||||
set(_LIB_DIR ${INSTALL_DIR}/lib)
|
||||
set(_LIB_EXT "so")
|
||||
set(_LIB_PREFIX "lib")
|
||||
endif ()
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${_INCLUDE_DIR} CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${_LIB_DIR}/${_LIB_PREFIX}SDL2.${_LIB_EXT} CACHE FILEPATH "Path to SDL2 library")
|
||||
endif ()
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x64/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x64 CACHE PATH "Location of SDL2 DLL")
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
||||
endif()
|
||||
|
||||
else ()
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
|
||||
endif ()
|
||||
|
|
79
cmake/externals/sixense/CMakeLists.txt
vendored
79
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -1,30 +1,32 @@
|
|||
include(ExternalProject)
|
||||
include(SelectLibraryConfigurations)
|
||||
|
||||
set(EXTERNAL_NAME Sixense)
|
||||
set(EXTERNAL_NAME sixense)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip")
|
||||
#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc")
|
||||
#set(SIXENSE_NEW_LAYOUT 0)
|
||||
|
||||
#set(SIXENSE_URL "http://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
|
||||
#set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
|
||||
#set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
|
||||
set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
|
||||
set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL ./SixenseSDK_062612.zip
|
||||
URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc
|
||||
URL ${SIXENSE_URL}
|
||||
URL_MD5 ${SIXENSE_URL_MD5}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
elseif (UNIX)
|
||||
find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
# find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
elseif (WIN32)
|
||||
endif ()
|
||||
|
||||
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
|
@ -39,22 +41,51 @@ if (WIN32)
|
|||
set(ARCH_DIR "Win32")
|
||||
set(ARCH_SUFFIX "")
|
||||
endif()
|
||||
|
||||
if (${SIXENSE_NEW_LAYOUT})
|
||||
# for 2015 SDKs (using the VS2013 versions may be causing the crash, so use the VS2010 versions)
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/VS2010/release_dll")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2010/release_dll")
|
||||
else()
|
||||
# for the 2012 SDK
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/release_dll")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll")
|
||||
endif()
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_DLL_PATH}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/lib/osx_x64/debug_dll/libsixensed_x64.dylib CACHE TYPE INTERNAL)
|
||||
|
||||
set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name-release
|
||||
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/release_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name-debug
|
||||
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/debug_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
elseif(NOT ANDROID)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
|
||||
|
||||
endif()
|
||||
|
||||
|
|
49
cmake/externals/zlib/CMakeLists.txt
vendored
49
cmake/externals/zlib/CMakeLists.txt
vendored
|
@ -1,28 +1,31 @@
|
|||
set(EXTERNAL_NAME zlib)
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
set(EXTERNAL_NAME zlib)
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
include(ExternalProject)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://zlib.net/zlib128.zip
|
||||
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://zlib.net/zlib128.zip
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of zlib include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of zlib include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE FILEPATH "Location of ZLib DLL")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/zlib.lib CACHE FILEPATH "Location of zlib release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/zlibd.lib CACHE FILEPATH "Location of zlib debug library")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(${EXTERNAL_NAME_UPPER})
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of zlib include directories")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} BINARY_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${BINARY_DIR}/Release CACHE FILEPATH "Location of GLEW DLL")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${BINARY_DIR}/Release/zlib.lib CACHE FILEPATH "Location of ZLib release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of ZLib debug library")
|
||||
|
||||
endif ()
|
||||
# Force selected libraries into the cache
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of zlib libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries")
|
||||
|
|
|
@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS)
|
|||
string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)
|
||||
|
||||
# has the user told us they specific don't want this as an external project?
|
||||
if (GET_${_PROJ_NAME_UPPER})
|
||||
if (NOT USE_LOCAL_${_PROJ_NAME_UPPER})
|
||||
# have we already detected we can't have this as external project on this OS?
|
||||
if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT)
|
||||
# have we already setup the target?
|
||||
|
|
|
@ -18,12 +18,19 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE)
|
|||
@ONLY
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins")
|
||||
else()
|
||||
set(PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
|
||||
# add a post-build command to copy DLLs beside the executable
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DBUNDLE_EXECUTABLE=$<TARGET_FILE:${TARGET_NAME}>
|
||||
-DBUNDLE_PLUGIN_DIR=$<TARGET_FILE_DIR:${TARGET_NAME}>/${PLUGIN_PATH}
|
||||
-P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake
|
||||
)
|
||||
|
||||
|
|
33
cmake/macros/SetupHifiPlugin.cmake
Normal file
33
cmake/macros/SetupHifiPlugin.cmake
Normal file
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2015/10/25
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(SETUP_HIFI_PLUGIN)
|
||||
set(${TARGET_NAME}_SHARED 1)
|
||||
setup_hifi_library(${ARGV})
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins")
|
||||
else()
|
||||
set(PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
|
||||
# create the destination for the plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET ${DIR} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
|
||||
)
|
||||
|
||||
endmacro()
|
14
cmake/macros/TargetFaceshift.cmake
Normal file
14
cmake/macros/TargetFaceshift.cmake
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_FACESHIFT)
|
||||
add_dependency_external_projects(faceshift)
|
||||
find_package(Faceshift REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES})
|
||||
add_definitions(-DHAVE_FACESHIFT)
|
||||
endmacro()
|
14
cmake/macros/TargetSDL2.cmake
Normal file
14
cmake/macros/TargetSDL2.cmake
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_SDL2)
|
||||
add_dependency_external_projects(sdl2)
|
||||
find_package(SDL2 REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR})
|
||||
target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY})
|
||||
add_definitions(-DHAVE_SDL2)
|
||||
endmacro()
|
14
cmake/macros/TargetSixense.cmake
Normal file
14
cmake/macros/TargetSixense.cmake
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_SIXENSE)
|
||||
add_dependency_external_projects(sixense)
|
||||
find_package(Sixense REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
|
||||
add_definitions(-DHAVE_SIXENSE)
|
||||
endmacro()
|
22
cmake/macros/TargetZlib.cmake
Normal file
22
cmake/macros/TargetZlib.cmake
Normal file
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_ZLIB)
|
||||
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(zlib)
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${ZLIB_DLL_PATH})
|
||||
endif()
|
||||
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
endmacro()
|
|
@ -18,32 +18,9 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("faceshift")
|
||||
|
||||
find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS})
|
||||
|
||||
if (APPLE)
|
||||
set(ARCH_DIR "MacOS")
|
||||
elseif (UNIX)
|
||||
set(ARCH_DIR "UNIX")
|
||||
elseif (WIN32)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
else()
|
||||
set(ARCH_DIR "Win32")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
|
||||
find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(FACESHIFT)
|
||||
|
||||
set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)
|
||||
|
||||
mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)
|
|
@ -30,6 +30,4 @@ include(SelectLibraryConfigurations)
|
|||
select_library_configurations(GLEW)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
|
||||
|
||||
message(STATUS "Found GLEW - Assuming that GLEW is static and defining GLEW_STATIC")
|
||||
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
|
|
@ -34,17 +34,26 @@ if (UNIX)
|
|||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||
)
|
||||
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win64"
|
||||
"C:/OpenSSL/" "C:/OpenSSL-Win32/" "C:/OpenSSL-Win64/"
|
||||
)
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||
)
|
||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/")
|
||||
else()
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||
)
|
||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/")
|
||||
endif()
|
||||
|
||||
unset(_programfiles)
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS})
|
||||
|
||||
else ()
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("openssl")
|
||||
|
|
|
@ -18,49 +18,10 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("sixense")
|
||||
|
||||
find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
|
||||
if (APPLE)
|
||||
find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
elseif (UNIX)
|
||||
find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
# find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
elseif (WIN32)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
set(ARCH_SUFFIX "_x64")
|
||||
else()
|
||||
set(ARCH_DIR "Win32")
|
||||
set(ARCH_SUFFIX "")
|
||||
endif()
|
||||
|
||||
find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
|
||||
find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(SIXENSE)
|
||||
|
||||
set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
|
||||
if (WIN32)
|
||||
list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH)
|
||||
endif ()
|
||||
|
||||
set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS})
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH})
|
||||
endif ()
|
||||
|
||||
find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
|
||||
mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS)
|
||||
|
|
|
@ -21,45 +21,17 @@ if (WIN32)
|
|||
hifi_library_search_hints("iViewHMD")
|
||||
|
||||
find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)
|
||||
|
||||
set(IVIEWHMD_DLLS
|
||||
avcodec-53.dll
|
||||
avformat-53.dll
|
||||
avutil-51.dll
|
||||
libboost_filesystem-mgw45-mt-1_49.dll
|
||||
libboost_system-mgw45-mt-1_49.dll
|
||||
libboost_thread-mgw45-mt-1_49.dll
|
||||
libgcc_s_dw2-1.dll
|
||||
libiViewNG-LibCore.dll
|
||||
libopencv_calib3d244.dll
|
||||
libopencv_core244.dll
|
||||
libopencv_features2d244.dll
|
||||
libopencv_flann244.dll
|
||||
libopencv_highgui244.dll
|
||||
libopencv_imgproc244.dll
|
||||
libopencv_legacy244.dll
|
||||
libopencv_ml244.dll
|
||||
libopencv_video244.dll
|
||||
libstdc++-6.dll
|
||||
opencv_core220.dll
|
||||
opencv_highgui220.dll
|
||||
opencv_imgproc220.dll
|
||||
swscale-2.dll
|
||||
)
|
||||
|
||||
foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS})
|
||||
find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH)
|
||||
list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH})
|
||||
endforeach()
|
||||
find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
|
||||
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})
|
||||
|
||||
add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS})
|
||||
add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATH_3RD_PARTY})
|
||||
|
||||
mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)
|
||||
|
||||
|
|
|
@ -10,5 +10,47 @@
|
|||
#
|
||||
|
||||
include(BundleUtilities)
|
||||
|
||||
|
||||
# replace copy_resolved_item_into_bundle
|
||||
#
|
||||
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
|
||||
# the resolved item matches the resolved embedded item. This not not really an issue that
|
||||
# should rise to the level of a "warning" so we replace this message with a "status:"
|
||||
#
|
||||
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
||||
if(WIN32)
|
||||
# ignore case on Windows
|
||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
||||
else()
|
||||
set(resolved_item_compare "${resolved_item}")
|
||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
||||
# this is our only change from the original version
|
||||
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
|
||||
else()
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@")
|
||||
|
||||
message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}")
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_EXTENSION "dylib")
|
||||
elseif (WIN32)
|
||||
set(PLUGIN_EXTENSION "dll")
|
||||
else()
|
||||
set(PLUGIN_EXTENSION "so")
|
||||
endif()
|
||||
|
||||
file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
|
|
@ -484,6 +484,14 @@
|
|||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "wantTerseEditLogging",
|
||||
"type": "checkbox",
|
||||
"label": "Edit Logging (Terse)",
|
||||
"help": "Logging of all edits to entities",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "verboseDebug",
|
||||
"type": "checkbox",
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
/**
|
||||
* Copyright (c) 2010 Maxim Vasiliev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author Maxim Vasiliev
|
||||
* Date: 09.09.2010
|
||||
* Time: 19:02:33
|
||||
*/
|
||||
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s<e.length;s++){f=e[s].value;if(t&&(f===""||f===null))continue;m=e[s].name;g=m.split(n);l=[];c=r;h="";for(o=0;o<g.length;o++){v=g[o].split("][");if(v.length>1){for(u=0;u<v.length;u++){if(u==0){v[u]=v[u]+"]"}else if(u==v.length-1){v[u]="["+v[u]}else{v[u]="["+v[u]+"]"}d=v[u].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);if(d){for(a=1;a<d.length;a++){if(d[a])l.push(d[a])}}else{l.push(v[u])}}}else l=l.concat(v)}for(o=0;o<l.length;o++){v=l[o];if(v.indexOf("[]")>-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o<l.length-1){if(!c[v])c[v]={};c=c[v]}else{c[v]=f}}}}return r}function n(e,t,n,s){var o=i(e,t,n,s);return o.length>0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i<s;i++){if(r[i].selected)n.push(r[i].value)}return n}return e})
|
||||
/**
|
||||
* Copyright (c) 2010 Maxim Vasiliev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author Maxim Vasiliev
|
||||
* Date: 09.09.2010
|
||||
* Time: 19:02:33
|
||||
*/
|
||||
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s<e.length;s++){f=e[s].value;if(t&&(f===""||f===null))continue;m=e[s].name;g=m.split(n);l=[];c=r;h="";for(o=0;o<g.length;o++){v=g[o].split("][");if(v.length>1){for(u=0;u<v.length;u++){if(u==0){v[u]=v[u]+"]"}else if(u==v.length-1){v[u]="["+v[u]}else{v[u]="["+v[u]+"]"}d=v[u].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);if(d){for(a=1;a<d.length;a++){if(d[a])l.push(d[a])}}else{l.push(v[u])}}}else l=l.concat(v)}for(o=0;o<l.length;o++){v=l[o];if(v.indexOf("[]")>-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o<l.length-1){if(!c[v])c[v]={};c=c[v]}else{c[v]=f}}}}return r}function n(e,t,n,s){var o=i(e,t,n,s);return o.length>0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i<s;i++){if(r[i].selected)n.push(r[i].value)}return n}return e})
|
||||
|
|
|
@ -244,4 +244,4 @@ B(this,"mouseOut");!c||a.stickyTracking||c.shared&&!this.noSharedTooltip||c.hide
|
|||
f=(c.visible=a=c.userOptions.visible=a===u?!h:a)?"show":"hide";t(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&t(d.series,function(a){a.options.stacking&&a.visible&&(a.isDirty=!0)});t(c.linkedSeries,function(b){b.setVisible(a,!1)});g&&(d.isDirtyBox=!0);!1!==b&&d.redraw();B(c,f)},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||
|
||||
e.len:this.chart.plotSizeX,h,k,l=[];if(!1!==this.options.enableMouseTracking&&!this.singularTooltips){a&&(this.tooltipPoints=null);t(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(k=0;k<a;k++)if(e=b[k],c=e.x,c>=f.min&&c<=f.max)for(h=b[k+1],c=d===u?0:d+1,d=b[k+1]?T(x(0,K((e.clientX+(h?h.wrappedClientX||h.clientX:g))/2)),g):g;0<=c&&c<=d;)l[c++]=e;this.tooltipPoints=l}},show:function(){this.setVisible(!0)},
|
||||
hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===u?!this.selected:a;this.checkbox&&(this.checkbox.checked=a);B(this,a?"select":"unselect")},drawTracker:$a.drawTrackerGraph});v(U,{Axis:ma,Chart:Ea,Color:za,Point:Ca,Tick:xa,Renderer:Ja,Series:fa,SVGElement:O,SVGRenderer:Ja,arrayMin:La,arrayMax:va,charts:Y,dateFormat:Ka,format:ua,pathAnim:ib,getOptions:function(){return M},hasBidiBug:Db,isTouchDevice:wb,numberFormat:ka,seriesTypes:P,setOptions:function(a){M=G(!0,M,a);pb();
|
||||
return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})();
|
||||
return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})();
|
||||
|
|
|
@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
|
|||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::EntityServer
|
||||
<< NodeType::AssetServer;
|
||||
<< NodeType::AssetServer
|
||||
<< NodeType::MessagesMixer;
|
||||
|
||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (message->getSize() == 0) {
|
||||
|
@ -66,7 +67,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
|
||||
static const NodeSet VALID_NODE_TYPES {
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer
|
||||
};
|
||||
|
||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||
|
@ -226,7 +227,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
// if the allowed editors list is empty then everyone can adjust locks
|
||||
bool canAdjustLocks = allowedEditors.empty();
|
||||
|
||||
if (allowedEditors.contains(username)) {
|
||||
if (allowedEditors.contains(username, Qt::CaseInsensitive)) {
|
||||
// we have a non-empty allowed editors list - check if this user is verified to be in it
|
||||
if (!verifiedUsername) {
|
||||
if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) {
|
||||
|
|
|
@ -62,6 +62,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
LogUtils::init();
|
||||
Setting::init();
|
||||
|
||||
// to work around the Qt constant wireless scanning, set the env for polling interval very high
|
||||
const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
|
||||
|
||||
connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit);
|
||||
|
||||
|
@ -268,6 +272,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
||||
|
||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||
|
@ -549,7 +554,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
defaultedType != Assignment::AllTypes;
|
||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||
if (!excludedTypes.contains(defaultedType)
|
||||
&& defaultedType != Assignment::UNUSED_0
|
||||
&& defaultedType != Assignment::UNUSED_1
|
||||
&& defaultedType != Assignment::AgentType) {
|
||||
|
||||
|
@ -1822,3 +1826,24 @@ void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> messag
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// This packet has been matched to a source node and they're asking not to be in the domain anymore
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
const QUuid& nodeUUID = message->getSourceID();
|
||||
|
||||
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;
|
||||
|
||||
if (limitedNodeList->killNodeWithUUID(nodeUUID)) {
|
||||
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
removedNodePacket->reset();
|
||||
removedNodePacket->write(nodeUUID.toRfc4122());
|
||||
|
||||
// broadcast out the DomainServerRemovedNode message
|
||||
limitedNodeList->eachNode([&limitedNodeList](const SharedNodePointer& otherNode){
|
||||
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ public slots:
|
|||
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
|
||||
void processPathQueryPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
|
||||
void processNodeDisconnectRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
private slots:
|
||||
void aboutToQuit();
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
|
||||
}
|
||||
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
|
||||
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
||||
|
||||
if (foundValue) {
|
||||
|
|
|
@ -16,11 +16,14 @@ var NUM_AC = 3; // This is the number of AC. Their ID need to be unique and betw
|
|||
var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1))
|
||||
|
||||
// Those variables MUST be common to every scripts
|
||||
var controlVoxelSize = 0.25;
|
||||
var controlVoxelPosition = { x: 2000 , y: 0, z: 0 };
|
||||
var controlEntitySize = 0.25;
|
||||
var controlEntityPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||
Script.include("libraries/toolBars.js");
|
||||
Script.include("../libraries/toolBars.js");
|
||||
|
||||
var clip_url = null;
|
||||
var input_text = null;
|
||||
|
||||
var DO_NOTHING = 0;
|
||||
var PLAY = 1;
|
||||
|
@ -28,14 +31,7 @@ var PLAY_LOOP = 2;
|
|||
var STOP = 3;
|
||||
var SHOW = 4;
|
||||
var HIDE = 5;
|
||||
|
||||
var COLORS = [];
|
||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||
COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||
|
||||
var LOAD = 6;
|
||||
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
|
@ -53,6 +49,7 @@ var onOffIcon = new Array();
|
|||
var playIcon = new Array();
|
||||
var playLoopIcon = new Array();
|
||||
var stopIcon = new Array();
|
||||
var loadIcon = new Array();
|
||||
setupToolBars();
|
||||
|
||||
|
||||
|
@ -104,6 +101,14 @@ function setupToolBars() {
|
|||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
loadIcon[i] = toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-upload.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
nameOverlays.push(Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 0, green: 0, blue: 0 },
|
||||
|
@ -124,30 +129,46 @@ function setupToolBars() {
|
|||
}
|
||||
|
||||
function sendCommand(id, action) {
|
||||
|
||||
if (action === SHOW) {
|
||||
toolBars[id].selectTool(onOffIcon[id], false);
|
||||
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, loadIcon[id]);
|
||||
} else if (action === HIDE) {
|
||||
toolBars[id].selectTool(onOffIcon[id], true);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, loadIcon[id]);
|
||||
} else if (toolBars[id].toolSelected(onOffIcon[id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id === toolBars.length - 1) {
|
||||
for (i = 0; i < NUM_AC; i++) {
|
||||
sendCommand(i, action);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Fix this to use some mechanism other than voxels
|
||||
//Voxels.setVoxel(controlVoxelPosition.x + id * controlVoxelSize, controlVoxelPosition.y, controlVoxelPosition.z,
|
||||
// controlVoxelSize, COLORS[action].red, COLORS[action].green, COLORS[action].blue);
|
||||
if (id === (toolBars.length - 1))
|
||||
id = -1;
|
||||
|
||||
var controlEntity = Entities.addEntity({
|
||||
name: 'New Actor Controller',
|
||||
type: "Box",
|
||||
color: { red: 0, green: 0, blue: 0 },
|
||||
position: controlEntityPosition,
|
||||
dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize },
|
||||
visible: false,
|
||||
lifetime: 10,
|
||||
userData: JSON.stringify({
|
||||
idKey: {
|
||||
uD_id: id
|
||||
},
|
||||
actionKey: {
|
||||
uD_action: action
|
||||
},
|
||||
clipKey: {
|
||||
uD_url: clip_url
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
@ -167,6 +188,12 @@ function mousePressEvent(event) {
|
|||
sendCommand(i, PLAY_LOOP);
|
||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
} else {
|
||||
// Check individual controls
|
||||
for (i = 0; i < NUM_AC; i++) {
|
||||
|
@ -182,6 +209,12 @@ function mousePressEvent(event) {
|
|||
sendCommand(i, PLAY_LOOP);
|
||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
|
@ -225,4 +258,4 @@ Controller.mousePressEvent.connect(mousePressEvent);
|
|||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
moveUI();
|
||||
moveUI();
|
|
@ -9,30 +9,25 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
// Set the following variables to the values needed
|
||||
var filename = HIFI_PUBLIC_BUCKET + "ozan/bartender.rec";
|
||||
var clip_url = null;
|
||||
var playFromCurrentLocation = true;
|
||||
var useDisplayName = true;
|
||||
var useAttachments = true;
|
||||
var useHeadModel = true;
|
||||
var useSkeletonModel = true;
|
||||
var useAvatarModel = true;
|
||||
|
||||
// ID of the agent. Two agents can't have the same ID.
|
||||
var id = 0;
|
||||
|
||||
// Set head and skeleton models
|
||||
Avatar.faceModelURL = "http://public.highfidelity.io/models/heads/EvilPhilip_v7.fst";
|
||||
Avatar.skeletonModelURL = "http://public.highfidelity.io/models/skeletons/Philip_Carl_Body_A-Pose.fst";
|
||||
// Set position/orientation/scale here if playFromCurrentLocation is true
|
||||
Avatar.position = { x:1, y: 1, z: 1 };
|
||||
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
Avatar.scale = 1.0;
|
||||
|
||||
// Those variables MUST be common to every scripts
|
||||
var controlVoxelSize = 0.25;
|
||||
var controlVoxelPosition = { x: 2000 , y: 0, z: 0 };
|
||||
var controlEntitySize = 0.25;
|
||||
var controlEntityPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||
var DO_NOTHING = 0;
|
||||
|
@ -41,121 +36,117 @@ var PLAY_LOOP = 2;
|
|||
var STOP = 3;
|
||||
var SHOW = 4;
|
||||
var HIDE = 5;
|
||||
var LOAD = 6;
|
||||
|
||||
var COLORS = [];
|
||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||
COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||
Recording.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Recording.setPlayerUseDisplayName(useDisplayName);
|
||||
Recording.setPlayerUseAttachments(useAttachments);
|
||||
Recording.setPlayerUseHeadModel(false);
|
||||
Recording.setPlayerUseSkeletonModel(useAvatarModel);
|
||||
|
||||
controlVoxelPosition.x += id * controlVoxelSize;
|
||||
function setupEntityViewer() {
|
||||
var entityViewerOffset = 10;
|
||||
var entityViewerPosition = { x: controlEntityPosition.x - entityViewerOffset,
|
||||
y: controlEntityPosition.y, z: controlEntityPosition.z };
|
||||
var entityViewerOrientation = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
|
||||
Avatar.loadRecording(filename);
|
||||
|
||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Avatar.setPlayerUseDisplayName(useDisplayName);
|
||||
Avatar.setPlayerUseAttachments(useAttachments);
|
||||
Avatar.setPlayerUseHeadModel(useHeadModel);
|
||||
Avatar.setPlayerUseSkeletonModel(useSkeletonModel);
|
||||
|
||||
function setupVoxelViewer() {
|
||||
var voxelViewerOffset = 10;
|
||||
var voxelViewerPosition = JSON.parse(JSON.stringify(controlVoxelPosition));
|
||||
voxelViewerPosition.x -= voxelViewerOffset;
|
||||
var voxelViewerOrientation = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
|
||||
VoxelViewer.setPosition(voxelViewerPosition);
|
||||
VoxelViewer.setOrientation(voxelViewerOrientation);
|
||||
VoxelViewer.queryOctree();
|
||||
EntityViewer.setPosition(entityViewerPosition);
|
||||
EntityViewer.setOrientation(entityViewerOrientation);
|
||||
EntityViewer.queryOctree();
|
||||
}
|
||||
|
||||
function getAction(controlVoxel) {
|
||||
if (controlVoxel.x != controlVoxelPosition.x ||
|
||||
controlVoxel.y != controlVoxelPosition.y ||
|
||||
controlVoxel.z != controlVoxelPosition.z ||
|
||||
controlVoxel.s != controlVoxelSize) {
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
for (i in COLORS) {
|
||||
if (controlVoxel.red === COLORS[i].red &&
|
||||
controlVoxel.green === COLORS[i].green &&
|
||||
controlVoxel.blue === COLORS[i].blue) {
|
||||
function getAction(controlEntity) {
|
||||
if (controlEntity === null) {
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
// TODO: Fix this to use some mechanism other than voxels
|
||||
//Voxels.eraseVoxel(controlVoxelPosition.x, controlVoxelPosition.y, controlVoxelPosition.z, controlVoxelSize);
|
||||
return parseInt(i);
|
||||
}
|
||||
}
|
||||
var userData = JSON.parse(Entities.getEntityProperties(controlEntity, ["userData"]).userData);
|
||||
|
||||
var uD_id = userData.idKey.uD_id;
|
||||
var uD_action = userData.actionKey.uD_action;
|
||||
var uD_url = userData.clipKey.uD_url;
|
||||
|
||||
Entities.deleteEntity((Entities.getEntityProperties(controlEntity)).id);
|
||||
|
||||
return DO_NOTHING;
|
||||
if (uD_id === id || uD_id === -1) {
|
||||
if (uD_action === 6)
|
||||
clip_url = uD_url;
|
||||
|
||||
return uD_action;
|
||||
} else {
|
||||
return DO_NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
count = 300; // This is necessary to wait for the audio mixer to connect
|
||||
count = 100; // This is necessary to wait for the audio mixer to connect
|
||||
function update(event) {
|
||||
VoxelViewer.queryOctree();
|
||||
if (count > 0) {
|
||||
count--;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Fix this to use some mechanism other than voxels
|
||||
// Voxels.getVoxelAt(controlVoxelPosition.x, controlVoxelPosition.y, controlVoxelPosition.z, controlVoxelSize);
|
||||
var controlVoxel = false;
|
||||
var action = getAction(controlVoxel);
|
||||
|
||||
switch(action) {
|
||||
case PLAY:
|
||||
print("Play");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Avatar.isPlaying()) {
|
||||
Avatar.startPlaying();
|
||||
}
|
||||
Avatar.setPlayerLoop(false);
|
||||
break;
|
||||
case PLAY_LOOP:
|
||||
print("Play loop");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Avatar.isPlaying()) {
|
||||
Avatar.startPlaying();
|
||||
}
|
||||
Avatar.setPlayerLoop(true);
|
||||
break;
|
||||
case STOP:
|
||||
print("Stop");
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.stopPlaying();
|
||||
}
|
||||
break;
|
||||
case SHOW:
|
||||
print("Show");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
break;
|
||||
case HIDE:
|
||||
print("Hide");
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.stopPlaying();
|
||||
}
|
||||
Agent.isAvatar = false;
|
||||
break;
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
default:
|
||||
print("Unknown action: " + action);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.play();
|
||||
}
|
||||
EntityViewer.queryOctree();
|
||||
if (count > 0) {
|
||||
count--;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var controlEntity = Entities.findClosestEntity(controlEntityPosition, controlEntitySize);
|
||||
var action = getAction(controlEntity);
|
||||
|
||||
switch(action) {
|
||||
case PLAY:
|
||||
print("Play");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Recording.isPlaying()) {
|
||||
Recording.startPlaying();
|
||||
}
|
||||
Recording.setPlayerLoop(false);
|
||||
break;
|
||||
case PLAY_LOOP:
|
||||
print("Play loop");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Recording.isPlaying()) {
|
||||
Recording.startPlaying();
|
||||
}
|
||||
Recording.setPlayerLoop(true);
|
||||
break;
|
||||
case STOP:
|
||||
print("Stop");
|
||||
if (Recording.isPlaying()) {
|
||||
Recording.stopPlaying();
|
||||
}
|
||||
break;
|
||||
case SHOW:
|
||||
print("Show");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
break;
|
||||
case HIDE:
|
||||
print("Hide");
|
||||
if (Recording.isPlaying()) {
|
||||
Recording.stopPlaying();
|
||||
}
|
||||
Agent.isAvatar = false;
|
||||
break;
|
||||
case LOAD:
|
||||
print("Load");
|
||||
if(clip_url !== null) {
|
||||
Recording.loadRecording(clip_url);
|
||||
}
|
||||
break;
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
default:
|
||||
print("Unknown action: " + action);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Recording.isPlaying()) {
|
||||
Recording.play();
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
setupVoxelViewer();
|
||||
setupEntityViewer();
|
||||
|
|
|
@ -14,8 +14,7 @@ var filename = "http://your.recording.url";
|
|||
var playFromCurrentLocation = true;
|
||||
var loop = true;
|
||||
|
||||
Avatar.faceModelURL = "http://public.highfidelity.io/models/heads/EvilPhilip_v7.fst";
|
||||
Avatar.skeletonModelURL = "http://public.highfidelity.io/models/skeletons/Philip_Carl_Body_A-Pose.fst";
|
||||
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998";
|
||||
|
||||
// Set position here if playFromCurrentLocation is true
|
||||
Avatar.position = { x:1, y: 1, z: 1 };
|
||||
|
@ -23,30 +22,34 @@ Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
|||
Avatar.scale = 1.0;
|
||||
|
||||
Agent.isAvatar = true;
|
||||
|
||||
|
||||
Avatar.loadRecording(filename);
|
||||
|
||||
count = 300; // This is necessary to wait for the audio mixer to connect
|
||||
function update(event) {
|
||||
if (count > 0) {
|
||||
count--;
|
||||
return;
|
||||
}
|
||||
if (count == 0) {
|
||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Avatar.setPlayerLoop(loop);
|
||||
Avatar.startPlaying();
|
||||
Avatar.play();
|
||||
Vec3.print("Playing from ", Avatar.position);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.play();
|
||||
} else {
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
if (count > 0) {
|
||||
count--;
|
||||
return;
|
||||
}
|
||||
if (count == 0) {
|
||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Avatar.setPlayerLoop(loop);
|
||||
Avatar.setPlayerUseDisplayName(true);
|
||||
Avatar.setPlayerUseAttachments(true);
|
||||
Avatar.setPlayerUseHeadModel(false);
|
||||
Avatar.setPlayerUseSkeletonModel(true);
|
||||
Avatar.startPlaying();
|
||||
Avatar.play();
|
||||
Vec3.print("Playing from ", Avatar.position);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.play();
|
||||
} else {
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
|
|
29
examples/acScripts/animatedAvatarAgent.js
Normal file
29
examples/acScripts/animatedAvatarAgent.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
"use strict";
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*global Agent, Avatar, Script, Entities, Vec3, print*/
|
||||
//
|
||||
// animatedAvatar.js
|
||||
// examples/acScripts
|
||||
//
|
||||
// Created by Howard Stearns 11/6/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'.
|
||||
// In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'.
|
||||
|
||||
var origin = {x: 500, y: 502, z: 500};
|
||||
var spread = 10; // meters
|
||||
var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35};
|
||||
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy
|
||||
Avatar.displayName = "'Bot";
|
||||
var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start.
|
||||
|
||||
Agent.isAvatar = true;
|
||||
Script.setTimeout(function () {
|
||||
Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread});
|
||||
print("Starting at", JSON.stringify(Avatar.position));
|
||||
Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame);
|
||||
}, millisecondsToWaitBeforeStarting);
|
188
examples/avatarMover/avatarMover.js
Normal file
188
examples/avatarMover/avatarMover.js
Normal file
|
@ -0,0 +1,188 @@
|
|||
(function() {
|
||||
this.defaultRange = 5;
|
||||
this.acceleration = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
this.onColor = {
|
||||
red: 77,
|
||||
green: 11,
|
||||
blue: 111
|
||||
};
|
||||
this.offColor = {
|
||||
red: 200,
|
||||
green: 0,
|
||||
blue: 0
|
||||
};
|
||||
var self = this;
|
||||
//Default forward direction of mover object
|
||||
this.forward = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: -1
|
||||
};
|
||||
this.isMoving = false;
|
||||
this.velocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
this.defaultThrust = 500;
|
||||
this.maxRotMixVal = 0.01;
|
||||
this.minRotMixVal = this.maxRotMixVal * 0.5;
|
||||
this.minThrustPercentage = 0.2;
|
||||
this.userData = {};
|
||||
|
||||
|
||||
this.getUserData = function() {
|
||||
if (this.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateUserData = function() {
|
||||
Entities.editEntity(this.entityId, {
|
||||
userData: JSON.stringify(this.userData)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.toggleMover = function() {
|
||||
if (!this.userData.active) {
|
||||
this.activate();
|
||||
} else if (this.userData.active) {
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.clickReleaseOnEntity = function(entityId, mouseEvent) {
|
||||
this.entityId = entityId
|
||||
if (mouseEvent.isLeftButton) {
|
||||
this.toggleMover();
|
||||
}
|
||||
}
|
||||
|
||||
this.activate = function() {
|
||||
//activate a light at the movers position
|
||||
this.properties = Entities.getEntityProperties(this.entityId);
|
||||
this.getUserData();
|
||||
this.userData.active = true;
|
||||
this.initUserData();
|
||||
var lightPos = this.properties.position;
|
||||
lightPos.y += .1;
|
||||
this.light = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: lightPos,
|
||||
isSpotlight: false,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 2
|
||||
},
|
||||
color: this.onColor,
|
||||
intensity: 10
|
||||
// rotation: {x : 0, y: Math.PI/2, z: 0}
|
||||
});
|
||||
|
||||
this.field = Overlays.addOverlay("sphere", {
|
||||
position: this.properties.position,
|
||||
size: this.userData.range,
|
||||
solid: false,
|
||||
color: {
|
||||
red: 250,
|
||||
green: 10,
|
||||
blue: 10
|
||||
},
|
||||
})
|
||||
|
||||
//change color
|
||||
Entities.editEntity(this.entityId, {
|
||||
color: this.onColor,
|
||||
});
|
||||
}
|
||||
|
||||
this.initUserData = function() {
|
||||
this.userData.range = this.userData.range || this.defaultRange;
|
||||
this.userData.thrust = this.userData.thrust || this.defaultThrust;
|
||||
this.updateUserData();
|
||||
}
|
||||
|
||||
this.updateOverlays = function() {
|
||||
if (this.field) {
|
||||
Overlays.editOverlay(this.field, {
|
||||
size: this.userData.range
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.deactivate = function() {
|
||||
this.userData.active = false;
|
||||
this.updateUserData();
|
||||
Entities.editEntity(this.entityId, {
|
||||
color: this.offColor
|
||||
});
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
this.scriptEnding = function() {
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
self.getUserData();
|
||||
self.updateOverlays();
|
||||
if (!self.userData.active) {
|
||||
return;
|
||||
}
|
||||
self.distance = Vec3.distance(MyAvatar.position, self.properties.position);
|
||||
if (self.distance < self.userData.range) {
|
||||
self.rotationMixVal = map(self.distance, 0, self.userData.range, self.maxRotMixVal, self.minRotMixVal);
|
||||
|
||||
//We want to extract yaw from rotated object so avatars do not pith or roll, as they will be stuck that way.
|
||||
self.sanitizedRotation = Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(self.properties.rotation).y, 0);
|
||||
self.newOrientation = Quat.mix(MyAvatar.orientation, self.sanitizedRotation, self.rotationMixVal);
|
||||
MyAvatar.orientation = self.newOrientation;
|
||||
|
||||
self.rotatedDir = {
|
||||
x: self.forward.x,
|
||||
y: self.forward.y,
|
||||
z: self.forward.z
|
||||
};
|
||||
self.rotatedDir = Vec3.multiplyQbyV(self.properties.rotation, self.rotatedDir);
|
||||
|
||||
self.thrust = map(self.distance, 0, self.userData.range, self.userData.thrust, self.userData.thrust * self.minThrustPercentage);
|
||||
self.direction = Vec3.normalize(self.rotatedDir);
|
||||
self.velocity = Vec3.multiply(self.direction, self.thrust);
|
||||
MyAvatar.addThrust(Vec3.multiply(self.velocity, deltaTime));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
Script.update.disconnect(this.update);
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
|
||||
this.cleanUp = function() {
|
||||
Entities.deleteEntity(this.light);
|
||||
Overlays.deleteOverlay(this.field);
|
||||
}
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(this.scriptEnding);
|
||||
Script.update.connect(this.update);
|
||||
|
||||
});
|
18
examples/avatarMover/avatarMoverSpawner.js
Normal file
18
examples/avatarMover/avatarMoverSpawner.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx";
|
||||
var scriptURL = Script.resolvePath('avatarMover.js');
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var avatarMover = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
userData: JSON.stringify({range: 5}),
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(avatarMover);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
129
examples/away.js
Normal file
129
examples/away.js
Normal file
|
@ -0,0 +1,129 @@
|
|||
"use strict";
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*global HMD, AudioDevice, MyAvatar, Controller, Script, Overlays, print*/
|
||||
//
|
||||
// away.js
|
||||
// examples
|
||||
//
|
||||
// Created by Howard Stearns 11/3/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key.
|
||||
// See MAIN CONTROL, below, for what "paused" actually does.
|
||||
|
||||
var IK_WINDOW_AFTER_GOING_ACTIVE = 3000; // milliseconds
|
||||
var OVERLAY_DATA = {
|
||||
text: "Paused:\npress any key to continue",
|
||||
font: {size: 75},
|
||||
color: {red: 200, green: 255, blue: 255},
|
||||
alpha: 0.9
|
||||
};
|
||||
|
||||
// ANIMATION
|
||||
// We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect
|
||||
// using an animation graph with a state that we turn on and off through the animation var defined with that state.
|
||||
var awayAnimationHandlerId, activeAnimationHandlerId, stopper;
|
||||
function playAwayAnimation() {
|
||||
function animateAway() {
|
||||
return {isAway: true, isNotAway: false, isNotMoving: false, ikOverlayAlpha: 0.0};
|
||||
}
|
||||
if (stopper) {
|
||||
Script.clearTimeout(stopper);
|
||||
stopper = false;
|
||||
MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment
|
||||
}
|
||||
awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null);
|
||||
}
|
||||
function stopAwayAnimation() {
|
||||
MyAvatar.removeAnimationStateHandler(awayAnimationHandlerId);
|
||||
if (stopper) {
|
||||
print('WARNING: unexpected double stop');
|
||||
return;
|
||||
}
|
||||
// How do we know when to turn ikOverlayAlpha back on?
|
||||
// It cannot be as soon as we want to stop the away animation, because then things will look goofy as we come out of that animation.
|
||||
// (Imagine an away animation that sits or kneels, and then stands back up when coming out of it. If head is at the HMD, then it won't
|
||||
// want to track the standing up animation.)
|
||||
// Our standard anim graph flips 'awayOutroOnDone' for one frame, but it's a trigger (not an animVar) and other folks might use different graphs.
|
||||
// So... Just give us a fixed amount of time to be done with animation, before we turn ik back on.
|
||||
var backToNormal = false;
|
||||
stopper = Script.setTimeout(function () {
|
||||
backToNormal = true;
|
||||
stopper = false;
|
||||
}, IK_WINDOW_AFTER_GOING_ACTIVE);
|
||||
function animateActive(state) {
|
||||
if (state.ikOverlayAlpha) {
|
||||
// Once the right state gets reflected back to us, we don't need the hander any more.
|
||||
// But we are locked against handler changes during the execution of a handler, so remove asynchronously.
|
||||
Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0);
|
||||
}
|
||||
// It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes.
|
||||
return {isAway: false, isNotAway: true, ikOverlayAlpha: backToNormal ? 1.0 : 0.0}; // IWBNI we had a way of deleting an anim var.
|
||||
}
|
||||
activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway', 'isNotAway', 'isNotMoving', 'ikOverlayAlpha']);
|
||||
}
|
||||
|
||||
// OVERLAY
|
||||
var overlay = Overlays.addOverlay("text", OVERLAY_DATA);
|
||||
function showOverlay() {
|
||||
var screen = Controller.getViewportDimensions();
|
||||
Overlays.editOverlay(overlay, {visible: true, x: screen.x / 4, y: screen.y / 4});
|
||||
}
|
||||
function hideOverlay() {
|
||||
Overlays.editOverlay(overlay, {visible: false});
|
||||
}
|
||||
hideOverlay();
|
||||
|
||||
|
||||
// MAIN CONTROL
|
||||
var wasMuted, isAway;
|
||||
function goAway() {
|
||||
if (isAway) {
|
||||
return;
|
||||
}
|
||||
isAway = true;
|
||||
print('going "away"');
|
||||
wasMuted = AudioDevice.getMuted();
|
||||
if (!wasMuted) {
|
||||
AudioDevice.toggleMute();
|
||||
}
|
||||
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
|
||||
playAwayAnimation(); // animation is still seen by others
|
||||
showOverlay();
|
||||
}
|
||||
function goActive() {
|
||||
if (!isAway) {
|
||||
return;
|
||||
}
|
||||
isAway = false;
|
||||
print('going "active"');
|
||||
if (!wasMuted) {
|
||||
AudioDevice.toggleMute();
|
||||
}
|
||||
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
|
||||
stopAwayAnimation();
|
||||
hideOverlay();
|
||||
}
|
||||
Script.scriptEnding.connect(goActive);
|
||||
Controller.keyPressEvent.connect(function (event) {
|
||||
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
|
||||
return;
|
||||
}
|
||||
if (!isAway && (event.text === '.')) {
|
||||
goAway();
|
||||
} else {
|
||||
goActive();
|
||||
}
|
||||
});
|
||||
var wasHmdActive = false;
|
||||
Script.update.connect(function () {
|
||||
if (HMD.active !== wasHmdActive) {
|
||||
wasHmdActive = !wasHmdActive;
|
||||
if (wasHmdActive) {
|
||||
goAway();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
// breakdanceCore.js
|
||||
// examples/toys
|
||||
//
|
||||
// This is the core breakdance game library, it can be used as part of an entity script, or an omniTool module, or bootstapped on it's own
|
||||
// Created by Brad Hefta-Gaub on August 24, 2015
|
|
@ -11,7 +11,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
Script.include("libraries/utils.js");
|
||||
Script.include("breakdanceCore.js");
|
||||
breakdanceStart();
|
||||
Script.update.connect(breakdanceUpdate);
|
|
@ -71,8 +71,8 @@ function maybePlaySound(deltaTime) {
|
|||
var palm2Position = MyAvatar.getRightPalmPosition();
|
||||
var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position));
|
||||
|
||||
var palm1Velocity = Controller.getSpatialControlVelocity(1);
|
||||
var palm2Velocity = Controller.getSpatialControlVelocity(3);
|
||||
var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity;
|
||||
var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity;
|
||||
var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity));
|
||||
|
||||
const CLAP_SPEED = 0.7;
|
||||
|
|
72
examples/controllers/Spacemouse/spacemouseExample.js
Normal file
72
examples/controllers/Spacemouse/spacemouseExample.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// spaceMouseDebug.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
var firstmove = 1;
|
||||
var position = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var rotation = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
function toggleFirstMove() {
|
||||
if(firstmove){
|
||||
print("____________________________________");
|
||||
firstmove = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function spacemouseCheck() {
|
||||
return Controller.Hardware.Spacemouse !== undefined;
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
if(spacemouseCheck){
|
||||
if(Controller.getValue(Controller.Standard.LY) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller TY: " + Controller.getValue(Controller.Standard.LY));
|
||||
}
|
||||
|
||||
if(Controller.getValue(Controller.Standard.LX) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller RZ: " + Controller.getValue(Controller.Standard.LX));
|
||||
}
|
||||
|
||||
if(Controller.getValue(Controller.Standard.LB) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller LEFTB: " + Controller.getValue(Controller.Standard.LB));
|
||||
}
|
||||
|
||||
if(Controller.getValue(Controller.Standard.RY) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller TZ: " + Controller.getValue(Controller.Standard.RY));
|
||||
}
|
||||
|
||||
if(Controller.getValue(Controller.Standard.RX) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller TX: " + Controller.getValue(Controller.Standard.RX));
|
||||
}
|
||||
|
||||
if(Controller.getValue(Controller.Standard.RB) != 0){
|
||||
toggleFirstMove();
|
||||
print("- Controller RIGHTB: " + Controller.getValue(Controller.Standard.RB));
|
||||
}
|
||||
|
||||
firstmove = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
108
examples/controllers/controllerMappings.js
Normal file
108
examples/controllers/controllerMappings.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
//
|
||||
// controllerScriptingExamples.js
|
||||
// examples
|
||||
//
|
||||
// Created by Sam Gondelman on 6/2/15
|
||||
// Rewritten by Alessandro Signa on 11/05/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// This allows to change the input mapping to easly understand of how the new mapping works.
|
||||
// Two different ways are presented: the first one uses a JSON file to create the mapping, the second one declares the new routes explicitly one at a time.
|
||||
// You shuold prefer the first method if you have a lot of new routes, the second one if you want to express the action as a function.
|
||||
|
||||
|
||||
/*
|
||||
This function returns a JSON body. It's in charge to modify the standard controller and the mouse/keyboard mapping.
|
||||
|
||||
The Standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action)
|
||||
This example will overwrite the mapping of the left axis (Standard.LY, Standard.LX).
|
||||
It's possible to find all the standard inputs (and their mapping) into standard.json
|
||||
To try these changes you need a controller, not the keyboard.
|
||||
|
||||
The keyboard/mouse inputs are mapped directly to actions since the keyboard doesn't have its default mapping passing through the Standard controller.
|
||||
If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton).
|
||||
If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M).
|
||||
*/
|
||||
myFirstMapping = function() {
|
||||
return {
|
||||
"name": "controllerMapping_First",
|
||||
"channels": [
|
||||
|
||||
{ "from": "Standard.LY", "to": "Actions.Yaw" },
|
||||
{ "from": "Standard.LX", "to": "Actions.Yaw" },
|
||||
|
||||
{ "from": "Keyboard.W", "to": "Actions.YAW_LEFT" },
|
||||
{ "from": "Keyboard.M", "to": "Actions.YAW_RIGHT" },
|
||||
|
||||
{ "from": "Keyboard.LeftMouseButton", "to": "Actions.Up" }
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var firstWay = true;
|
||||
var mapping;
|
||||
var MAPPING_NAME;
|
||||
|
||||
|
||||
if(firstWay){
|
||||
var myFirstMappingJSON = myFirstMapping();
|
||||
print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON));
|
||||
mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON));
|
||||
mapping.enable();
|
||||
}else{
|
||||
MAPPING_NAME = "controllerMapping_Second";
|
||||
var mapping2 = Controller.newMapping(MAPPING_NAME);
|
||||
mapping2.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) {
|
||||
print("Keyboard.RightMouseClicked");
|
||||
});
|
||||
mapping2.from(Controller.Standard.LX).to(Controller.Actions.Yaw);
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
//-----------------some info prints that you would like to enable-----------------------
|
||||
Object.keys(Controller.Standard).forEach(function (input) {
|
||||
print("Controller.Standard." + input + ":" + Controller.Standard[input]);
|
||||
});
|
||||
|
||||
Object.keys(Controller.Hardware).forEach(function (deviceName) {
|
||||
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
|
||||
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(Controller.Actions).forEach(function (actionName) {
|
||||
print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]);
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
Controller.hardwareChanged.connect(function () {
|
||||
print("hardwareChanged ---------------------------------------------------");
|
||||
Object.keys(Controller.Hardware).forEach(function (deviceName) {
|
||||
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
|
||||
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
|
||||
});
|
||||
});
|
||||
print("-------------------------------------------------------------------");
|
||||
});
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if(firstWay){
|
||||
mapping.disable();
|
||||
} else {
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
}
|
||||
});
|
File diff suppressed because it is too large
Load diff
102
examples/controllers/handPosesDebug.js
Normal file
102
examples/controllers/handPosesDebug.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// handPosesDebug.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
function makeSphere(color) {
|
||||
var SPHERE_SIZE = 0.05;
|
||||
var sphere = Overlays.addOverlay("sphere", {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
size: SPHERE_SIZE,
|
||||
color: color,
|
||||
alpha: 1.0,
|
||||
solid: true,
|
||||
visible: true,
|
||||
});
|
||||
|
||||
return sphere;
|
||||
}
|
||||
|
||||
|
||||
var NUM_HANDS = 2;
|
||||
var NUM_SPHERES_PER_HAND = 2;
|
||||
var LEFT_HAND = 0;
|
||||
var RIGHT_HAND = 1;
|
||||
|
||||
var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ];
|
||||
|
||||
|
||||
function index(handNum, indexNum) {
|
||||
return handNum * NUM_HANDS + indexNum;
|
||||
}
|
||||
|
||||
var app = {};
|
||||
|
||||
|
||||
function setup() {
|
||||
app.spheres = new Array();
|
||||
|
||||
for (var h = 0; h < NUM_HANDS; h++) {
|
||||
for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) {
|
||||
var i = index(h, s);
|
||||
app.spheres[i] = makeSphere(COLORS[h]);
|
||||
print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateHand(handNum, deltaTime) {
|
||||
var pose;
|
||||
var handName = "right";
|
||||
if (handNum == LEFT_HAND) {
|
||||
pose = MyAvatar.getLeftHandPose();
|
||||
handName = "left";
|
||||
} else {
|
||||
pose = MyAvatar.getRightHandPose();
|
||||
handName = "right";
|
||||
}
|
||||
|
||||
if (pose.valid) {
|
||||
//print(handName + " hand moving" + JSON.stringify(pose));
|
||||
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
|
||||
position: pose.translation,
|
||||
visible: true,
|
||||
});
|
||||
var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation);
|
||||
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
|
||||
position: vpos,
|
||||
visible: true,
|
||||
});
|
||||
} else {
|
||||
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
|
||||
visible: false
|
||||
});
|
||||
|
||||
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
updateHand(LEFT_HAND, deltaTime);
|
||||
updateHand(RIGHT_HAND, deltaTime);
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
print("Removing spheres = " + JSON.stringify(app.spheres));
|
||||
for (var i = 0; i < app.spheres.length; i++) {
|
||||
Overlays.deleteOverlay(app.spheres[i]);
|
||||
}
|
||||
}
|
||||
|
||||
setup();
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst";
|
|||
|
||||
// Load sounds that will be played
|
||||
|
||||
var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav");
|
||||
|
||||
var chords = new Array();
|
||||
// Nylon string guitar
|
||||
chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw");
|
||||
|
@ -56,97 +58,128 @@ var NUM_GUITARS = 3;
|
|||
var guitarSelector = NUM_CHORDS;
|
||||
var whichChord = 1;
|
||||
|
||||
var leftHanded = true;
|
||||
var leftHanded = false;
|
||||
var strumHand, chordHand, strumTrigger, chordTrigger;
|
||||
if (leftHanded) {
|
||||
var strumHand = 0;
|
||||
var chordHand = 1;
|
||||
strumHand = Controller.Standard.LeftHand;
|
||||
chordHand = Controller.Standard.RightHand;
|
||||
strumTrigger = Controller.Standard.LT;
|
||||
chordTrigger = Controller.Standard.RT;
|
||||
changeGuitar = Controller.Standard.RB;
|
||||
chord1 = Controller.Standard.X;
|
||||
chord2 = Controller.Standard.Y;
|
||||
chord3 = Controller.Standard.A;
|
||||
chord4 = Controller.Standard.B;
|
||||
} else {
|
||||
var strumHand = 1;
|
||||
var chordHand = 0;
|
||||
strumHand = Controller.Standard.RightHand;
|
||||
chordHand = Controller.Standard.LeftHand;
|
||||
strumTrigger = Controller.Standard.RT;
|
||||
chordTrigger = Controller.Standard.LT;
|
||||
changeGuitar = Controller.Standard.LB;
|
||||
chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra??
|
||||
chord2 = Controller.Standard.DD;
|
||||
chord3 = Controller.Standard.DL;
|
||||
chord4 = Controller.Standard.DR;
|
||||
}
|
||||
|
||||
var lastPosition = { x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0 };
|
||||
|
||||
var soundPlaying = false;
|
||||
var audioInjector = null;
|
||||
var selectorPressed = false;
|
||||
var position;
|
||||
|
||||
MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0);
|
||||
MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0);
|
||||
|
||||
function checkHands(deltaTime) {
|
||||
for (var palm = 0; palm < 2; palm++) {
|
||||
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
|
||||
var volume = length(palmVelocity) / 5.0;
|
||||
var position = Controller.getSpatialControlPosition(palm * 2 + 1);
|
||||
var myPelvis = MyAvatar.position;
|
||||
var trigger = Controller.getTriggerValue(strumHand);
|
||||
var chord = Controller.getTriggerValue(chordHand);
|
||||
var strumVelocity = Controller.getPoseValue(strumHand).velocity;
|
||||
var volume = length(strumVelocity) / 5.0;
|
||||
|
||||
if (volume > 1.0) volume = 1.0;
|
||||
if ((chord > 0.1) && soundPlaying.isPlaying) {
|
||||
// If chord finger trigger pulled, stop current chord
|
||||
print("stopped sound");
|
||||
soundPlaying.stop();
|
||||
if (volume == 0.0) {
|
||||
volume = 1.0;
|
||||
}
|
||||
|
||||
var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition;
|
||||
var myPelvis = MyAvatar.position;
|
||||
var strumming = Controller.getValue(strumTrigger);
|
||||
var chord = Controller.getValue(chordTrigger);
|
||||
|
||||
if (volume > 1.0) volume = 1.0;
|
||||
if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) {
|
||||
// If chord finger trigger pulled, stop current chord
|
||||
print("stopping chord because cord trigger pulled");
|
||||
audioInjector.stop();
|
||||
}
|
||||
|
||||
// Change guitars if button FWD (5) pressed
|
||||
if (Controller.getValue(changeGuitar)) {
|
||||
if (!selectorPressed) {
|
||||
print("changeGuitar:" + changeGuitar);
|
||||
guitarSelector += NUM_CHORDS;
|
||||
if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
|
||||
guitarSelector = 0;
|
||||
}
|
||||
print("new guitarBase: " + guitarSelector);
|
||||
stopAudio(true);
|
||||
selectorPressed = true;
|
||||
}
|
||||
} else {
|
||||
selectorPressed = false;
|
||||
}
|
||||
|
||||
var BUTTON_COUNT = 6;
|
||||
if (Controller.getValue(chord1)) {
|
||||
whichChord = 1;
|
||||
stopAudio(true);
|
||||
} else if (Controller.getValue(chord2)) {
|
||||
whichChord = 2;
|
||||
stopAudio(true);
|
||||
} else if (Controller.getValue(chord3)) {
|
||||
whichChord = 3;
|
||||
stopAudio(true);
|
||||
} else if (Controller.getValue(chord4)) {
|
||||
whichChord = 4;
|
||||
stopAudio(true);
|
||||
}
|
||||
|
||||
// Change guitars if button FWD (5) pressed
|
||||
if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) {
|
||||
if (!selectorPressed) {
|
||||
guitarSelector += NUM_CHORDS;
|
||||
if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
|
||||
guitarSelector = 0;
|
||||
}
|
||||
selectorPressed = true;
|
||||
}
|
||||
} else {
|
||||
selectorPressed = false;
|
||||
}
|
||||
var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
|
||||
var strummingHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
|
||||
|
||||
if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) {
|
||||
whichChord = 1;
|
||||
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) {
|
||||
whichChord = 2;
|
||||
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) {
|
||||
whichChord = 3;
|
||||
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) {
|
||||
whichChord = 4;
|
||||
}
|
||||
var strumNowAbove = strumHandPosition.y > strummingHeight;
|
||||
var strumNowBelow = strumHandPosition.y <= strummingHeight;
|
||||
var strumWasAbove = lastPosition.y > strummingHeight;
|
||||
var strumWasBelow = lastPosition.y <= strummingHeight;
|
||||
var strumUp = strumNowAbove && strumWasBelow;
|
||||
var strumDown = strumNowBelow && strumWasAbove;
|
||||
|
||||
if (palm == strumHand) {
|
||||
if ((strumUp || strumDown) && (strumming > 0.1)) {
|
||||
// If hand passes downward or upward through 'strings', and finger trigger pulled, play
|
||||
playChord(strumHandPosition, volume);
|
||||
}
|
||||
lastPosition = strumHandPosition;
|
||||
}
|
||||
|
||||
var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
|
||||
var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
|
||||
//printVector(position);
|
||||
if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) ||
|
||||
((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){
|
||||
// If hand passes downward or upward through 'strings', and finger trigger pulled, play
|
||||
playChord(position, volume);
|
||||
}
|
||||
lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
|
||||
}
|
||||
function stopAudio(killInjector) {
|
||||
if (audioInjector && audioInjector.isPlaying) {
|
||||
print("stopped sound");
|
||||
audioInjector.stop();
|
||||
}
|
||||
if (killInjector) {
|
||||
audioInjector = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function playChord(position, volume) {
|
||||
if (soundPlaying.isPlaying) {
|
||||
print("stopped sound");
|
||||
soundPlaying.stop();
|
||||
stopAudio();
|
||||
print("Played sound: " + whichChord + " at volume " + volume);
|
||||
if (!audioInjector) {
|
||||
var index = guitarSelector + whichChord;
|
||||
var chord = chords[guitarSelector + whichChord];
|
||||
audioInjector = Audio.playSound(chord, { position: position, volume: volume });
|
||||
} else {
|
||||
audioInjector.restart();
|
||||
}
|
||||
|
||||
print("Played sound: " + whichChord + " at volume " + options.volume);
|
||||
if (!soundPlaying) {
|
||||
soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], {
|
||||
position: position,
|
||||
volume: volume
|
||||
});
|
||||
} else {
|
||||
soundPlaying.restart();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
|
@ -154,15 +187,19 @@ function keyPressEvent(event) {
|
|||
keyVolume = 0.4;
|
||||
if (event.text == "1") {
|
||||
whichChord = 1;
|
||||
stopAudio(true);
|
||||
playChord(MyAvatar.position, keyVolume);
|
||||
} else if (event.text == "2") {
|
||||
whichChord = 2;
|
||||
stopAudio(true);
|
||||
playChord(MyAvatar.position, keyVolume);
|
||||
} else if (event.text == "3") {
|
||||
whichChord = 3;
|
||||
stopAudio(true);
|
||||
playChord(MyAvatar.position, keyVolume);
|
||||
} else if (event.text == "4") {
|
||||
whichChord = 4;
|
||||
stopAudio(true);
|
||||
playChord(MyAvatar.position, keyVolume);
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +207,7 @@ function keyPressEvent(event) {
|
|||
function scriptEnding() {
|
||||
MyAvatar.detachOne(guitarModel);
|
||||
}
|
||||
|
||||
// Connect a call back that happens every frame
|
||||
Script.update.connect(checkHands);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0;
|
|||
|
||||
function checkSticks(deltaTime) {
|
||||
for (var palm = 0; palm < 2; palm++) {
|
||||
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
|
||||
var handPose = (palm == 0) ? MyAvatar.leftHandPose : MyAvatar.rightHandPose;
|
||||
var palmVelocity = handPose.velocity;
|
||||
var speed = length(palmVelocity);
|
||||
|
||||
const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently
|
||||
|
@ -64,7 +65,7 @@ function checkSticks(deltaTime) {
|
|||
if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {
|
||||
state[palm] = 0;
|
||||
|
||||
var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) };
|
||||
var options = { position: handPose.translation };
|
||||
|
||||
if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }
|
||||
options.volume = strokeSpeed[palm];
|
||||
|
|
|
@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) {
|
|||
this.trigger = trigger;
|
||||
this.holdingFrisbee = false;
|
||||
this.entity = false;
|
||||
this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); }
|
||||
this.palmPosition = function () {
|
||||
return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
|
||||
};
|
||||
|
||||
this.grabButtonPressed = function() {
|
||||
return (
|
||||
Controller.isButtonPressed(this.forwardButton) ||
|
||||
Controller.isButtonPressed(this.button3) ||
|
||||
Controller.getTriggerValue(this.trigger) > 0.5
|
||||
Controller.getValue(this.forwardButton) ||
|
||||
Controller.getValue(this.button3) ||
|
||||
Controller.getValue(this.trigger) > 0.5
|
||||
)
|
||||
};
|
||||
this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); };
|
||||
this.holdPosition = function () {
|
||||
return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
|
||||
};
|
||||
|
||||
this.holdRotation = function() {
|
||||
var q = Controller.getSpatialControlRawRotation(this.palm);
|
||||
var q = (this.palm == LEFT_PALM) ? Controller.getPoseValue(Controller.Standard.leftHand).rotation
|
||||
: Controller.getPoseValue(Controller.Standard.rightHand).rotation;
|
||||
q = Quat.multiply(MyAvatar.orientation, q);
|
||||
return {x: q.x, y: q.y, z: q.z, w: q.w};
|
||||
};
|
||||
this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); };
|
||||
this.tipVelocity = function () {
|
||||
return this.tip == LEFT_TIP ? MyAvatar.leftHandTipPose.velocity : MyAvatar.rightHandTipPose.velocity;
|
||||
};
|
||||
}
|
||||
|
||||
function MouseControl(button) {
|
||||
this.button = button;
|
||||
}
|
||||
|
||||
var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0);
|
||||
var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1);
|
||||
var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT);
|
||||
var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT);
|
||||
|
||||
var leftMouseControl = new MouseControl("LEFT");
|
||||
var middleMouseControl = new MouseControl("MIDDLE");
|
||||
|
@ -302,12 +311,7 @@ function initToolBar() {
|
|||
}
|
||||
|
||||
function hydraCheck() {
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
|
||||
return true;//hydrasConnected;
|
||||
return Controller.Hardware.Hydra !== undefined;
|
||||
}
|
||||
|
||||
function checkController(deltaTime) {
|
||||
|
|
|
@ -15,24 +15,64 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// FIXME kickback functionality was removed because the joint setting interface in
|
||||
// MyAvatar has apparently changed, breaking it.
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
Script.include("../../libraries/constants.js");
|
||||
Script.include("../../libraries/toolBars.js");
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var RED = { red: 255, green: 0, blue: 0 };
|
||||
var LASER_WIDTH = 2;
|
||||
var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ];
|
||||
var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ];
|
||||
var MIN_THROWER_DELAY = 1000;
|
||||
var MAX_THROWER_DELAY = 1000;
|
||||
var RELOAD_INTERVAL = 5;
|
||||
var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4";
|
||||
var BULLET_VELOCITY = 10.0;
|
||||
var GUN_OFFSETS = [ {
|
||||
x: -0.04,
|
||||
y: 0.26,
|
||||
z: 0.04
|
||||
}, {
|
||||
x: 0.04,
|
||||
y: 0.26,
|
||||
z: 0.04
|
||||
} ];
|
||||
|
||||
var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ];
|
||||
|
||||
var BARREL_OFFSETS = [ {
|
||||
x: -0.12,
|
||||
y: 0.12,
|
||||
z: 0.04
|
||||
}, {
|
||||
x: 0.12,
|
||||
y: 0.12,
|
||||
z: 0.04
|
||||
} ];
|
||||
|
||||
var mapping = Controller.newMapping();
|
||||
var validPoses = [ false, false ];
|
||||
var barrelVectors = [ 0, 0 ];
|
||||
var barrelTips = [ 0, 0 ];
|
||||
var pointer = [];
|
||||
|
||||
pointer.push(Overlays.addOverlay("line3d", {
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 0, z: 0 },
|
||||
color: RED,
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: LASER_WIDTH
|
||||
}));
|
||||
|
||||
pointer.push(Overlays.addOverlay("line3d", {
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 0, z: 0 },
|
||||
color: RED,
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: LASER_WIDTH
|
||||
|
@ -42,135 +82,116 @@ function getRandomFloat(min, max) {
|
|||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
var lastX = 0;
|
||||
var lastY = 0;
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
var isMouseDown = false;
|
||||
|
||||
var MIN_THROWER_DELAY = 1000;
|
||||
var MAX_THROWER_DELAY = 1000;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
var RELOAD_INTERVAL = 5;
|
||||
|
||||
var KICKBACK_ANGLE = 15;
|
||||
var elbowKickAngle = 0.0;
|
||||
var rotationBeforeKickback;
|
||||
|
||||
var showScore = false;
|
||||
|
||||
|
||||
// Load some sound to use for loading and firing
|
||||
// Load some sound to use for loading and firing
|
||||
var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");
|
||||
var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");
|
||||
var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw");
|
||||
var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw");
|
||||
var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw");
|
||||
|
||||
var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4";
|
||||
|
||||
var audioOptions = {
|
||||
volume: 0.9
|
||||
volume: 0.9
|
||||
}
|
||||
|
||||
var shotsFired = 0;
|
||||
var shotTime = new Date();
|
||||
|
||||
var activeControllers = 0;
|
||||
|
||||
// initialize our controller triggers
|
||||
var triggerPulled = new Array();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
for (t = 0; t < numberOfTriggers; t++) {
|
||||
triggerPulled[t] = false;
|
||||
}
|
||||
|
||||
var isLaunchButtonPressed = false;
|
||||
var score = 0;
|
||||
var shotTime = new Date();
|
||||
var isLaunchButtonPressed = false;
|
||||
var score = 0;
|
||||
|
||||
var bulletID = false;
|
||||
var targetID = false;
|
||||
|
||||
// Create overlay buttons and reticle
|
||||
|
||||
// Create overlay buttons and reticle
|
||||
var BUTTON_SIZE = 32;
|
||||
var PADDING = 3;
|
||||
var NUM_BUTTONS = 3;
|
||||
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
|
||||
Script.include(["../../libraries/toolBars.js"]);
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) {
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) {
|
||||
return {
|
||||
x: startX,
|
||||
y: (screenSize.y - (BUTTON_SIZE + PADDING)),
|
||||
};
|
||||
});
|
||||
var reticle = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - (BUTTON_SIZE / 2),
|
||||
y: screenSize.y / 2 - (BUTTON_SIZE / 2),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg",
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var offButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
|
||||
alpha: 1
|
||||
});
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
startX += BUTTON_SIZE + PADDING;
|
||||
var platformButton = toolBar.addOverlay("image", {
|
||||
x: startX,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
|
||||
alpha: 1
|
||||
});
|
||||
x: startX,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
startX += BUTTON_SIZE + PADDING;
|
||||
var gridButton = toolBar.addOverlay("image", {
|
||||
x: startX,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
|
||||
alpha: 1
|
||||
});
|
||||
x: startX,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
if (showScore) {
|
||||
var text = Overlays.addOverlay("text", {
|
||||
x: screenSize.x / 2 - 100,
|
||||
y: screenSize.y / 2 - 50,
|
||||
width: 150,
|
||||
height: 50,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 0, blue: 0},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: "Score: " + score
|
||||
});
|
||||
x: screenSize.x / 2 - 100,
|
||||
y: screenSize.y / 2 - 50,
|
||||
width: 150,
|
||||
height: 50,
|
||||
color: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
textColor: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: "Score: " + score
|
||||
});
|
||||
}
|
||||
|
||||
var BULLET_VELOCITY = 10.0;
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
if (entity2 === targetID) {
|
||||
score++;
|
||||
if (showScore) {
|
||||
Overlays.editOverlay(text, { text: "Score: " + score } );
|
||||
Overlays.editOverlay(text, {
|
||||
text: "Score: " + score
|
||||
});
|
||||
}
|
||||
|
||||
// We will delete the bullet and target in 1/2 sec, but for now we can see them bounce!
|
||||
// We will delete the bullet and target in 1/2 sec, but for now we can
|
||||
// see them bounce!
|
||||
Script.setTimeout(deleteBulletAndTarget, 500);
|
||||
|
||||
// Turn the target and the bullet white
|
||||
Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
|
||||
Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
|
||||
Entities.editEntity(entity1, {
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
});
|
||||
Entities.editEntity(entity2, {
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
});
|
||||
|
||||
// play the sound near the camera so the shooter can hear it
|
||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
|
@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) {
|
|||
|
||||
var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;
|
||||
var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
|
||||
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
|
||||
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
|
||||
|
||||
bulletID = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
position: position,
|
||||
dimensions: {
|
||||
x: bSize,
|
||||
y: bSize,
|
||||
z: bSize
|
||||
},
|
||||
color: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
velocity: bVelocity,
|
||||
lifetime: BULLET_LIFETIME,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: bGravity,
|
||||
z: 0
|
||||
},
|
||||
damping: 0.01,
|
||||
density: 8000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
||||
bulletID = Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: position,
|
||||
dimensions: { x: bSize, y: bSize, z: bSize },
|
||||
color: { red: 0, green: 0, blue: 0 },
|
||||
velocity: bVelocity,
|
||||
lifetime: BULLET_LIFETIME,
|
||||
gravity: { x: 0, y: bGravity, z: 0 },
|
||||
damping: 0.01,
|
||||
density: 8000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
|
||||
|
||||
// Play firing sounds
|
||||
audioOptions.position = position;
|
||||
// Play firing sounds
|
||||
audioOptions.position = position;
|
||||
Audio.playSound(fireSound, audioOptions);
|
||||
shotsFired++;
|
||||
if ((shotsFired % RELOAD_INTERVAL) == 0) {
|
||||
Audio.playSound(loadSound, audioOptions);
|
||||
}
|
||||
|
||||
// Kickback the arm
|
||||
if (elbowKickAngle > 0.0) {
|
||||
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
|
||||
}
|
||||
rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm");
|
||||
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
|
||||
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE));
|
||||
MyAvatar.setJointData("LeftForeArm", armRotation);
|
||||
elbowKickAngle = KICKBACK_ANGLE;
|
||||
}
|
||||
|
||||
function shootTarget() {
|
||||
var TARGET_SIZE = 0.50;
|
||||
var TARGET_GRAVITY = 0.0;
|
||||
|
@ -232,95 +257,152 @@ function shootTarget() {
|
|||
var DISTANCE_TO_LAUNCH_FROM = 5.0;
|
||||
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
|
||||
var camera = Camera.getPosition();
|
||||
|
||||
var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 });
|
||||
|
||||
var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
});
|
||||
targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
|
||||
var forwardVector = Quat.getFront(targetDirection);
|
||||
|
||||
|
||||
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM));
|
||||
|
||||
var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
|
||||
velocity.y += TARGET_UP_VELOCITY;
|
||||
|
||||
targetID = Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: newPosition,
|
||||
dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 },
|
||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
||||
velocity: velocity,
|
||||
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
|
||||
lifetime: TARGET_LIFETIME,
|
||||
rotation: Camera.getOrientation(),
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true });
|
||||
targetID = Entities.addEntity({
|
||||
type: "Box",
|
||||
position: newPosition,
|
||||
dimensions: {
|
||||
x: TARGET_SIZE * (0.5 + Math.random()),
|
||||
y: TARGET_SIZE * (0.5 + Math.random()),
|
||||
z: TARGET_SIZE * (0.5 + Math.random()) / 4.0
|
||||
},
|
||||
color: {
|
||||
red: Math.random() * 255,
|
||||
green: Math.random() * 255,
|
||||
blue: Math.random() * 255
|
||||
},
|
||||
velocity: velocity,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: TARGET_GRAVITY,
|
||||
z: 0
|
||||
},
|
||||
lifetime: TARGET_LIFETIME,
|
||||
rotation: Camera.getOrientation(),
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
||||
// Record start time
|
||||
// Record start time
|
||||
shotTime = new Date();
|
||||
|
||||
// Play target shoot sound
|
||||
audioOptions.position = newPosition;
|
||||
audioOptions.position = newPosition;
|
||||
Audio.playSound(targetLaunchSound, audioOptions);
|
||||
}
|
||||
|
||||
function makeGrid(type, scale, size) {
|
||||
var separation = scale * 2;
|
||||
var separation = scale * 2;
|
||||
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
|
||||
var x, y, z;
|
||||
var GRID_LIFE = 60.0;
|
||||
var dimensions;
|
||||
var GRID_LIFE = 60.0;
|
||||
var dimensions;
|
||||
|
||||
for (x = 0; x < size; x++) {
|
||||
for (y = 0; y < size; y++) {
|
||||
for (z = 0; z < size; z++) {
|
||||
|
||||
dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 };
|
||||
|
||||
Entities.addEntity(
|
||||
{ type: type,
|
||||
position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },
|
||||
dimensions: dimensions,
|
||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
lifetime: GRID_LIFE,
|
||||
rotation: Camera.getOrientation(),
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true });
|
||||
dimensions = {
|
||||
x: separation / 2.0 * (0.5 + Math.random()),
|
||||
y: separation / 2.0 * (0.5 + Math.random()),
|
||||
z: separation / 2.0 * (0.5 + Math.random()) / 4.0
|
||||
};
|
||||
|
||||
Entities.addEntity({
|
||||
type: type,
|
||||
position: {
|
||||
x: pos.x + x * separation,
|
||||
y: pos.y + y * separation,
|
||||
z: pos.z + z * separation
|
||||
},
|
||||
dimensions: dimensions,
|
||||
color: {
|
||||
red: Math.random() * 255,
|
||||
green: Math.random() * 255,
|
||||
blue: Math.random() * 255
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
lifetime: GRID_LIFE,
|
||||
rotation: Camera.getOrientation(),
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makePlatform(gravity, scale, size) {
|
||||
var separation = scale * 2;
|
||||
var separation = scale * 2;
|
||||
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
|
||||
pos.y -= separation * size;
|
||||
var x, y, z;
|
||||
var TARGET_LIFE = 60.0;
|
||||
var TARGET_LIFE = 60.0;
|
||||
var INITIAL_GAP = 0.5;
|
||||
var dimensions;
|
||||
var dimensions;
|
||||
|
||||
for (x = 0; x < size; x++) {
|
||||
for (y = 0; y < size; y++) {
|
||||
for (z = 0; z < size; z++) {
|
||||
|
||||
dimensions = { x: separation/2.0, y: separation, z: separation/2.0 };
|
||||
dimensions = {
|
||||
x: separation / 2.0,
|
||||
y: separation,
|
||||
z: separation / 2.0
|
||||
};
|
||||
|
||||
Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - (separation * size / 2.0) + x * separation,
|
||||
y: pos.y + y * (separation + INITIAL_GAP),
|
||||
z: pos.z - (separation * size / 2.0) + z * separation },
|
||||
dimensions: dimensions,
|
||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
||||
velocity: { x: 0, y: 0.05, z: 0 },
|
||||
gravity: { x: 0, y: gravity, z: 0 },
|
||||
lifetime: TARGET_LIFE,
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true });
|
||||
Entities.addEntity({
|
||||
type: "Box",
|
||||
position: {
|
||||
x: pos.x - (separation * size / 2.0) + x * separation,
|
||||
y: pos.y + y * (separation + INITIAL_GAP),
|
||||
z: pos.z - (separation * size / 2.0) + z * separation
|
||||
},
|
||||
dimensions: dimensions,
|
||||
color: {
|
||||
red: Math.random() * 255,
|
||||
green: Math.random() * 255,
|
||||
blue: Math.random() * 255
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0.05,
|
||||
z: 0
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: gravity,
|
||||
z: 0
|
||||
},
|
||||
lifetime: TARGET_LIFE,
|
||||
damping: 0.1,
|
||||
density: 100.0,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) {
|
|||
// Make a floor for this stuff to fall onto
|
||||
Entities.addEntity({
|
||||
type: "Box",
|
||||
position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z },
|
||||
dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },
|
||||
color: { red: 100, green: 100, blue: 100 },
|
||||
position: {
|
||||
x: pos.x,
|
||||
y: pos.y - separation / 2.0,
|
||||
z: pos.z
|
||||
},
|
||||
dimensions: {
|
||||
x: 2.0 * separation * size,
|
||||
y: separation / 2.0,
|
||||
z: 2.0 * separation * size
|
||||
},
|
||||
color: {
|
||||
red: 100,
|
||||
green: 100,
|
||||
blue: 100
|
||||
},
|
||||
lifetime: TARGET_LIFE
|
||||
});
|
||||
|
||||
|
@ -340,153 +434,79 @@ function keyPressEvent(event) {
|
|||
// if our tools are off, then don't do anything
|
||||
if (event.text == "t") {
|
||||
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
|
||||
Script.setTimeout(shootTarget, time);
|
||||
Script.setTimeout(shootTarget, time);
|
||||
} else if ((event.text == ".") || (event.text == "SPACE")) {
|
||||
shootFromMouse(false);
|
||||
} else if (event.text == ",") {
|
||||
shootFromMouse(true);
|
||||
} else if (event.text == "r") {
|
||||
playLoadSound();
|
||||
} else if (event.text == "s") {
|
||||
// Hit this key to dump a posture from hydra to log
|
||||
Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm"));
|
||||
Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm"));
|
||||
Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand"));
|
||||
}
|
||||
}
|
||||
|
||||
function playLoadSound() {
|
||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
audioOptions.position = MyAvatar.leftHandPose.translation;
|
||||
Audio.playSound(loadSound, audioOptions);
|
||||
// Raise arm to firing posture
|
||||
takeFiringPose();
|
||||
}
|
||||
|
||||
function clearPose() {
|
||||
MyAvatar.clearJointData("LeftForeArm");
|
||||
MyAvatar.clearJointData("LeftArm");
|
||||
MyAvatar.clearJointData("LeftHand");
|
||||
}
|
||||
|
||||
function deleteBulletAndTarget() {
|
||||
Entities.deleteEntity(bulletID);
|
||||
Entities.deleteEntity(targetID);
|
||||
bulletID = false;
|
||||
targetID = false;
|
||||
bulletID = false;
|
||||
targetID = false;
|
||||
}
|
||||
|
||||
function takeFiringPose() {
|
||||
clearPose();
|
||||
if (Controller.getNumberOfSpatialControls() == 0) {
|
||||
MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843});
|
||||
MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219});
|
||||
MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333});
|
||||
}
|
||||
}
|
||||
|
||||
MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40);
|
||||
MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40);
|
||||
|
||||
// Give a bit of time to load before playing sound
|
||||
Script.setTimeout(playLoadSound, 2000);
|
||||
|
||||
function update(deltaTime) {
|
||||
if (activeControllers == 0) {
|
||||
if (Controller.getNumberOfSpatialControls() > 0) {
|
||||
activeControllers = Controller.getNumberOfSpatialControls();
|
||||
clearPose();
|
||||
}
|
||||
}
|
||||
// FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
|
||||
var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ];
|
||||
|
||||
var KICKBACK_DECAY_RATE = 0.125;
|
||||
if (elbowKickAngle > 0.0) {
|
||||
if (elbowKickAngle > 0.5) {
|
||||
var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE;
|
||||
elbowKickAngle -= newAngle;
|
||||
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
|
||||
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle));
|
||||
MyAvatar.setJointData("LeftForeArm", armRotation);
|
||||
} else {
|
||||
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
|
||||
if (Controller.getNumberOfSpatialControls() > 0) {
|
||||
clearPose();
|
||||
}
|
||||
elbowKickAngle = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check for trigger press
|
||||
|
||||
var numberOfTriggers = 2;
|
||||
var controllersPerTrigger = 2;
|
||||
|
||||
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
|
||||
for (var t = 0; t < 2; t++) {
|
||||
var shootABullet = false;
|
||||
var triggerValue = Controller.getTriggerValue(t);
|
||||
if (triggerPulled[t]) {
|
||||
// must release to at least 0.1
|
||||
if (triggerValue < 0.1) {
|
||||
triggerPulled[t] = false; // unpulled
|
||||
}
|
||||
} else {
|
||||
// must pull to at least
|
||||
if (triggerValue > 0.5) {
|
||||
triggerPulled[t] = true; // pulled
|
||||
shootABullet = true;
|
||||
}
|
||||
}
|
||||
var palmController = t * controllersPerTrigger;
|
||||
var palmPosition = Controller.getSpatialControlPosition(palmController);
|
||||
var fingerTipController = palmController + 1;
|
||||
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
|
||||
var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition);
|
||||
|
||||
// Update Lasers
|
||||
Overlays.editOverlay(pointer[t], {
|
||||
start: palmPosition,
|
||||
end: laserTip,
|
||||
alpha: 1
|
||||
for (var side = 0; side < 2; side++) {
|
||||
// First check if the controller is valid
|
||||
var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]);
|
||||
validPoses[side] = controllerPose.valid;
|
||||
if (!controllerPose.valid) {
|
||||
Overlays.editOverlay(pointer[side], {
|
||||
visible: false
|
||||
});
|
||||
|
||||
if (shootABullet) {
|
||||
|
||||
var palmToFingerTipVector =
|
||||
{ x: (fingerTipPosition.x - palmPosition.x),
|
||||
y: (fingerTipPosition.y - palmPosition.y),
|
||||
z: (fingerTipPosition.z - palmPosition.z) };
|
||||
|
||||
// just off the front of the finger tip
|
||||
var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
|
||||
y: fingerTipPosition.y + palmToFingerTipVector.y/2,
|
||||
z: fingerTipPosition.z + palmToFingerTipVector.z/2};
|
||||
|
||||
var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector));
|
||||
|
||||
shootBullet(position, velocity, false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to adjust the laser
|
||||
var tipPose = tipPoses[side];
|
||||
var handRotation = tipPoses[side].rotation;
|
||||
var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
|
||||
barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
|
||||
barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
});
|
||||
|
||||
var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]);
|
||||
// Update Lasers
|
||||
Overlays.editOverlay(pointer[side], {
|
||||
start: barrelTips[side],
|
||||
end: laserTip,
|
||||
alpha: 1,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function shootFromMouse(grenade) {
|
||||
var DISTANCE_FROM_CAMERA = 1.0;
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(Camera.getOrientation());
|
||||
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
|
||||
var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
|
||||
shootBullet(newPosition, velocity, grenade);
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
// position
|
||||
isMouseDown = false;
|
||||
function triggerChanged(side, value) {
|
||||
var pressed = (value != 0);
|
||||
if (pressed) {
|
||||
var position = barrelTips[side];
|
||||
var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side]));
|
||||
shootBullet(position, velocity, false);
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var clickedText = false;
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay == offButton) {
|
||||
Script.stop();
|
||||
} else if (clickedOverlay == platformButton) {
|
||||
|
@ -494,25 +514,37 @@ function mousePressEvent(event) {
|
|||
makePlatform(-9.8, 1.0, platformSize);
|
||||
} else if (clickedOverlay == gridButton) {
|
||||
makeGrid("Box", 1.0, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(reticle);
|
||||
mapping.disable();
|
||||
toolBar.cleanup();
|
||||
Overlays.deleteOverlay(pointer[0]);
|
||||
Overlays.deleteOverlay(pointer[1]);
|
||||
for (var i = 0; i < pointer.length; ++i) {
|
||||
Overlays.deleteOverlay(pointer[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(text);
|
||||
MyAvatar.detachOne(gunModel);
|
||||
MyAvatar.detachOne(gunModel);
|
||||
MyAvatar.detachOne(GUN_MODEL);
|
||||
MyAvatar.detachOne(GUN_MODEL);
|
||||
clearPose();
|
||||
}
|
||||
|
||||
MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40);
|
||||
MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40);
|
||||
|
||||
// Give a bit of time to load before playing sound
|
||||
Script.setTimeout(playLoadSound, 2000);
|
||||
|
||||
mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) {
|
||||
triggerChanged(0, value);
|
||||
});
|
||||
|
||||
mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) {
|
||||
triggerChanged(1, value);
|
||||
});
|
||||
mapping.enable();
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.update.connect(update);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,303 +0,0 @@
|
|||
//
|
||||
// hydraMove.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on February 10, 2014
|
||||
// Updated by Philip Rosedale on September 8, 2014
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
|
||||
// avatar flying through the hydra/controller joysticks
|
||||
//
|
||||
// The joysticks (on hydra) will drive the avatar much like a playstation controller.
|
||||
//
|
||||
// Pressing the '4' or the 'FWD' button and moving/banking the hand will allow you to move and fly.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var damping = 0.9;
|
||||
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
|
||||
var joysticksCaptured = false;
|
||||
var THRUST_CONTROLLER = 0;
|
||||
var VIEW_CONTROLLER = 1;
|
||||
var INITIAL_THRUST_MULTIPLIER = 1.0;
|
||||
var THRUST_INCREASE_RATE = 1.05;
|
||||
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||
var thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
|
||||
var grabDelta = { x: 0, y: 0, z: 0};
|
||||
var grabStartPosition = { x: 0, y: 0, z: 0};
|
||||
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabbingWithRightHand = false;
|
||||
var wasGrabbingWithRightHand = false;
|
||||
var grabbingWithLeftHand = false;
|
||||
var wasGrabbingWithLeftHand = false;
|
||||
|
||||
var EPSILON = 0.000001;
|
||||
var velocity = { x: 0, y: 0, z: 0};
|
||||
var THRUST_MAG_UP = 100.0;
|
||||
var THRUST_MAG_DOWN = 100.0;
|
||||
var THRUST_MAG_FWD = 150.0;
|
||||
var THRUST_MAG_BACK = 100.0;
|
||||
var THRUST_MAG_LATERAL = 150.0;
|
||||
var THRUST_JUMP = 120.0;
|
||||
|
||||
var YAW_MAG = 100.0;
|
||||
var PITCH_MAG = 100.0;
|
||||
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
var JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
|
||||
|
||||
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_BUTTON_4 = 4;
|
||||
var LEFT_BUTTON_FWD = 5;
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_BUTTON_4 = 10;
|
||||
var RIGHT_BUTTON_FWD = 11;
|
||||
|
||||
|
||||
|
||||
function printVector(text, v, decimals) {
|
||||
print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals));
|
||||
}
|
||||
|
||||
var debug = false;
|
||||
var RED_COLOR = { red: 255, green: 0, blue: 0 };
|
||||
var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
|
||||
var defaultPosition = { x: 0, y: 0, z: 0};
|
||||
var RADIUS = 0.05;
|
||||
var greenSphere = -1;
|
||||
var redSphere = -1;
|
||||
|
||||
function createDebugOverlay() {
|
||||
|
||||
if (greenSphere == -1) {
|
||||
greenSphere = Overlays.addOverlay("sphere", {
|
||||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: GRAY_COLOR,
|
||||
alpha: 0.75,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
});
|
||||
redSphere = Overlays.addOverlay("sphere", {
|
||||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: RED_COLOR,
|
||||
alpha: 0.5,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function destroyDebugOverlay() {
|
||||
if (greenSphere != -1) {
|
||||
Overlays.deleteOverlay(greenSphere);
|
||||
Overlays.deleteOverlay(redSphere);
|
||||
greenSphere = -1;
|
||||
redSphere = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function displayDebug() {
|
||||
if (!(grabbingWithRightHand || grabbingWithLeftHand)) {
|
||||
if (greenSphere != -1) {
|
||||
destroyDebugOverlay();
|
||||
}
|
||||
} else {
|
||||
// update debug indicator
|
||||
if (greenSphere == -1) {
|
||||
createDebugOverlay();
|
||||
}
|
||||
|
||||
var displayOffset = { x:0, y:0.5, z:-0.5 };
|
||||
|
||||
Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } );
|
||||
Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } );
|
||||
}
|
||||
}
|
||||
|
||||
function getJoystickPosition(palm) {
|
||||
// returns CONTROLLER_ID position in avatar local frame
|
||||
var invRotation = Quat.inverse(MyAvatar.orientation);
|
||||
var palmWorld = Controller.getSpatialControlPosition(palm);
|
||||
var palmRelative = Vec3.subtract(palmWorld, MyAvatar.position);
|
||||
var palmLocal = Vec3.multiplyQbyV(invRotation, palmRelative);
|
||||
return palmLocal;
|
||||
}
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab position changes
|
||||
function getAndResetGrabDelta() {
|
||||
var HAND_GRAB_SCALE_DISTANCE = 2.0;
|
||||
var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
|
||||
grabDelta = { x: 0, y: 0, z: 0};
|
||||
var avatarRotation = MyAvatar.orientation;
|
||||
var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
|
||||
return result;
|
||||
}
|
||||
|
||||
function getGrabRotation() {
|
||||
var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
|
||||
return quatDiff;
|
||||
}
|
||||
|
||||
// When move button is pressed, process results
|
||||
function handleGrabBehavior(deltaTime) {
|
||||
// check for and handle grab behaviors
|
||||
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
|
||||
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
|
||||
stoppedGrabbingWithLeftHand = false;
|
||||
stoppedGrabbingWithRightHand = false;
|
||||
|
||||
if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
grabStartPosition = getJoystickPosition(RIGHT_PALM);
|
||||
if (debug) printVector("start position", grabStartPosition, 3);
|
||||
}
|
||||
if (grabbingWithRightHand) {
|
||||
grabDelta = Vec3.subtract(getJoystickPosition(RIGHT_PALM), grabStartPosition);
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
}
|
||||
if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
|
||||
stoppedGrabbingWithRightHand = true;
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
grabStartPosition = getJoystickPosition(LEFT_PALM);
|
||||
if (debug) printVector("start position", grabStartPosition, 3);
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand) {
|
||||
grabDelta = Vec3.subtract(getJoystickPosition(LEFT_PALM), grabStartPosition);
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
}
|
||||
if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
|
||||
stoppedGrabbingWithLeftHand = true;
|
||||
}
|
||||
|
||||
grabbing = grabbingWithRightHand || grabbingWithLeftHand;
|
||||
stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
|
||||
|
||||
if (grabbing) {
|
||||
|
||||
var headOrientation = MyAvatar.headOrientation;
|
||||
var front = Quat.getFront(headOrientation);
|
||||
var right = Quat.getRight(headOrientation);
|
||||
var up = Quat.getUp(headOrientation);
|
||||
|
||||
if (debug) {
|
||||
printVector("grabDelta: ", grabDelta, 3);
|
||||
}
|
||||
|
||||
var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta)));
|
||||
|
||||
var THRUST_GRAB_SCALING = 100000.0;
|
||||
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustFront);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime);
|
||||
MyAvatar.addThrust(thrustUp);
|
||||
|
||||
// add some rotation...
|
||||
var deltaRotation = getGrabRotation();
|
||||
var PITCH_SCALING = 2.5;
|
||||
var PITCH_DEAD_ZONE = 2.0;
|
||||
var YAW_SCALING = 2.5;
|
||||
var ROLL_SCALING = 2.0;
|
||||
|
||||
var euler = Quat.safeEulerAngles(deltaRotation);
|
||||
|
||||
// Adjust body yaw by roll from controller
|
||||
var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) +
|
||||
(euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
|
||||
// Adjust head pitch from controller
|
||||
var pitch = 0.0;
|
||||
if (Math.abs(euler.x) > PITCH_DEAD_ZONE) {
|
||||
pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE);
|
||||
}
|
||||
MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime);
|
||||
|
||||
// TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster)
|
||||
|
||||
}
|
||||
|
||||
wasGrabbingWithRightHand = grabbingWithRightHand;
|
||||
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
||||
}
|
||||
|
||||
// Update for joysticks and move button
|
||||
var THRUST_DEAD_ZONE = 0.1;
|
||||
var ROTATE_DEAD_ZONE = 0.1;
|
||||
function flyWithHydra(deltaTime) {
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
||||
if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) {
|
||||
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
|
||||
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
|
||||
}
|
||||
var headOrientation = MyAvatar.headOrientation;
|
||||
|
||||
var front = Quat.getFront(headOrientation);
|
||||
var right = Quat.getRight(headOrientation);
|
||||
var up = Quat.getUp(headOrientation);
|
||||
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.y * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustFront);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
} else {
|
||||
thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
|
||||
}
|
||||
|
||||
// View Controller
|
||||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) {
|
||||
|
||||
// change the body yaw based on our x controller
|
||||
var orientation = MyAvatar.orientation;
|
||||
var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
|
||||
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
|
||||
|
||||
// change the headPitch based on our x controller
|
||||
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
|
||||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior(deltaTime);
|
||||
displayDebug();
|
||||
|
||||
}
|
||||
|
||||
Script.update.connect(flyWithHydra);
|
||||
Controller.captureJoystick(THRUST_CONTROLLER);
|
||||
Controller.captureJoystick(VIEW_CONTROLLER);
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseJoystick(THRUST_CONTROLLER);
|
||||
Controller.releaseJoystick(VIEW_CONTROLLER);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
|
@ -20,10 +20,11 @@ var BALL_SIZE = 0.08;
|
|||
var PADDLE_SIZE = 0.20;
|
||||
var PADDLE_THICKNESS = 0.06;
|
||||
var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };
|
||||
var BALL_COLOR = { red: 255, green: 0, blue: 0 };
|
||||
var BALL_COLOR = { red: 0, green: 255, blue: 0 };
|
||||
var LINE_COLOR = { red: 255, green: 255, blue: 0 };
|
||||
var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 };
|
||||
|
||||
//probably we need to fix these initial values (offsets and orientation)
|
||||
var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 };
|
||||
var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 };
|
||||
var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0);
|
||||
|
@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0;
|
|||
var lastSoundTime = 0;
|
||||
var gameOn = false;
|
||||
var leftHanded = true;
|
||||
var controllerID;
|
||||
|
||||
|
||||
function setControllerID() {
|
||||
if (leftHanded) {
|
||||
controllerID = 1;
|
||||
} else {
|
||||
controllerID = 3;
|
||||
}
|
||||
}
|
||||
|
||||
setControllerID();
|
||||
Menu.addMenu("PaddleBall");
|
||||
Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true });
|
||||
|
||||
|
@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line;
|
|||
function createEntities() {
|
||||
ball = Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: Controller.getSpatialControlPosition(controllerID),
|
||||
position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
|
||||
dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE },
|
||||
color: BALL_COLOR,
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
|
@ -73,28 +63,28 @@ function createEntities() {
|
|||
|
||||
paddle = Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: Controller.getSpatialControlPosition(controllerID),
|
||||
position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
|
||||
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 },
|
||||
color: PADDLE_COLOR,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.10,
|
||||
visible: false,
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
|
||||
collisionsWillMove: false });
|
||||
rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
|
||||
collisionsWillMove: false });
|
||||
|
||||
modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";
|
||||
paddleModel = Entities.addEntity(
|
||||
{ type: "Model",
|
||||
position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET),
|
||||
position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET),
|
||||
dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 },
|
||||
color: PADDLE_COLOR,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
ignoreCollisions: true,
|
||||
modelURL: modelURL,
|
||||
damping: 0.10,
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
|
||||
collisionsWillMove: false });
|
||||
rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
|
||||
collisionsWillMove: false });
|
||||
|
||||
line = Overlays.addOverlay("line3d", {
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
|
@ -118,7 +108,7 @@ function deleteEntities() {
|
|||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
var palmPosition = Controller.getSpatialControlPosition(controllerID);
|
||||
var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
|
||||
var controllerActive = (Vec3.length(palmPosition) > 0);
|
||||
|
||||
if (!gameOn && controllerActive) {
|
||||
|
@ -133,8 +123,8 @@ function update(deltaTime) {
|
|||
}
|
||||
|
||||
var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));
|
||||
var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation);
|
||||
var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(),
|
||||
var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation);
|
||||
var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
|
||||
Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET ));
|
||||
|
||||
var props = Entities.getEntityProperties(ball);
|
||||
|
@ -146,10 +136,10 @@ function update(deltaTime) {
|
|||
Entities.editEntity(ball, { velocity: ballVelocity });
|
||||
Overlays.editOverlay(line, { start: props.position, end: holdPosition });
|
||||
Entities.editEntity(paddle, { position: holdPosition,
|
||||
velocity: Controller.getSpatialControlVelocity(controllerID),
|
||||
velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
|
||||
rotation: paddleWorldOrientation });
|
||||
Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)),
|
||||
velocity: Controller.getSpatialControlVelocity(controllerID),
|
||||
velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
|
||||
rotation: paddleWorldOrientation });
|
||||
|
||||
}
|
||||
|
@ -182,7 +172,6 @@ function menuItemEvent(menuItem) {
|
|||
leftHanded = Menu.isOptionChecked("Left-Handed");
|
||||
}
|
||||
if ((leftHanded != oldHanded) && gameOn) {
|
||||
setControllerID();
|
||||
deleteEntities();
|
||||
createEntities();
|
||||
}
|
||||
|
|
|
@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
|||
|
||||
// maybe we should make these constants...
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_TIP = 1;
|
||||
var LEFT_BUTTON_FWD = 5;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_TIP = 3;
|
||||
var RIGHT_BUTTON_FWD = 11;
|
||||
var RIGHT_BUTTON_3 = 9;
|
||||
|
||||
var BALL_RADIUS = 0.08;
|
||||
var GRAVITY_STRENGTH = 3.0;
|
||||
|
@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) {
|
|||
}
|
||||
|
||||
function checkControllerSide(whichSide) {
|
||||
var BUTTON_FWD;
|
||||
var BUTTON_3;
|
||||
var TRIGGER;
|
||||
var palmPosition;
|
||||
var palmRotation;
|
||||
var ballAlreadyInHand;
|
||||
|
@ -79,35 +69,35 @@ function checkControllerSide(whichSide) {
|
|||
var linearVelocity;
|
||||
var angularVelocity;
|
||||
var AVERAGE_FACTOR = 0.33;
|
||||
|
||||
var grabButtonPressed;
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
BUTTON_FWD = LEFT_BUTTON_FWD;
|
||||
BUTTON_3 = LEFT_BUTTON_3;
|
||||
TRIGGER = 0;
|
||||
palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
|
||||
palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM));
|
||||
palmPosition = MyAvatar.leftHandPose.translation;
|
||||
palmRotation = MyAvatar.leftHandPose.rotation;
|
||||
ballAlreadyInHand = leftBallAlreadyInHand;
|
||||
handMessage = "LEFT";
|
||||
averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(LEFT_TIP)),
|
||||
averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity),
|
||||
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));
|
||||
|
||||
linearVelocity = averageLinearVelocity[0];
|
||||
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP));
|
||||
angularVelocity = MyAvatar.leftHandTipPose.angularVelocity;
|
||||
grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5);
|
||||
|
||||
} else {
|
||||
BUTTON_FWD = RIGHT_BUTTON_FWD;
|
||||
BUTTON_3 = RIGHT_BUTTON_3;
|
||||
TRIGGER = 1;
|
||||
palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
|
||||
palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM));
|
||||
palmPosition = MyAvatar.rightHandPose.translation;
|
||||
palmRotation = MyAvatar.rightHandPose.rotation;
|
||||
ballAlreadyInHand = rightBallAlreadyInHand;
|
||||
averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(RIGHT_TIP)),
|
||||
averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity),
|
||||
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));
|
||||
|
||||
linearVelocity = averageLinearVelocity[1];
|
||||
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP));
|
||||
angularVelocity = MyAvatar.rightHandTipPose.angularVelocity;
|
||||
handMessage = "RIGHT";
|
||||
grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5);
|
||||
|
||||
}
|
||||
|
||||
var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5));
|
||||
|
||||
|
||||
// If I don't currently have a ball in my hand, then try to catch closest one
|
||||
if (!ballAlreadyInHand && grabButtonPressed) {
|
||||
var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
|
||||
|
@ -187,10 +177,8 @@ function checkControllerSide(whichSide) {
|
|||
if (ballAlreadyInHand) {
|
||||
if (whichSide == LEFT_PALM) {
|
||||
handEntity = leftHandEntity;
|
||||
whichTip = LEFT_TIP;
|
||||
} else {
|
||||
handEntity = rightHandEntity;
|
||||
whichTip = RIGHT_TIP;
|
||||
}
|
||||
|
||||
// If holding the ball keep it in the palm
|
||||
|
@ -231,22 +219,10 @@ function checkControllerSide(whichSide) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkController(deltaTime) {
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
|
||||
// this is expected for hydras
|
||||
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
||||
debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger);
|
||||
return; // bail if no hydra
|
||||
}
|
||||
|
||||
checkControllerSide(LEFT_PALM);
|
||||
checkControllerSide(RIGHT_PALM);
|
||||
checkControllerSide(LEFT_PALM);
|
||||
checkControllerSide(RIGHT_PALM);
|
||||
}
|
||||
|
||||
|
||||
|
|
10
examples/controllers/rightClickExample.js
Normal file
10
examples/controllers/rightClickExample.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
var MAPPING_NAME = "com.highfidelity.rightClickExample";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) {
|
||||
print("Keyboard.RightMouseClicked");
|
||||
});
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
});
|
|
@ -8,6 +8,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.load("away.js");
|
||||
Script.load("progress.js");
|
||||
Script.load("edit.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
|
|
144
examples/drylake/explodeHelicopter.js
Normal file
144
examples/drylake/explodeHelicopter.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
var explosionSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/eric/sounds/explosion.wav");
|
||||
|
||||
var partsURLS = [{
|
||||
url: "https://s3.amazonaws.com/hifi-public/eric/models/blade.fbx",
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 2
|
||||
}
|
||||
}, {
|
||||
url: "https://s3.amazonaws.com/hifi-public/eric/models/body.fbx",
|
||||
dimensions: {
|
||||
x: 2.2,
|
||||
y: 2.98,
|
||||
z: 7.96
|
||||
}
|
||||
}, {
|
||||
url: "https://s3.amazonaws.com/hifi-public/eric/models/tail.fbx",
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
}
|
||||
}];
|
||||
|
||||
var parts = [];
|
||||
var emitters = [];
|
||||
|
||||
var explodePosition;
|
||||
var helicopter;
|
||||
var entities = Entities.findEntities(MyAvatar.position, 2000);
|
||||
for (i = 0; i < entities.length; i++) {
|
||||
var name = Entities.getEntityProperties(entities[i], 'name').name;
|
||||
if (name === "Helicopter") {
|
||||
var helicopter = entities[i];
|
||||
explodeHelicopter(Entities.getEntityProperties(helicopter, 'position').position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function explodeHelicopter(explodePosition) {
|
||||
Audio.playSound(explosionSound, {
|
||||
position: explodePosition,
|
||||
volume: 0.5
|
||||
});
|
||||
Entities.deleteEntity(helicopter);
|
||||
for (var i = 0; i < partsURLS.length; i++) {
|
||||
var position = Vec3.sum(explodePosition, {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
});
|
||||
var part = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: partsURLS[i].url,
|
||||
dimensions: partsURLS[i].dimensions,
|
||||
position: position,
|
||||
shapeType: "box",
|
||||
collisionsWillMove: true,
|
||||
damping: 0,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.6,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: Math.random(),
|
||||
y: -10,
|
||||
z: Math.random()
|
||||
}
|
||||
});
|
||||
|
||||
var emitter = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "fire",
|
||||
isEmitting: true,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
position: explodePosition,
|
||||
emitRate: 100,
|
||||
colorStart: {
|
||||
red: 70,
|
||||
green: 70,
|
||||
blue: 137
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 99,
|
||||
blue: 42
|
||||
},
|
||||
colorFinish: {
|
||||
red: 255,
|
||||
green: 99,
|
||||
blue: 32
|
||||
},
|
||||
radiusSpread: 0.2,
|
||||
radiusStart: 0.3,
|
||||
radiusEnd: 0.04,
|
||||
particleRadius: 0.09,
|
||||
radiusFinish: 0.0,
|
||||
emitSpeed: 0.1,
|
||||
speedSpread: 0.1,
|
||||
alphaStart: 0.1,
|
||||
alpha: 0.7,
|
||||
alphaFinish: 0.1,
|
||||
emitOrientation: Quat.fromPitchYawRollDegrees(-90, 0, 0),
|
||||
emitDimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 0.1
|
||||
},
|
||||
polarFinish: Math.PI,
|
||||
polarStart: 0,
|
||||
|
||||
accelerationSpread: {
|
||||
x: 0.1,
|
||||
y: 0.01,
|
||||
z: 0.1
|
||||
},
|
||||
lifespan: 1,
|
||||
});
|
||||
emitters.push(emitter)
|
||||
parts.push(part);
|
||||
}
|
||||
|
||||
Script.setTimeout(function() {
|
||||
var pos = Entities.getEntityProperties(parts[1], "position").position;
|
||||
Entities.editEntity(emitters[0], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||
Entities.editEntity(emitters[1], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||
Entities.editEntity(emitters[2], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||
}, 5000)
|
||||
|
||||
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
parts.forEach(function(part) {
|
||||
Entities.deleteEntity(part);
|
||||
});
|
||||
emitters.forEach(function(emitter){
|
||||
Entities.deleteEntity(emitter);
|
||||
})
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
132
examples/drylake/helicopter.js
Normal file
132
examples/drylake/helicopter.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx?v3";
|
||||
var animationURL = "https://s3.amazonaws.com/hifi-public/eric/models/bladeAnimation.fbx?v7";
|
||||
var spawnPosition = {
|
||||
x: 1031,
|
||||
y: 145,
|
||||
z: 1041
|
||||
};
|
||||
|
||||
var speed = 0;
|
||||
|
||||
var helicopterSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/ryan/helicopter.L.wav");
|
||||
var audioInjector = Audio.playSound(helicopterSound, {
|
||||
volume: 0.3,
|
||||
loop: true
|
||||
});
|
||||
|
||||
// These constants define the Spotlight position and orientation relative to the model
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 2,
|
||||
y: 0,
|
||||
z: -5
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
|
||||
// Evaluate the world light entity positions and orientations from the model ones
|
||||
function evalLightWorldTransform(modelPos, modelRot) {
|
||||
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var helicopter = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "Helicopter",
|
||||
modelURL: modelURL,
|
||||
animation: {
|
||||
url: animationURL,
|
||||
running: true,
|
||||
fps: 180
|
||||
|
||||
},
|
||||
dimensions: {
|
||||
x: 12.13,
|
||||
y: 3.14,
|
||||
z: 9.92
|
||||
},
|
||||
position: spawnPosition,
|
||||
});
|
||||
|
||||
|
||||
|
||||
var spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
name: "helicopter light",
|
||||
intensity: 2,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 1,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 200
|
||||
},
|
||||
exponent: 0.01,
|
||||
cutoff: 10,
|
||||
isSpotlight: true
|
||||
});
|
||||
|
||||
var debugLight = Entities.addEntity({
|
||||
type: "Box",
|
||||
dimensions: {
|
||||
x: .1,
|
||||
y: .1,
|
||||
z: .3
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 0
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(debugLight);
|
||||
Entities.deleteEntity(helicopter);
|
||||
Entities.deleteEntity(spotlight);
|
||||
|
||||
}
|
||||
|
||||
function update() {
|
||||
var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']);
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
Entities.editEntity(spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q
|
||||
});
|
||||
Entities.editEntity(debugLight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q
|
||||
});
|
||||
|
||||
audioInjector.setOptions({
|
||||
position: modelProperties.position,
|
||||
});
|
||||
|
||||
//Move forward
|
||||
var newRotation = Quat.multiply(modelProperties.rotation, {
|
||||
x: 0,
|
||||
y: .002,
|
||||
z: 0,
|
||||
w: 1
|
||||
})
|
||||
var newPosition = Vec3.sum(modelProperties.position, Vec3.multiply(speed, Quat.getFront(modelProperties.rotation)));
|
||||
Entities.editEntity(helicopter, {
|
||||
position: newPosition,
|
||||
rotation: newRotation
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(cleanup);
|
610
examples/edit.js
610
examples/edit.js
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,7 @@
|
|||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../toys/breakdanceCore.js");
|
||||
Script.include("../breakdanceCore.js");
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var _this;
|
||||
|
|
42
examples/entityScripts/createParamsEntity.js
Normal file
42
examples/entityScripts/createParamsEntity.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// createParamsEntity.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 11/6/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script demonstrates creating an entity and sending it a method call with parameters.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var PARAMS_SCRIPT_URL = Script.resolvePath('paramsEntity.js');
|
||||
|
||||
var testEntity = Entities.addEntity({
|
||||
name: 'paramsTestEntity',
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
},
|
||||
type: 'Box',
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
visible: false,
|
||||
script: PARAMS_SCRIPT_URL
|
||||
});
|
||||
|
||||
|
||||
var subData1 = ['apple', 'banana', 'orange'];
|
||||
var subData2 = {
|
||||
thing: 1,
|
||||
otherThing: 2
|
||||
};
|
||||
var data = [subData1, JSON.stringify(subData2), 'third'];
|
||||
Script.setTimeout(function() {
|
||||
print('sending data to entity')
|
||||
Entities.callEntityMethod(testEntity, 'testParams', data);
|
||||
}, 1500)
|
21
examples/entityScripts/createRecorder.js
Normal file
21
examples/entityScripts/createRecorder.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(6, Quat.getFront(rotation)));
|
||||
|
||||
var recordAreaEntity = Entities.addEntity({
|
||||
name: 'recorderEntity',
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
type: 'Box',
|
||||
position: center,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
visible: true,
|
||||
script: "https://hifi-public.s3.amazonaws.com/sam/record/recordingEntityScript.js",
|
||||
});
|
45
examples/entityScripts/paramsEntity.js
Normal file
45
examples/entityScripts/paramsEntity.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// paramsEntity.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 11/6/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script demonstrates how to recieve parameters from a Entities.callEntityMethod call
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
(function() {
|
||||
|
||||
function ParamsEntity() {
|
||||
return;
|
||||
}
|
||||
|
||||
ParamsEntity.prototype = {
|
||||
preload: function(entityID) {
|
||||
print('entity loaded')
|
||||
this.entityID = entityID;
|
||||
},
|
||||
testParams: function(myID, paramsArray) {
|
||||
|
||||
paramsArray.forEach(function(param) {
|
||||
var p;
|
||||
try {
|
||||
p = JSON.parse(param);
|
||||
print("it's a json param")
|
||||
print('json param property:' + p.thing);
|
||||
} catch (err) {
|
||||
print('not a json param')
|
||||
p = param;
|
||||
print('param is:' + p);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new ParamsEntity();
|
||||
});
|
106
examples/entityScripts/recordingEntityScript.js
Normal file
106
examples/entityScripts/recordingEntityScript.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// recordingEntityScript.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Alessandro Signa on 11/12/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
// All the avatars in the area when the master presses the button will start/stop recording.
|
||||
//
|
||||
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
(function () {
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js");
|
||||
|
||||
var insideRecorderArea = false;
|
||||
var enteredInTime = false;
|
||||
var isAvatarRecording = false;
|
||||
var _this;
|
||||
|
||||
function recordingEntity() {
|
||||
_this = this;
|
||||
return;
|
||||
}
|
||||
|
||||
function update() {
|
||||
var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted;
|
||||
if (isRecordingStarted && !isAvatarRecording) {
|
||||
_this.startRecording();
|
||||
} else if ((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)) {
|
||||
_this.stopRecording();
|
||||
} else if (!isRecordingStarted && insideRecorderArea && !enteredInTime) {
|
||||
//if an avatar enters the zone while a recording is started he will be able to participate to the next group recording
|
||||
enteredInTime = true;
|
||||
}
|
||||
};
|
||||
|
||||
recordingEntity.prototype = {
|
||||
|
||||
preload: function (entityID) {
|
||||
print("RECORDING ENTITY PRELOAD");
|
||||
this.entityID = entityID;
|
||||
|
||||
var entityProperties = Entities.getEntityProperties(_this.entityID);
|
||||
if (!entityProperties.ignoreForCollisions) {
|
||||
Entities.editEntity(_this.entityID, { ignoreForCollisions: true });
|
||||
}
|
||||
|
||||
//print(JSON.stringify(entityProperties));
|
||||
var recordingKey = getEntityCustomData("recordingKey", _this.entityID, undefined);
|
||||
if (recordingKey === undefined) {
|
||||
setEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false });
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
},
|
||||
enterEntity: function (entityID) {
|
||||
print("entering in the recording area");
|
||||
insideRecorderArea = true;
|
||||
var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted;
|
||||
if (!isRecordingStarted) {
|
||||
//i'm in the recording area in time (before the event starts)
|
||||
enteredInTime = true;
|
||||
}
|
||||
},
|
||||
leaveEntity: function (entityID) {
|
||||
print("leaving the recording area");
|
||||
insideRecorderArea = false;
|
||||
enteredInTime = false;
|
||||
},
|
||||
|
||||
startRecording: function (entityID) {
|
||||
if (enteredInTime && !isAvatarRecording) {
|
||||
print("RECORDING STARTED");
|
||||
Recording.startRecording();
|
||||
isAvatarRecording = true;
|
||||
}
|
||||
},
|
||||
|
||||
stopRecording: function (entityID) {
|
||||
if (isAvatarRecording) {
|
||||
print("RECORDING ENDED");
|
||||
Recording.stopRecording();
|
||||
Recording.loadLastRecording();
|
||||
isAvatarRecording = false;
|
||||
recordingFile = Window.save("Save recording to file", "./groupRecording", "Recordings (*.hfr)");
|
||||
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
|
||||
Recording.saveRecording(recordingFile);
|
||||
}
|
||||
}
|
||||
},
|
||||
unload: function (entityID) {
|
||||
print("RECORDING ENTITY UNLOAD");
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new recordingEntity();
|
||||
});
|
102
examples/entityScripts/recordingMaster.js
Normal file
102
examples/entityScripts/recordingMaster.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// recordingMaster.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Alessandro Signa on 11/12/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script to find the recorder (created by crateRecorder.js) and drive the start/end of the recording for anyone who is inside the box
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/toolBars.js");
|
||||
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js");
|
||||
|
||||
|
||||
|
||||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(rotation)));
|
||||
|
||||
var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
var ALPHA_ON = 1.0;
|
||||
var ALPHA_OFF = 0.7;
|
||||
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
|
||||
|
||||
var toolBar = null;
|
||||
var recordIcon;
|
||||
|
||||
var isRecordingEntityFound = false;
|
||||
|
||||
var isRecording = false;
|
||||
|
||||
var recordAreaEntity = null;
|
||||
findRecorder();
|
||||
|
||||
function findRecorder() {
|
||||
foundEntities = Entities.findEntities(MyAvatar.position, 50);
|
||||
for (var i = 0; i < foundEntities.length; i++) {
|
||||
var name = Entities.getEntityProperties(foundEntities[i], "name").name;
|
||||
if (name === "recorderEntity") {
|
||||
recordAreaEntity = foundEntities[i];
|
||||
isRecordingEntityFound = true;
|
||||
print("Found recorder Entity!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupToolBar();
|
||||
|
||||
function setupToolBar() {
|
||||
if (toolBar != null) {
|
||||
print("Multiple calls to setupToolBar()");
|
||||
return;
|
||||
}
|
||||
Tool.IMAGE_HEIGHT /= 2;
|
||||
Tool.IMAGE_WIDTH /= 2;
|
||||
|
||||
toolBar = new ToolBar(0, 100, ToolBar.HORIZONTAL); //put the button in the up-left corner
|
||||
|
||||
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
||||
|
||||
recordIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-record.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
x: 0, y: 0,
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON,
|
||||
visible: isRecordingEntityFound,
|
||||
}, true, isRecording);
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
if (recordIcon === toolBar.clicked(clickedOverlay, false)) {
|
||||
if (!isRecording) {
|
||||
print("I'm the master. I want to start recording");
|
||||
isRecording = true;
|
||||
setEntityCustomData("recordingKey", recordAreaEntity, {isRecordingStarted: true});
|
||||
|
||||
} else {
|
||||
print("I want to stop recording");
|
||||
isRecording = false;
|
||||
setEntityCustomData("recordingKey", recordAreaEntity, {isRecordingStarted: false});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cleanup() {
|
||||
toolBar.cleanup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
90
examples/entityScripts/synchronizerEntityScript.js
Normal file
90
examples/entityScripts/synchronizerEntityScript.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// synchronizerEntityScript.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Alessandro Signa on 11/12/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
// This script shows how to create a synchronized event between avatars trhough an entity.
|
||||
// It works using the entity's userData: the master change its value and every client checks it every frame
|
||||
// This entity prints a message when the event starts and when it ends.
|
||||
// The client running synchronizerMaster.js is the event master and it decides when the event starts/ends by pressing a button.
|
||||
// All the avatars in the area when the master presses the button will receive a message.
|
||||
//
|
||||
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
var insideArea = false;
|
||||
var isJoiningTheEvent = false;
|
||||
var _this;
|
||||
|
||||
|
||||
|
||||
function ParamsEntity() {
|
||||
_this = this;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ParamsEntity.prototype = {
|
||||
update: function(){
|
||||
var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData);
|
||||
var valueToCheck = userData.myKey.valueToCheck;
|
||||
if(valueToCheck && !isJoiningTheEvent){
|
||||
_this.sendMessage();
|
||||
}else if((!valueToCheck && isJoiningTheEvent) || (isJoiningTheEvent && !insideArea)){
|
||||
_this.stopMessage();
|
||||
}
|
||||
},
|
||||
preload: function(entityID) {
|
||||
print('entity loaded')
|
||||
this.entityID = entityID;
|
||||
Script.update.connect(_this.update);
|
||||
},
|
||||
enterEntity: function(entityID) {
|
||||
print("enterEntity("+entityID+")");
|
||||
var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData);
|
||||
var valueToCheck = userData.myKey.valueToCheck;
|
||||
if(!valueToCheck){
|
||||
//i'm in the area in time (before the event starts)
|
||||
insideArea = true;
|
||||
}
|
||||
change(entityID);
|
||||
},
|
||||
leaveEntity: function(entityID) {
|
||||
print("leaveEntity("+entityID+")");
|
||||
Entities.editEntity(entityID, { color: { red: 255, green: 190, blue: 20} });
|
||||
insideArea = false;
|
||||
},
|
||||
|
||||
sendMessage: function(myID){
|
||||
if(insideArea && !isJoiningTheEvent){
|
||||
print("The event started");
|
||||
isJoiningTheEvent = true;
|
||||
}
|
||||
},
|
||||
|
||||
stopMessage: function(myID){
|
||||
if(isJoiningTheEvent){
|
||||
print("The event ended");
|
||||
isJoiningTheEvent = false;
|
||||
}
|
||||
},
|
||||
clean: function(entityID) {
|
||||
Script.update.disconnect(_this.update);
|
||||
}
|
||||
}
|
||||
|
||||
function change(entityID) {
|
||||
Entities.editEntity(entityID, { color: { red: 255, green: 100, blue: 220} });
|
||||
}
|
||||
|
||||
|
||||
return new ParamsEntity();
|
||||
});
|
117
examples/entityScripts/synchronizerMaster.js
Normal file
117
examples/entityScripts/synchronizerMaster.js
Normal file
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// synchronizerMaster.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Alessandro Signa on 11/12/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script to spawn a box (synchronizer) and drive the start/end of the event for anyone who is inside the box
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var PARAMS_SCRIPT_URL = Script.resolvePath('synchronizerEntityScript.js');
|
||||
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
Script.include("../libraries/toolBars.js");
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
|
||||
|
||||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(rotation)));
|
||||
|
||||
var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
var ALPHA_ON = 1.0;
|
||||
var ALPHA_OFF = 0.7;
|
||||
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
|
||||
|
||||
var toolBar = null;
|
||||
var recordIcon;
|
||||
|
||||
|
||||
|
||||
var isHappening = false;
|
||||
|
||||
var testEntity = Entities.addEntity({
|
||||
name: 'paramsTestEntity',
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 1,
|
||||
z: 2
|
||||
},
|
||||
type: 'Box',
|
||||
position: center,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
visible: true,
|
||||
ignoreForCollisions: true,
|
||||
script: PARAMS_SCRIPT_URL,
|
||||
|
||||
userData: JSON.stringify({
|
||||
myKey: {
|
||||
valueToCheck: false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
setupToolBar();
|
||||
|
||||
function setupToolBar() {
|
||||
if (toolBar != null) {
|
||||
print("Multiple calls to setupToolBar()");
|
||||
return;
|
||||
}
|
||||
Tool.IMAGE_HEIGHT /= 2;
|
||||
Tool.IMAGE_WIDTH /= 2;
|
||||
|
||||
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner
|
||||
|
||||
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
||||
|
||||
recordIcon = toolBar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-record.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
x: 0, y: 0,
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON,
|
||||
visible: true
|
||||
}, true, isHappening);
|
||||
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
if (recordIcon === toolBar.clicked(clickedOverlay, false)) {
|
||||
if (!isHappening) {
|
||||
print("I'm the event master. I want the event starts");
|
||||
isHappening = true;
|
||||
setEntityCustomData("myKey", testEntity, {valueToCheck: true});
|
||||
|
||||
} else {
|
||||
print("I want the event stops");
|
||||
isHappening = false;
|
||||
setEntityCustomData("myKey", testEntity, {valueToCheck: false});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cleanup() {
|
||||
toolBar.cleanup();
|
||||
Entities.callEntityMethod(testEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings
|
||||
Entities.deleteEntity(testEntity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
|
@ -10,25 +10,19 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// initialize our triggers
|
||||
var triggerPulled = new Array();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
for (t = 0; t < numberOfTriggers; t++) {
|
||||
var NUMBER_OF_TRIGGERS = 2;
|
||||
for (t = 0; t < NUMBER_OF_TRIGGERS; t++) {
|
||||
triggerPulled[t] = false;
|
||||
}
|
||||
|
||||
var triggers = new Array();
|
||||
triggers[0] = Controller.Standard.LT;
|
||||
triggers[1] = Controller.Standard.RT;
|
||||
function checkController(deltaTime) {
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
var triggerToggled = false;
|
||||
|
||||
// this is expected for hydras
|
||||
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
|
||||
for (var t = 0; t < numberOfTriggers; t++) {
|
||||
var triggerValue = Controller.getTriggerValue(t);
|
||||
|
||||
for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) {
|
||||
var triggerValue = Controller.getValue(triggers[t]);
|
||||
if (triggerPulled[t]) {
|
||||
// must release to at least 0.1
|
||||
if (triggerValue < 0.1) {
|
||||
|
@ -41,17 +35,14 @@ function checkController(deltaTime) {
|
|||
triggerToggled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerToggled) {
|
||||
print("a trigger was toggled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(checkController);
|
||||
|
||||
function printKeyEvent(eventName, event) {
|
||||
print(eventName);
|
||||
print(" event.key=" + event.key);
|
||||
|
@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) {
|
|||
}
|
||||
function keyPressEvent(event) {
|
||||
printKeyEvent("keyPressEvent", event);
|
||||
|
||||
if (event.text == "A") {
|
||||
print("the A key was pressed");
|
||||
}
|
||||
|
@ -72,10 +62,8 @@ function keyPressEvent(event) {
|
|||
print("the <space> key was pressed");
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
printKeyEvent("keyReleaseEvent", event);
|
||||
|
||||
if (event.text == "A") {
|
||||
print("the A key was released");
|
||||
}
|
||||
|
@ -83,11 +71,9 @@ function keyReleaseEvent(event) {
|
|||
print("the <space> key was pressed");
|
||||
}
|
||||
}
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
// prevent the A key from going through to the application
|
||||
Controller.captureKeyEvents({ text: "A" });
|
||||
Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key
|
||||
|
@ -95,8 +81,6 @@ Controller.captureKeyEvents({ text: " " });
|
|||
Controller.captureKeyEvents({ text: "@", isMeta: true });
|
||||
Controller.captureKeyEvents({ text: "page up" });
|
||||
Controller.captureKeyEvents({ text: "page down" });
|
||||
|
||||
|
||||
function printMouseEvent(eventName, event) {
|
||||
print(eventName);
|
||||
print(" event.x,y=" + event.x + ", " + event.y);
|
||||
|
@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) {
|
|||
print(" event.isMeta=" + event.isMeta);
|
||||
print(" event.isAlt=" + event.isAlt);
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
printMouseEvent("mouseMoveEvent", event);
|
||||
}
|
||||
function mousePressEvent(event) {
|
||||
printMouseEvent("mousePressEvent", event);
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
printMouseEvent("mouseReleaseEvent", event);
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
function printTouchEvent(eventName, event) {
|
||||
print(eventName);
|
||||
|
||||
|
@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) {
|
|||
print(" event.radius=" + event.radius);
|
||||
print(" event.isPinching=" + event.isPinching);
|
||||
print(" event.isPinchOpening=" + event.isPinchOpening);
|
||||
|
||||
print(" event.angle=" + event.angle);
|
||||
for (var i = 0; i < event.points.length; i++) {
|
||||
print(" event.angles[" + i + "]:" + event.angles[i]);
|
||||
|
@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) {
|
|||
print(" event.isRotating=" + event.isRotating);
|
||||
print(" event.rotating=" + event.rotating);
|
||||
}
|
||||
|
||||
function touchBeginEvent(event) {
|
||||
printTouchEvent("touchBeginEvent", event);
|
||||
}
|
||||
|
||||
function touchUpdateEvent(event) {
|
||||
printTouchEvent("touchUpdateEvent", event);
|
||||
}
|
||||
|
||||
function touchEndEvent(event) {
|
||||
printTouchEvent("touchEndEvent", event);
|
||||
}
|
||||
|
@ -167,8 +143,6 @@ function touchEndEvent(event) {
|
|||
Controller.touchBeginEvent.connect(touchBeginEvent);
|
||||
Controller.touchUpdateEvent.connect(touchUpdateEvent);
|
||||
Controller.touchEndEvent.connect(touchEndEvent);
|
||||
|
||||
|
||||
function wheelEvent(event) {
|
||||
print("wheelEvent");
|
||||
print(" event.x,y=" + event.x + ", " + event.y);
|
||||
|
@ -182,9 +156,7 @@ function wheelEvent(event) {
|
|||
print(" event.isMeta=" + event.isMeta);
|
||||
print(" event.isAlt=" + event.isAlt);
|
||||
}
|
||||
|
||||
Controller.wheelEvent.connect(wheelEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseKeyEvents({ text: "A" });
|
||||
|
@ -194,5 +166,4 @@ function scriptEnding() {
|
|||
Controller.releaseKeyEvents({ text: "page up" });
|
||||
Controller.releaseKeyEvents({ text: "page down" });
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
73
examples/example/avatarcontrol/handControlledHead.js
Normal file
73
examples/example/avatarcontrol/handControlledHead.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// handControlledHead.js
|
||||
// examples
|
||||
//
|
||||
// Created by Alessandro Signa on 10/11/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script allows you to look around, driving the rotation of the avatar's head by the right hand orientation.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
const YAW_MULTIPLIER = 20000;
|
||||
const PITCH_MULTIPLIER = 15000;
|
||||
const EPSILON = 0.001;
|
||||
var firstPress = true;
|
||||
var handPreviousVerticalRotation = 0.0;
|
||||
var handCurrentVerticalRotation = 0.0;
|
||||
var handPreviousHorizontalRotation = 0.0;
|
||||
var handCurrentHorizontalRotation = 0.0;
|
||||
var rotatedHandPosition;
|
||||
var rotatedTipPosition;
|
||||
|
||||
function update(deltaTime) {
|
||||
if(Controller.getValue(Controller.Standard.RightPrimaryThumb)){
|
||||
pitchManager(deltaTime);
|
||||
}else if(!firstPress){
|
||||
firstPress = true;
|
||||
}
|
||||
if(firstPress && MyAvatar.headYaw){
|
||||
MyAvatar.headYaw -= MyAvatar.headYaw/10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function pitchManager(deltaTime){
|
||||
|
||||
rotatedHandPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandPosition());
|
||||
rotatedTipPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandTipPosition());
|
||||
|
||||
handCurrentVerticalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).y;
|
||||
handCurrentHorizontalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).x;
|
||||
|
||||
var handCurrentHorizontalRotationFiltered = handCurrentHorizontalRotation;
|
||||
|
||||
//to avoid yaw drift
|
||||
if((handCurrentHorizontalRotation - handPreviousHorizontalRotation) < EPSILON && (handCurrentHorizontalRotation - handPreviousHorizontalRotation) > -EPSILON){
|
||||
handCurrentHorizontalRotationFiltered = handPreviousHorizontalRotation;
|
||||
}
|
||||
|
||||
if(firstPress){
|
||||
handPreviousVerticalRotation = handCurrentVerticalRotation;
|
||||
handPreviousHorizontalRotation = handCurrentHorizontalRotation;
|
||||
firstPress = false;
|
||||
}
|
||||
|
||||
MyAvatar.headPitch += (handCurrentVerticalRotation - handPreviousVerticalRotation)*PITCH_MULTIPLIER*deltaTime;
|
||||
MyAvatar.headYaw -= (handCurrentHorizontalRotationFiltered - handPreviousHorizontalRotation)*YAW_MULTIPLIER*deltaTime;
|
||||
|
||||
|
||||
handPreviousVerticalRotation = handCurrentVerticalRotation;
|
||||
handPreviousHorizontalRotation = handCurrentHorizontalRotationFiltered;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function clean(){
|
||||
MyAvatar.headYaw = 0.0;
|
||||
}
|
||||
|
||||
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(clean);
|
265
examples/example/games/color_busters/colorBusterWand.js
Normal file
265
examples/example/games/color_busters/colorBusterWand.js
Normal file
|
@ -0,0 +1,265 @@
|
|||
//
|
||||
// colorBusterWand.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is the entity script that attaches to a wand for the Color Busters game
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
Script.include("../../../libraries/utils.js");
|
||||
|
||||
var COMBINED_COLOR_DURATION = 5;
|
||||
|
||||
var INDICATOR_OFFSET_UP = 0.40;
|
||||
|
||||
var REMOVE_CUBE_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/boop.wav';
|
||||
var COMBINE_COLORS_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/powerup.wav';
|
||||
|
||||
var COLOR_INDICATOR_DIMENSIONS = {
|
||||
x: 0.10,
|
||||
y: 0.10,
|
||||
z: 0.10
|
||||
};
|
||||
|
||||
var _this;
|
||||
|
||||
function ColorBusterWand() {
|
||||
_this = this;
|
||||
}
|
||||
|
||||
ColorBusterWand.prototype = {
|
||||
combinedColorsTimer: null,
|
||||
soundIsPlaying: false,
|
||||
preload: function(entityID) {
|
||||
print("preload");
|
||||
this.entityID = entityID;
|
||||
this.REMOVE_CUBE_SOUND = SoundCache.getSound(REMOVE_CUBE_SOUND_URL);
|
||||
this.COMBINE_COLORS_SOUND = SoundCache.getSound(COMBINE_COLORS_SOUND_URL);
|
||||
},
|
||||
|
||||
collisionWithEntity: function(me, otherEntity, collision) {
|
||||
var otherProperties = Entities.getEntityProperties(otherEntity, ["name", "userData"]);
|
||||
var myProperties = Entities.getEntityProperties(me, ["userData"]);
|
||||
var myUserData = JSON.parse(myProperties.userData);
|
||||
var otherUserData = JSON.parse(otherProperties.userData);
|
||||
|
||||
if (otherProperties.name === 'Hifi-ColorBusterWand') {
|
||||
print('HIT ANOTHER COLOR WAND!!');
|
||||
if (otherUserData.hifiColorBusterWandKey.colorLocked !== true && myUserData.hifiColorBusterWandKey.colorLocked !== true) {
|
||||
if (otherUserData.hifiColorBusterWandKey.originalColorName === myUserData.hifiColorBusterWandKey.originalColorName) {
|
||||
print('BUT ITS THE SAME COLOR!')
|
||||
return;
|
||||
} else {
|
||||
print('COMBINE COLORS!' + this.entityID);
|
||||
this.combineColorsWithOtherWand(otherUserData.hifiColorBusterWandKey.originalColorName, myUserData.hifiColorBusterWandKey.originalColorName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (otherProperties.name === 'Hifi-ColorBusterCube') {
|
||||
if (otherUserData.hifiColorBusterCubeKey.originalColorName === myUserData.hifiColorBusterWandKey.currentColor) {
|
||||
print('HIT THE SAME COLOR CUBE');
|
||||
this.removeCubeOfSameColor(otherEntity);
|
||||
} else {
|
||||
print('HIT A CUBE OF A DIFFERENT COLOR');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
combineColorsWithOtherWand: function(otherColor, myColor) {
|
||||
print('combining my :' + myColor + " with their: " + otherColor);
|
||||
|
||||
if ((myColor === 'violet') || (myColor === 'orange') || (myColor === 'green')) {
|
||||
print('MY WAND ALREADY COMBINED');
|
||||
return;
|
||||
}
|
||||
|
||||
var newColor;
|
||||
if ((otherColor === 'red' && myColor == 'yellow') || (myColor === 'red' && otherColor === 'yellow')) {
|
||||
//orange
|
||||
newColor = 'orange';
|
||||
}
|
||||
|
||||
if ((otherColor === 'red' && myColor == 'blue') || (myColor === 'red' && otherColor === 'blue')) {
|
||||
//violet
|
||||
newColor = 'violet';
|
||||
}
|
||||
|
||||
if ((otherColor === 'blue' && myColor == 'yellow') || (myColor === 'blue' && otherColor === 'yellow')) {
|
||||
//green.
|
||||
newColor = 'green';
|
||||
}
|
||||
|
||||
_this.combinedColorsTimer = Script.setTimeout(function() {
|
||||
_this.resetToOriginalColor(myColor);
|
||||
_this.combinedColorsTimer = null;
|
||||
}, COMBINED_COLOR_DURATION * 1000);
|
||||
|
||||
setEntityCustomData('hifiColorBusterWandKey', this.entityID, {
|
||||
owner: MyAvatar.sessionUUID,
|
||||
currentColor: newColor,
|
||||
originalColorName: myColor,
|
||||
colorLocked: false
|
||||
});
|
||||
|
||||
|
||||
this.playSoundAtCurrentPosition(false);
|
||||
},
|
||||
|
||||
setCurrentColor: function(newColor) {
|
||||
var color;
|
||||
|
||||
if (newColor === 'orange') {
|
||||
color = {
|
||||
red: 255,
|
||||
green: 165,
|
||||
blue: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (newColor === 'violet') {
|
||||
color = {
|
||||
red: 128,
|
||||
green: 0,
|
||||
blue: 128
|
||||
};
|
||||
}
|
||||
|
||||
if (newColor === 'green') {
|
||||
color = {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (newColor === 'red') {
|
||||
color = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (newColor === 'yellow') {
|
||||
color = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (newColor === 'blue') {
|
||||
color = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
};
|
||||
}
|
||||
|
||||
Entities.editEntity(this.colorIndicator, {
|
||||
color: color
|
||||
});
|
||||
|
||||
// print('SET THIS COLOR INDICATOR TO:' + newColor);
|
||||
},
|
||||
|
||||
resetToOriginalColor: function(myColor) {
|
||||
setEntityCustomData('hifiColorBusterWandKey', this.entityID, {
|
||||
owner: MyAvatar.sessionUUID,
|
||||
currentColor: myColor,
|
||||
originalColorName: myColor,
|
||||
colorLocked: false
|
||||
});
|
||||
|
||||
this.setCurrentColor(myColor);
|
||||
},
|
||||
|
||||
removeCubeOfSameColor: function(cube) {
|
||||
this.playSoundAtCurrentPosition(true);
|
||||
Entities.callEntityMethod(cube, 'cubeEnding');
|
||||
Entities.deleteEntity(cube);
|
||||
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
this.currentProperties = Entities.getEntityProperties(this.entityID);
|
||||
this.createColorIndicator();
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
this.currentProperties = Entities.getEntityProperties(this.entityID);
|
||||
|
||||
var color = JSON.parse(this.currentProperties.userData).hifiColorBusterWandKey.currentColor;
|
||||
|
||||
this.setCurrentColor(color);
|
||||
this.updateColorIndicatorLocation();
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
Entities.deleteEntity(this.colorIndicator);
|
||||
if (this.combinedColorsTimer !== null) {
|
||||
Script.clearTimeout(this.combinedColorsTimer);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
createColorIndicator: function(color) {
|
||||
|
||||
|
||||
var properties = {
|
||||
name: 'Hifi-ColorBusterIndicator',
|
||||
type: 'Box',
|
||||
dimensions: COLOR_INDICATOR_DIMENSIONS,
|
||||
position: this.currentProperties.position,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true
|
||||
}
|
||||
|
||||
this.colorIndicator = Entities.addEntity(properties);
|
||||
},
|
||||
|
||||
updateColorIndicatorLocation: function() {
|
||||
|
||||
var position;
|
||||
|
||||
var upVector = Quat.getUp(this.currentProperties.rotation);
|
||||
var indicatorVector = Vec3.multiply(upVector, INDICATOR_OFFSET_UP);
|
||||
position = Vec3.sum(this.currentProperties.position, indicatorVector);
|
||||
|
||||
var properties = {
|
||||
position: position,
|
||||
rotation: this.currentProperties.rotation
|
||||
}
|
||||
|
||||
Entities.editEntity(this.colorIndicator, properties);
|
||||
},
|
||||
|
||||
|
||||
playSoundAtCurrentPosition: function(isRemoveCubeSound) {
|
||||
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
var audioProperties = {
|
||||
volume: 0.25,
|
||||
position: position
|
||||
};
|
||||
|
||||
if (isRemoveCubeSound === true) {
|
||||
Audio.playSound(this.REMOVE_CUBE_SOUND, audioProperties);
|
||||
} else {
|
||||
Audio.playSound(this.COMBINE_COLORS_SOUND, audioProperties);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
return new ColorBusterWand();
|
||||
});
|
130
examples/example/games/color_busters/createColorBusterCubes.js
Normal file
130
examples/example/games/color_busters/createColorBusterCubes.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// createColorBusterCubes.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates cubes that can be removed with a Color Buster wand.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var DELETE_AT_ENDING = false;
|
||||
|
||||
var CUBE_DIMENSIONS = {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
};
|
||||
|
||||
var NUMBER_OF_CUBES_PER_SIDE = 8;
|
||||
|
||||
var STARTING_CORNER_POSITION = {
|
||||
x: 100,
|
||||
y: 100,
|
||||
z: 100
|
||||
};
|
||||
var STARTING_COLORS = [
|
||||
['red', {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
}],
|
||||
['yellow', {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
}],
|
||||
['blue', {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
}],
|
||||
['orange', {
|
||||
red: 255,
|
||||
green: 165,
|
||||
blue: 0
|
||||
}],
|
||||
['violet', {
|
||||
red: 128,
|
||||
green: 0,
|
||||
blue: 128
|
||||
}],
|
||||
['green', {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
}]
|
||||
];
|
||||
|
||||
function chooseStartingColor() {
|
||||
var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)];
|
||||
return startingColor;
|
||||
}
|
||||
|
||||
var cubes = [];
|
||||
|
||||
function createColorBusterCube(row, column, vertical) {
|
||||
|
||||
print('make cube at ' + row + ':' + column + ":" + vertical);
|
||||
|
||||
var position = {
|
||||
x: STARTING_CORNER_POSITION.x + row,
|
||||
y: STARTING_CORNER_POSITION.y + vertical,
|
||||
z: STARTING_CORNER_POSITION.z + column
|
||||
};
|
||||
|
||||
var startingColor = chooseStartingColor();
|
||||
var colorBusterCubeProperties = {
|
||||
name: 'Hifi-ColorBusterCube',
|
||||
type: 'Box',
|
||||
dimensions: CUBE_DIMENSIONS,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: false,
|
||||
color: startingColor[1],
|
||||
position: position,
|
||||
userData: JSON.stringify({
|
||||
hifiColorBusterCubeKey: {
|
||||
originalColorName: startingColor[0]
|
||||
},
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
})
|
||||
};
|
||||
var cube = Entities.addEntity(colorBusterCubeProperties);
|
||||
cubes.push(cube);
|
||||
return cube
|
||||
}
|
||||
|
||||
function createBoard() {
|
||||
var vertical;
|
||||
var row;
|
||||
var column;
|
||||
for (vertical = 0; vertical < NUMBER_OF_CUBES_PER_SIDE; vertical++) {
|
||||
print('vertical:' + vertical)
|
||||
//create a single layer
|
||||
for (row = 0; row < NUMBER_OF_CUBES_PER_SIDE; row++) {
|
||||
print('row:' + row)
|
||||
for (column = 0; column < NUMBER_OF_CUBES_PER_SIDE; column++) {
|
||||
print('column:' + column)
|
||||
createColorBusterCube(row, column, vertical)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCubes() {
|
||||
while (cubes.length > 0) {
|
||||
Entities.deleteEntity(cubes.pop());
|
||||
}
|
||||
}
|
||||
|
||||
if (DELETE_AT_ENDING === true) {
|
||||
Script.scriptEnding.connect(deleteCubes);
|
||||
|
||||
}
|
||||
|
||||
createBoard();
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// createColorBusterWand.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates a wand that can be used to remove color buster blocks. Touch your wand to someone else's to combine colors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var DELETE_AT_ENDING = false;
|
||||
|
||||
var COLOR_WAND_MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand.fbx';
|
||||
var COLOR_WAND_COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand_collision_hull.obj';
|
||||
var COLOR_WAND_SCRIPT_URL = Script.resolvePath('colorBusterWand.js');
|
||||
|
||||
var COLOR_WAND_DIMENSIONS = {
|
||||
x: 0.04,
|
||||
y: 0.87,
|
||||
z: 0.04
|
||||
};
|
||||
|
||||
var COLOR_WAND_START_POSITION = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var STARTING_COLORS = [
|
||||
['red', {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
}],
|
||||
['yellow', {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
}],
|
||||
['blue', {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
}]
|
||||
];
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
|
||||
function chooseStartingColor() {
|
||||
var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)];
|
||||
return startingColor
|
||||
}
|
||||
|
||||
var wand;
|
||||
|
||||
function createColorBusterWand() {
|
||||
var startingColor = chooseStartingColor();
|
||||
var colorBusterWandProperties = {
|
||||
name: 'Hifi-ColorBusterWand',
|
||||
type: 'Model',
|
||||
modelURL: COLOR_WAND_MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLOR_WAND_COLLISION_HULL_URL,
|
||||
dimensions: COLOR_WAND_DIMENSIONS,
|
||||
position: center,
|
||||
script: COLOR_WAND_SCRIPT_URL,
|
||||
collisionsWillMove: true,
|
||||
userData: JSON.stringify({
|
||||
hifiColorBusterWandKey: {
|
||||
owner: MyAvatar.sessionUUID,
|
||||
currentColor: startingColor[0],
|
||||
originalColorName: startingColor[0],
|
||||
colorLocked: false
|
||||
},
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: false
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
wand = Entities.addEntity(colorBusterWandProperties);
|
||||
}
|
||||
|
||||
function deleteWand() {
|
||||
Entities.deleteEntity(wand);
|
||||
}
|
||||
|
||||
if (DELETE_AT_ENDING === true) {
|
||||
Script.scriptEnding.connect(deleteWand);
|
||||
}
|
||||
|
||||
createColorBusterWand();
|
|
@ -59,9 +59,7 @@ function controller(side) {
|
|||
this.triggerHeld = false;
|
||||
this.triggerThreshold = 0.9;
|
||||
this.side = side;
|
||||
this.palm = 2 * side;
|
||||
this.tip = 2 * side + 1;
|
||||
this.trigger = side;
|
||||
this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT;
|
||||
this.originalGravity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -150,8 +148,8 @@ function controller(side) {
|
|||
|
||||
|
||||
this.updateControllerState = function() {
|
||||
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
|
||||
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
|
||||
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include('../utilities/tools/vector.js');
|
||||
Script.include('../../utilities/tools/vector.js');
|
||||
|
||||
var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/";
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ var inHand = false;
|
|||
|
||||
function isControllerActive() {
|
||||
// I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both.
|
||||
controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0;
|
||||
controllerActive = (Vec3.length(MyAvatar.leftHandPose.translation) > 0) || Vec3.length(MyAvatar.rightHandPose.translation) > 0;
|
||||
return controllerActive;
|
||||
}
|
||||
|
||||
|
@ -312,10 +312,10 @@ function grabSword(hand) {
|
|||
}
|
||||
var handRotation;
|
||||
if (hand === "right") {
|
||||
handRotation = MyAvatar.getRightPalmRotation();
|
||||
handRotation = MyAvatar.rightHandPose.rotation;
|
||||
|
||||
} else if (hand === "left") {
|
||||
handRotation = MyAvatar.getLeftPalmRotation();
|
||||
handRotation = MyAvatar.leftHandPose.rotation;
|
||||
}
|
||||
var swordRotation = Entities.getEntityProperties(swordID).rotation;
|
||||
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation);
|
||||
|
|
26
examples/example/lineExample.js
Normal file
26
examples/example/lineExample.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// lineExample.js
|
||||
// examples/example
|
||||
//
|
||||
// Created by Ryan Huffman on October 27, 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../libraries/line.js");
|
||||
|
||||
var basePosition = MyAvatar.position;
|
||||
var color = { red: 128, green: 220, blue: 190 };
|
||||
var strokeWidth = 0.01;
|
||||
var line = new InfiniteLine(basePosition, color, 20);
|
||||
|
||||
for (var i = 0; i < (16 * Math.PI); i += 0.05) {
|
||||
var x = 0
|
||||
var y = 0.25 * Math.sin(i);
|
||||
var z = i / 10;
|
||||
|
||||
var position = Vec3.sum(basePosition, { x: x, y: y, z: z });
|
||||
line.enqueuePoint(position, strokeWidth);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue