Merge branch 'master' of https://github.com/highfidelity/hifi into received-message

This commit is contained in:
Ryan Huffman 2015-11-18 16:52:23 -08:00
commit 2a416ebc61
733 changed files with 40834 additions and 20287 deletions
.gitattributesBUILD.mdBUILD_ANDROID.mdBUILD_OSX.mdBUILD_WIN.mdCMakeLists.txt
assignment-client
cmake
domain-server
examples

27
.gitattributes vendored Normal file
View 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

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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 ()

View file

@ -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()

View file

@ -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);
});
}

View file

@ -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;

View file

@ -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) {

View file

@ -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();

View file

@ -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;
}

View file

@ -46,6 +46,7 @@ private:
Assignment _requestAssignment;
QPointer<ThreadedAssignment> _currentAssignment;
bool _isAssigned { false };
QString _assignmentServerHostname;
HifiSockAddr _assignmentServerSocket;
QTimer _requestTimer; // timer for requesting and assignment

View file

@ -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

View file

@ -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>();

View file

@ -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;
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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();

View file

@ -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

View file

@ -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;
}

View 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.
}

View 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

View file

@ -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;
}

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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 ""

View file

@ -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

View 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()

View file

@ -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")

View file

@ -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)

View file

@ -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 ()

View file

@ -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()

View file

@ -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")

View file

@ -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?

View file

@ -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
)

View 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()

View 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()

View 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()

View 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()

View 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()

View file

@ -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)

View file

@ -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)

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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@")

View file

@ -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",

View file

@ -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})

View file

@ -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"})})();

View file

@ -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())) {

View file

@ -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);
});
}
}

View file

@ -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();

View file

@ -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) {

View file

@ -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();

View file

@ -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();

View file

@ -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);

View 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);

View 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);
});

View 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
View 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();
}
}
});

View file

@ -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

View file

@ -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);

View file

@ -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;

View 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);

View 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

View 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);

View file

@ -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);

View file

@ -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];

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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();
}

View file

@ -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);
}

View 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);
});

View file

@ -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");

View 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);

View 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);

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@
//
(function() {
Script.include("../toys/breakdanceCore.js");
Script.include("../breakdanceCore.js");
Script.include("../libraries/utils.js");
var _this;

View 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)

View 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",
});

View 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();
});

View 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();
});

View 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);

View 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();
});

View 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);

View file

@ -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);

View 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);

View 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();
});

View 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();

View file

@ -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();

View file

@ -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);
}

View file

@ -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/";

View file

@ -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);

View 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