mirror of
https://github.com/lubosz/overte.git
synced 2025-08-29 09:06:46 +02:00
Merge branch 'master' into tony/anim-expressions
This commit is contained in:
commit
2b0ed55077
484 changed files with 11856 additions and 7996 deletions
29
BUILD_WIN.md
29
BUILD_WIN.md
|
@ -1,5 +1,7 @@
|
|||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file.
|
||||
|
||||
Interface can be built as 32 or 64 bit.
|
||||
|
||||
###Visual Studio 2013
|
||||
|
||||
You can use the Community or Professional editions of Visual Studio 2013.
|
||||
|
@ -25,25 +27,26 @@ We expect nmake.exe to be located at the following path.
|
|||
###Qt
|
||||
You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version.
|
||||
|
||||
NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of libraries for interface.exe to run. The 32-bit version of the static library is the one linked by our CMake find modules.
|
||||
|
||||
* [Download the online installer](http://qt-project.org/downloads)
|
||||
* When it asks you to select components, ONLY select the following:
|
||||
* When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference:
|
||||
* Qt > Qt 5.5.1 > **msvc2013 32-bit**
|
||||
* Qt > Qt 5.5.1 > **msvc2013 64-bit**
|
||||
|
||||
* [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)
|
||||
* Download the offline installer, 32- or 64-bit to match your build preference:
|
||||
* [32-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
|
||||
* [64-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013_64-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.5.1\msvc2013\lib\cmake` directory.
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` or `Qt\5.5.1\msvc2013_64\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.
|
||||
All libraries should be 32- or 64-bit to match your build preference.
|
||||
|
||||
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.
|
||||
We use CMake's `fixup_bundle` to find the DLLs all of our executable 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.
|
||||
|
||||
The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure:
|
||||
|
||||
|
@ -69,17 +72,23 @@ Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll
|
|||
QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb
|
||||
QSslSocket: cannot resolve SSL_get0_next_proto_negotiated
|
||||
|
||||
To prevent these problems, install OpenSSL yourself. Download the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Visual C++ 2008 Redistributables
|
||||
* Win32 OpenSSL v1.0.1p
|
||||
To prevent these problems, install OpenSSL yourself. Download one of the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Win32 OpenSSL v1.0.1q
|
||||
* Win64 OpenSSL v1.0.1q
|
||||
|
||||
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.
|
||||
|
||||
###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.
|
||||
|
||||
For 32-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12"
|
||||
|
||||
For 64-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12 Win64"
|
||||
|
||||
Open %HIFI_DIR%\build\hifi.sln and compile.
|
||||
|
||||
###Running Interface
|
||||
|
|
|
@ -36,7 +36,12 @@ if (WIN32)
|
|||
if (MSVC10)
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
elseif (MSVC12)
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86 ")
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(WINDOW_SDK_FOLDER "x64")
|
||||
else()
|
||||
set(WINDOW_SDK_FOLDER "x86")
|
||||
endif()
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}")
|
||||
endif ()
|
||||
message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
|
@ -221,3 +226,36 @@ if (HIFI_MEMORY_DEBUGGING)
|
|||
MESSAGE("-- Memory debugging is enabled")
|
||||
endif (UNIX)
|
||||
endif ()
|
||||
|
||||
include_application_version()
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
message(STATUS "+++++ Package for deployment will be generated on this build +++++")
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment)
|
||||
|
||||
set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico")
|
||||
set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico")
|
||||
string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE})
|
||||
string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER})
|
||||
|
||||
set(CPACK_PACKAGE_NAME "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack")
|
||||
set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}")
|
||||
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||
set(CPACK_PACKAGE_EXECUTABLES
|
||||
stack-manager "Stack Manager"
|
||||
interface "Interface"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./")
|
||||
endif (WIN32)
|
||||
|
||||
include(CPack)
|
||||
endif ()
|
||||
|
|
|
@ -11,3 +11,4 @@ link_hifi_libraries(
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AssetClient.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
#include <AssetClient.h>
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
#include "entities/AssignmentParentFinder.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "AbstractAudioInterface.h"
|
||||
|
||||
|
@ -43,8 +45,8 @@
|
|||
|
||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
|
||||
|
||||
Agent::Agent(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
Agent::Agent(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_entityEditSender(),
|
||||
_receivedAudioStream(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES,
|
||||
InboundAudioStream::Settings(0, false, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, false,
|
||||
|
@ -61,6 +63,8 @@ Agent::Agent(NLPacket& packet) :
|
|||
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
assetThread->start();
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
@ -79,46 +83,48 @@ Agent::Agent(NLPacket& packet) :
|
|||
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
|
||||
}
|
||||
|
||||
void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
auto packetType = packet->getType();
|
||||
void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
auto packetType = message->getType();
|
||||
|
||||
if (packetType == PacketType::OctreeStats) {
|
||||
|
||||
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(packet, senderNode);
|
||||
if (packet->getPayloadSize() > statsMessageLength) {
|
||||
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(message, senderNode);
|
||||
if (message->getSize() > statsMessageLength) {
|
||||
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
||||
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
|
||||
|
||||
int piggyBackedSizeWithHeader = message->getSize() - statsMessageLength;
|
||||
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
||||
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
|
||||
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggyBackedSizeWithHeader);
|
||||
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr());
|
||||
packet = QSharedPointer<NLPacket>(newPacket.release());
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr());
|
||||
message = QSharedPointer<ReceivedMessage>::create(*newPacket);
|
||||
} else {
|
||||
return; // bail since no piggyback data
|
||||
}
|
||||
|
||||
packetType = packet->getType();
|
||||
packetType = message->getType();
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) {
|
||||
_entityViewer.processDatagram(*packet, senderNode);
|
||||
if (packetType == PacketType::EntityData) {
|
||||
_entityViewer.processDatagram(*message, senderNode);
|
||||
} else if (packetType == PacketType::EntityErase) {
|
||||
_entityViewer.processEraseMessage(*message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
NodeType_t nodeType;
|
||||
packet->peekPrimitive(&nodeType);
|
||||
message->peekPrimitive(&nodeType);
|
||||
|
||||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(packet, senderNode);
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
|
||||
_receivedAudioStream.parseData(*packet);
|
||||
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
_receivedAudioStream.parseData(*message);
|
||||
|
||||
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
|
||||
|
||||
|
@ -131,6 +137,7 @@ void Agent::run() {
|
|||
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
|
||||
|
||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||
|
@ -223,6 +230,7 @@ void Agent::executeScript() {
|
|||
// call model URL setters with empty URLs so our avatar, if user, will have the default models
|
||||
scriptedAvatar->setFaceModelURL(QUrl());
|
||||
scriptedAvatar->setSkeletonModelURL(QUrl());
|
||||
|
||||
// give this AvatarData object to the script engine
|
||||
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
|
||||
|
||||
|
@ -279,6 +287,8 @@ void Agent::executeScript() {
|
|||
|
||||
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
|
||||
|
||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||
|
||||
// wire up our additional agent related processing to the update signal
|
||||
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
|
||||
|
||||
|
@ -379,7 +389,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > SCRIPT_AUDIO_BUFFER_BYTES
|
||||
? SCRIPT_AUDIO_BUFFER_BYTES
|
||||
: soundByteArray.size() - _numAvatarSoundSentBytes;
|
||||
numAvailableSamples = numAvailableBytes / sizeof(int16_t);
|
||||
numAvailableSamples = (int16_t)numAvailableBytes / sizeof(int16_t);
|
||||
|
||||
|
||||
// check if the all of the _numAvatarAudioBufferSamples to be sent are silence
|
||||
|
|
|
@ -39,7 +39,7 @@ class Agent : public ThreadedAssignment {
|
|||
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
|
||||
|
||||
public:
|
||||
Agent(NLPacket& packet);
|
||||
Agent(ReceivedMessage& message);
|
||||
|
||||
void setIsAvatar(bool isAvatar);
|
||||
bool isAvatar() const { return _isAvatar; }
|
||||
|
@ -63,9 +63,10 @@ private slots:
|
|||
void scriptRequestFinished();
|
||||
void executeScript();
|
||||
|
||||
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
||||
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void processAgentAvatarAndAudio(float deltaTime);
|
||||
|
||||
private:
|
||||
|
|
|
@ -225,11 +225,11 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet) {
|
||||
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
qDebug() << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
||||
|
||||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(*packet);
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
||||
|
||||
if (_currentAssignment && !_isAssigned) {
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||
|
@ -239,7 +239,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> pac
|
|||
|
||||
// switch our DomainHandler hostname and port to whoever sent us the assignment
|
||||
|
||||
nodeList->getDomainHandler().setSockAddr(packet->getSenderSockAddr(), _assignmentServerHostname);
|
||||
nodeList->getDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname);
|
||||
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
|
@ -274,8 +274,8 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> pac
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::handleStopNodePacket(QSharedPointer<NLPacket> packet) {
|
||||
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
|
||||
void AssignmentClient::handleStopNodePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
|
||||
|
||||
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
|
||||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
|
||||
|
|
|
@ -38,8 +38,8 @@ public slots:
|
|||
void aboutToQuit();
|
||||
|
||||
private slots:
|
||||
void handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet);
|
||||
void handleStopNodePacket(QSharedPointer<NLPacket> packet);
|
||||
void handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleStopNodePacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
private:
|
||||
void setUpStatusToMonitor();
|
||||
|
|
|
@ -207,14 +207,14 @@ void AssignmentClientMonitor::checkSpares() {
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> packet) {
|
||||
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// read out the sender ID
|
||||
QUuid senderID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
QUuid senderID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
SharedNodePointer matchingNode = nodeList->nodeWithUUID(senderID);
|
||||
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
|
||||
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
|
||||
|
||||
AssignmentClientChildData* childData = nullptr;
|
||||
|
||||
|
@ -251,7 +251,7 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> p
|
|||
|
||||
// get child's assignment type out of the packet
|
||||
quint8 assignmentType;
|
||||
packet->readPrimitive(&assignmentType);
|
||||
message->readPrimitive(&assignmentType);
|
||||
|
||||
childData->setChildType((Assignment::Type) assignmentType);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
private slots:
|
||||
void checkSpares();
|
||||
void childProcessFinished();
|
||||
void handleChildStatusPacket(QSharedPointer<NLPacket> packet);
|
||||
void handleChildStatusPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
public slots:
|
||||
void aboutToQuit();
|
||||
|
|
|
@ -19,26 +19,26 @@
|
|||
#include "assets/AssetServer.h"
|
||||
#include "messages/MessagesMixer.h"
|
||||
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
|
||||
|
||||
quint8 packedType;
|
||||
packet.peekPrimitive(&packedType);
|
||||
message.peekPrimitive(&packedType);
|
||||
|
||||
Assignment::Type unpackedType = (Assignment::Type) packedType;
|
||||
|
||||
switch (unpackedType) {
|
||||
case Assignment::AudioMixerType:
|
||||
return new AudioMixer(packet);
|
||||
return new AudioMixer(message);
|
||||
case Assignment::AvatarMixerType:
|
||||
return new AvatarMixer(packet);
|
||||
return new AvatarMixer(message);
|
||||
case Assignment::AgentType:
|
||||
return new Agent(packet);
|
||||
return new Agent(message);
|
||||
case Assignment::EntityServerType:
|
||||
return new EntityServer(packet);
|
||||
return new EntityServer(message);
|
||||
case Assignment::AssetServerType:
|
||||
return new AssetServer(packet);
|
||||
return new AssetServer(message);
|
||||
case Assignment::MessagesMixerType:
|
||||
return new MessagesMixer(packet);
|
||||
return new MessagesMixer(message);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class AssignmentFactory {
|
||||
public:
|
||||
static ThreadedAssignment* unpackAssignment(NLPacket& packet);
|
||||
static ThreadedAssignment* unpackAssignment(ReceivedMessage& message);
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentFactory_h
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
|
||||
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
|
||||
|
||||
AssetServer::AssetServer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
AssetServer::AssetServer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_taskPool(this)
|
||||
{
|
||||
|
||||
|
@ -40,7 +40,7 @@ AssetServer::AssetServer(NLPacket& packet) :
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
|
||||
packetReceiver.registerMessageListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
}
|
||||
|
||||
void AssetServer::run() {
|
||||
|
@ -84,20 +84,20 @@ void AssetServer::run() {
|
|||
}
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void AssetServer::handleAssetGetInfo(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
QByteArray assetHash;
|
||||
MessageID messageID;
|
||||
uint8_t extensionLength;
|
||||
|
||||
if (packet->getPayloadSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
|
||||
if (message->getSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
|
||||
qDebug() << "ERROR bad file request";
|
||||
return;
|
||||
}
|
||||
|
||||
packet->readPrimitive(&messageID);
|
||||
assetHash = packet->readWithoutCopy(SHA256_HASH_LENGTH);
|
||||
packet->readPrimitive(&extensionLength);
|
||||
QByteArray extension = packet->read(extensionLength);
|
||||
message->readPrimitive(&messageID);
|
||||
assetHash = message->readWithoutCopy(SHA256_HASH_LENGTH);
|
||||
message->readPrimitive(&extensionLength);
|
||||
QByteArray extension = message->read(extensionLength);
|
||||
|
||||
auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply);
|
||||
|
||||
|
@ -122,26 +122,26 @@ void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNode
|
|||
nodeList->sendPacket(std::move(replyPacket), *senderNode);
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void AssetServer::handleAssetGet(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
||||
auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + sizeof(DataOffset) + sizeof(DataOffset));
|
||||
|
||||
if (packet->getPayloadSize() < minSize) {
|
||||
if (message->getSize() < minSize) {
|
||||
qDebug() << "ERROR bad file request";
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue task
|
||||
auto task = new SendAssetTask(packet, senderNode, _resourcesDirectory);
|
||||
auto task = new SendAssetTask(message, senderNode, _resourcesDirectory);
|
||||
_taskPool.start(task);
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
void AssetServer::handleAssetUpload(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
||||
if (senderNode->getCanRez()) {
|
||||
qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
|
||||
|
||||
auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
|
||||
auto task = new UploadAssetTask(message, senderNode, _resourcesDirectory);
|
||||
_taskPool.start(task);
|
||||
} else {
|
||||
// this is a node the domain told us is not allowed to rez entities
|
||||
|
@ -151,7 +151,7 @@ void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, Sha
|
|||
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
|
||||
|
||||
MessageID messageID;
|
||||
packetList->readPrimitive(&messageID);
|
||||
message->readPrimitive(&messageID);
|
||||
|
||||
// write the message ID and a permission denied error
|
||||
permissionErrorPacket->writePrimitive(messageID);
|
||||
|
|
|
@ -18,19 +18,20 @@
|
|||
#include <QThreadPool>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
#include "ReceivedMessage.h"
|
||||
|
||||
class AssetServer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetServer(NLPacket& packet);
|
||||
AssetServer(ReceivedMessage& message);
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
|
||||
private slots:
|
||||
void handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleAssetGetInfo(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
|
||||
void handleAssetGet(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
|
||||
void handleAssetUpload(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer senderNode);
|
||||
|
||||
void sendStatsPacket();
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
#include "AssetUtils.h"
|
||||
|
||||
SendAssetTask::SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
|
||||
SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
|
||||
QRunnable(),
|
||||
_packet(packet),
|
||||
_message(message),
|
||||
_senderNode(sendToNode),
|
||||
_resourcesDir(resourcesDir)
|
||||
{
|
||||
|
@ -36,16 +36,16 @@ void SendAssetTask::run() {
|
|||
uint8_t extensionLength;
|
||||
DataOffset start, end;
|
||||
|
||||
_packet->readPrimitive(&messageID);
|
||||
QByteArray assetHash = _packet->read(SHA256_HASH_LENGTH);
|
||||
_packet->readPrimitive(&extensionLength);
|
||||
QByteArray extension = _packet->read(extensionLength);
|
||||
_message->readPrimitive(&messageID);
|
||||
QByteArray assetHash = _message->read(SHA256_HASH_LENGTH);
|
||||
_message->readPrimitive(&extensionLength);
|
||||
QByteArray extension = _message->read(extensionLength);
|
||||
|
||||
// `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`.
|
||||
// `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data,
|
||||
// starting at index 1.
|
||||
_packet->readPrimitive(&start);
|
||||
_packet->readPrimitive(&end);
|
||||
_message->readPrimitive(&start);
|
||||
_message->readPrimitive(&end);
|
||||
|
||||
QString hexHash = assetHash.toHex();
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ class NLPacket;
|
|||
|
||||
class SendAssetTask : public QRunnable {
|
||||
public:
|
||||
SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
|
||||
SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
QSharedPointer<NLPacket> _packet;
|
||||
QSharedPointer<ReceivedMessage> _message;
|
||||
SharedNodePointer _senderNode;
|
||||
QDir _resourcesDir;
|
||||
};
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include <NLPacketList.h>
|
||||
|
||||
|
||||
UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode,
|
||||
UploadAssetTask::UploadAssetTask(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode,
|
||||
const QDir& resourcesDir) :
|
||||
_packetList(packetList),
|
||||
_receivedMessage(receivedMessage),
|
||||
_senderNode(senderNode),
|
||||
_resourcesDir(resourcesDir)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, Shared
|
|||
}
|
||||
|
||||
void UploadAssetTask::run() {
|
||||
auto data = _packetList->getMessage();
|
||||
auto data = _receivedMessage->getMessage();
|
||||
|
||||
QBuffer buffer { &data };
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
|
|
@ -19,17 +19,19 @@
|
|||
#include <QtCore/QRunnable>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include "ReceivedMessage.h"
|
||||
|
||||
class NLPacketList;
|
||||
class Node;
|
||||
|
||||
class UploadAssetTask : public QRunnable {
|
||||
public:
|
||||
UploadAssetTask(QSharedPointer<NLPacketList> packetList, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
|
||||
UploadAssetTask(QSharedPointer<ReceivedMessage> message, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
QSharedPointer<NLPacketList> _packetList;
|
||||
QSharedPointer<ReceivedMessage> _receivedMessage;
|
||||
QSharedPointer<Node> _senderNode;
|
||||
QDir _resourcesDir;
|
||||
};
|
||||
|
|
|
@ -75,8 +75,8 @@ bool AudioMixer::shouldMute(float quietestFrame) {
|
|||
return (quietestFrame > _noiseMutingThreshold);
|
||||
}
|
||||
|
||||
AudioMixer::AudioMixer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
AudioMixer::AudioMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_trailingSleepRatio(1.0f),
|
||||
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
||||
_performanceThrottlingRatio(0.0f),
|
||||
|
@ -438,7 +438,6 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
AudioMixerClientData* listenerNodeData = static_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||
|
||||
// zero out the client mix for this node
|
||||
memset(_preMixSamples, 0, sizeof(_preMixSamples));
|
||||
memset(_mixSamples, 0, sizeof(_mixSamples));
|
||||
|
||||
// loop through all other nodes that have sufficient audio to mix
|
||||
|
@ -459,6 +458,9 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
if (otherNodeStream->getType() == PositionalAudioStream::Microphone) {
|
||||
streamUUID = otherNode->getUUID();
|
||||
}
|
||||
|
||||
// clear out the pre-mix samples before filling it up with this source
|
||||
memset(_preMixSamples, 0, sizeof(_preMixSamples));
|
||||
|
||||
if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) {
|
||||
streamsMixed += addStreamToMixForListeningNodeWithStream(listenerNodeData, streamUUID,
|
||||
|
@ -542,17 +544,17 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixer::handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
DependencyManager::get<NodeList>()->updateNodeWithDataFromPacket(packet, sendingNode);
|
||||
void AudioMixer::handleNodeAudioPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
DependencyManager::get<NodeList>()->updateNodeWithDataFromPacket(message, sendingNode);
|
||||
}
|
||||
|
||||
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
if (sendingNode->getCanAdjustLocks()) {
|
||||
auto newPacket = NLPacket::create(PacketType::MuteEnvironment, packet->getPayloadSize());
|
||||
auto newPacket = NLPacket::create(PacketType::MuteEnvironment, message->getSize());
|
||||
// Copy payload
|
||||
newPacket->write(packet->getPayload(), packet->getPayloadSize());
|
||||
newPacket->write(message->getRawMessage(), message->getSize());
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
||||
|
|
|
@ -28,7 +28,7 @@ const int READ_DATAGRAMS_STATS_WINDOW_SECONDS = 30;
|
|||
class AudioMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioMixer(NLPacket& packet);
|
||||
AudioMixer(ReceivedMessage& message);
|
||||
|
||||
void deleteLater() { qDebug() << "DELETE LATER CALLED?"; QObject::deleteLater(); }
|
||||
public slots:
|
||||
|
@ -41,8 +41,8 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void broadcastMixes();
|
||||
void handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void handleNodeAudioPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
void domainSettingsRequestComplete();
|
||||
|
|
|
@ -49,18 +49,18 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int AudioMixerClientData::parseData(NLPacket& packet) {
|
||||
PacketType packetType = packet.getType();
|
||||
int AudioMixerClientData::parseData(ReceivedMessage& message) {
|
||||
PacketType packetType = message.getType();
|
||||
|
||||
if (packetType == PacketType::AudioStreamStats) {
|
||||
|
||||
// skip over header, appendFlag, and num stats packed
|
||||
packet.seek(sizeof(quint8) + sizeof(quint16));
|
||||
message.seek(sizeof(quint8) + sizeof(quint16));
|
||||
|
||||
// read the downstream audio stream stats
|
||||
packet.readPrimitive(&_downstreamAudioStreamStats);
|
||||
message.readPrimitive(&_downstreamAudioStreamStats);
|
||||
|
||||
return packet.pos();
|
||||
return message.getPosition();
|
||||
|
||||
} else {
|
||||
PositionalAudioStream* matchingStream = NULL;
|
||||
|
@ -74,10 +74,10 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
|
|||
// we don't have a mic stream yet, so add it
|
||||
|
||||
// read the channel flag to see if our stream is stereo or not
|
||||
packet.seek(sizeof(quint16));
|
||||
message.seek(sizeof(quint16));
|
||||
|
||||
quint8 channelFlag;
|
||||
packet.readPrimitive(&channelFlag);
|
||||
message.readPrimitive(&channelFlag);
|
||||
|
||||
bool isStereo = channelFlag == 1;
|
||||
|
||||
|
@ -89,11 +89,11 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
|
|||
// this is injected audio
|
||||
|
||||
// grab the stream identifier for this injected audio
|
||||
packet.seek(sizeof(quint16));
|
||||
QUuid streamIdentifier = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
message.seek(sizeof(quint16));
|
||||
QUuid streamIdentifier = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
bool isStereo;
|
||||
packet.readPrimitive(&isStereo);
|
||||
message.readPrimitive(&isStereo);
|
||||
|
||||
if (!_audioStreams.contains(streamIdentifier)) {
|
||||
// we don't have this injected stream yet, so add it
|
||||
|
@ -105,9 +105,9 @@ int AudioMixerClientData::parseData(NLPacket& packet) {
|
|||
}
|
||||
|
||||
// seek to the beginning of the packet so that the next reader is in the right spot
|
||||
packet.seek(0);
|
||||
message.seek(0);
|
||||
|
||||
return matchingStream->parseData(packet);
|
||||
return matchingStream->parseData(message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer&
|
|||
statsPacket->writePrimitive(appendFlag);
|
||||
appendFlag = 1;
|
||||
|
||||
int numStreamStatsRoomFor = (statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||
int numStreamStatsRoomFor = (int)(statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||
|
||||
// calculate and pack the number of stream stats to follow
|
||||
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
const QHash<QUuid, PositionalAudioStream*>& getAudioStreams() const { return _audioStreams; }
|
||||
AvatarAudioStream* getAvatarAudioStream() const;
|
||||
|
||||
int parseData(NLPacket& packet);
|
||||
int parseData(ReceivedMessage& message);
|
||||
|
||||
void checkBuffersBeforeFrameSend();
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
|||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
|
||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
|
||||
|
||||
AvatarMixer::AvatarMixer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_broadcastThread(),
|
||||
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_trailingSleepRatio(1.0f),
|
||||
|
@ -157,7 +157,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
++_sumListeners;
|
||||
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
glm::vec3 myPosition = avatar.getPosition();
|
||||
glm::vec3 myPosition = avatar.getClientGlobalPosition();
|
||||
|
||||
// reset the internal state for correct random number distribution
|
||||
distribution.reset();
|
||||
|
@ -290,7 +290,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
||||
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||
glm::vec3 otherPosition = otherAvatar.getPosition();
|
||||
glm::vec3 otherPosition = otherAvatar.getClientGlobalPosition();
|
||||
float distanceToAvatar = glm::length(myPosition - otherPosition);
|
||||
|
||||
// potentially update the max full rate distance for this frame
|
||||
|
@ -424,19 +424,19 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void AvatarMixer::handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
|
||||
nodeList->updateNodeWithDataFromPacket(message, senderNode);
|
||||
}
|
||||
|
||||
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (senderNode->getLinkedData()) {
|
||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||
if (nodeData != nullptr) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
|
||||
// parse the identity packet and update the change timestamp if appropriate
|
||||
if (avatar.hasIdentityChangedAfterParsing(*packet)) {
|
||||
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
|
||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
@ -444,13 +444,13 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, Sh
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||
if (nodeData) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
|
||||
// parse the billboard packet and update the change timestamp if appropriate
|
||||
if (avatar.hasBillboardChangedAfterParsing(*packet)) {
|
||||
if (avatar.hasBillboardChangedAfterParsing(message->getMessage())) {
|
||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
@ -458,8 +458,8 @@ void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, S
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<NLPacket> packet) {
|
||||
DependencyManager::get<NodeList>()->processKillNode(*packet);
|
||||
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
DependencyManager::get<NodeList>()->processKillNode(*message);
|
||||
}
|
||||
|
||||
void AvatarMixer::sendStatsPacket() {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
class AvatarMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AvatarMixer(NLPacket& packet);
|
||||
AvatarMixer(ReceivedMessage& message);
|
||||
~AvatarMixer();
|
||||
public slots:
|
||||
/// runs the avatar mixer
|
||||
|
@ -32,10 +32,10 @@ public slots:
|
|||
void sendStatsPacket();
|
||||
|
||||
private slots:
|
||||
void handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<NLPacket> packet);
|
||||
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void domainSettingsRequestComplete();
|
||||
|
||||
private:
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
|
||||
#include "AvatarMixerClientData.h"
|
||||
|
||||
int AvatarMixerClientData::parseData(NLPacket& packet) {
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||
// pull the sequence number from the data first
|
||||
packet.readPrimitive(&_lastReceivedSequenceNumber);
|
||||
|
||||
message.readPrimitive(&_lastReceivedSequenceNumber);
|
||||
|
||||
// compute the offset to the data payload
|
||||
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
|
||||
return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
|
||||
}
|
||||
|
||||
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) {
|
||||
|
@ -40,7 +40,7 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
|
|||
}
|
||||
|
||||
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||
jsonObject["display_name"] = _avatar.getDisplayName();
|
||||
jsonObject["display_name"] = _avatar->getDisplayName();
|
||||
jsonObject["full_rate_distance"] = _fullRateDistance;
|
||||
jsonObject["max_av_distance"] = _maxAvatarDistance;
|
||||
jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame;
|
||||
|
@ -49,7 +49,7 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
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[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||
|
||||
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
|
||||
jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate();
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
|||
class AvatarMixerClientData : public NodeData {
|
||||
Q_OBJECT
|
||||
public:
|
||||
int parseData(NLPacket& packet);
|
||||
AvatarData& getAvatar() { return _avatar; }
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
AvatarData& getAvatar() { return *_avatar; }
|
||||
|
||||
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
private:
|
||||
AvatarData _avatar;
|
||||
AvatarSharedPointer _avatar { new AvatarData() };
|
||||
|
||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||
|
|
19
assignment-client/src/entities/AssignmentParentFinder.cpp
Normal file
19
assignment-client/src/entities/AssignmentParentFinder.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// AssignmentParentFinder.cpp
|
||||
// assignment-client/src/entities
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-22
|
||||
// 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 "AssignmentParentFinder.h"
|
||||
|
||||
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID) const {
|
||||
SpatiallyNestableWeakPointer parent;
|
||||
// search entities
|
||||
parent = _tree->findEntityByEntityItemID(parentID);
|
||||
return parent;
|
||||
}
|
34
assignment-client/src/entities/AssignmentParentFinder.h
Normal file
34
assignment-client/src/entities/AssignmentParentFinder.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AssignmentParentFinder.h
|
||||
// interface/src/entities
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-21
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentParentFinder_h
|
||||
#define hifi_AssignmentParentFinder_h
|
||||
|
||||
#include <memory>
|
||||
#include <QUuid>
|
||||
|
||||
#include <EntityTree.h>
|
||||
#include <SpatialParentFinder.h>
|
||||
|
||||
// This interface is used to turn a QUuid into a pointer to a "parent" -- something that children can
|
||||
// be spatially relative to. At this point, this means either an EntityItem or an Avatar.
|
||||
|
||||
class AssignmentParentFinder : public SpatialParentFinder {
|
||||
public:
|
||||
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
|
||||
virtual ~AssignmentParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
|
||||
|
||||
protected:
|
||||
EntityTreePointer _tree;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentParentFinder_h
|
|
@ -16,13 +16,14 @@
|
|||
#include "EntityServer.h"
|
||||
#include "EntityServerConsts.h"
|
||||
#include "EntityNodeData.h"
|
||||
#include "AssignmentParentFinder.h"
|
||||
|
||||
const char* MODEL_SERVER_NAME = "Entity";
|
||||
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
||||
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
|
||||
|
||||
EntityServer::EntityServer(NLPacket& packet) :
|
||||
OctreeServer(packet),
|
||||
EntityServer::EntityServer(ReceivedMessage& message) :
|
||||
OctreeServer(message),
|
||||
_entitySimulation(NULL)
|
||||
{
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
@ -40,9 +41,9 @@ EntityServer::~EntityServer() {
|
|||
tree->removeNewlyCreatedHook(this);
|
||||
}
|
||||
|
||||
void EntityServer::handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void EntityServer::handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->queueReceivedPacket(packet, senderNode);
|
||||
_octreeInboundPacketProcessor->queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +61,10 @@ OctreePointer EntityServer::createTree() {
|
|||
tree->setSimulation(simpleSimulation);
|
||||
_entitySimulation = simpleSimulation;
|
||||
}
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
DependencyManager::set<AssignmentParentFinder>(tree);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct ViewerSendingStats {
|
|||
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityServer(NLPacket& packet);
|
||||
EntityServer(ReceivedMessage& message);
|
||||
~EntityServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
|
@ -50,10 +50,10 @@ public:
|
|||
|
||||
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) override;
|
||||
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override;
|
||||
virtual QString serverSubclassStats();
|
||||
virtual QString serverSubclassStats() override;
|
||||
|
||||
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode);
|
||||
virtual void trackViewerGone(const QUuid& viewerNode);
|
||||
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) override;
|
||||
virtual void trackViewerGone(const QUuid& viewerNode) override;
|
||||
|
||||
public slots:
|
||||
void pruneDeletedEntities();
|
||||
|
@ -62,7 +62,7 @@ protected:
|
|||
virtual OctreePointer createTree() override;
|
||||
|
||||
private slots:
|
||||
void handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
EntitySimulation* _entitySimulation;
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
|
||||
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
||||
|
||||
MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet)
|
||||
MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message)
|
||||
{
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
}
|
||||
|
||||
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
|
@ -35,10 +35,10 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode) {
|
||||
QString channel, message;
|
||||
QUuid senderID;
|
||||
MessagesClient::decodeMessagesPacket(packetList, channel, message, senderID);
|
||||
MessagesClient::decodeMessagesPacket(receivedMessage, channel, message, senderID);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -53,13 +53,13 @@ void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, Shar
|
|||
});
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
QString channel = QString::fromUtf8(packetList->getMessage());
|
||||
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
_channelSubscribers[channel] << senderNode->getUUID();
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
QString channel = QString::fromUtf8(packetList->getMessage());
|
||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
if (_channelSubscribers.contains(channel)) {
|
||||
_channelSubscribers[channel].remove(senderNode->getUUID());
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
class MessagesMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessagesMixer(NLPacket& packet);
|
||||
MessagesMixer(ReceivedMessage& message);
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
|
@ -29,9 +29,9 @@ public slots:
|
|||
void sendStatsPacket();
|
||||
|
||||
private slots:
|
||||
void handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
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;
|
||||
|
|
|
@ -75,7 +75,7 @@ void OctreeInboundPacketProcessor::midProcess() {
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
if (_shuttingDown) {
|
||||
qDebug() << "OctreeInboundPacketProcessor::processPacket() while shutting down... ignoring incoming packet";
|
||||
return;
|
||||
|
@ -85,22 +85,22 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
|
||||
if (debugProcessPacket) {
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() payload=%p payloadLength=%lld",
|
||||
packet->getPayload(),
|
||||
packet->getPayloadSize());
|
||||
message->getRawMessage(),
|
||||
message->getSize());
|
||||
}
|
||||
|
||||
// Ask our tree subclass if it can handle the incoming packet...
|
||||
PacketType packetType = packet->getType();
|
||||
PacketType packetType = message->getType();
|
||||
|
||||
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
|
||||
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
|
||||
_receivedPacketCount++;
|
||||
|
||||
unsigned short int sequence;
|
||||
packet->readPrimitive(&sequence);
|
||||
message->readPrimitive(&sequence);
|
||||
|
||||
quint64 sentAt;
|
||||
packet->readPrimitive(&sentAt);
|
||||
message->readPrimitive(&sentAt);
|
||||
|
||||
quint64 arrivedAt = usecTimestampNow();
|
||||
if (sentAt > arrivedAt) {
|
||||
|
@ -118,7 +118,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
|
||||
if (debugProcessPacket || _myServer->wantsDebugReceiving()) {
|
||||
qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount << " command from client";
|
||||
qDebug() << " receivedBytes=" << packet->getDataSize();
|
||||
qDebug() << " receivedBytes=" << message->getSize();
|
||||
qDebug() << " sequence=" << sequence;
|
||||
qDebug() << " sentAt=" << sentAt << " usecs";
|
||||
qDebug() << " arrivedAt=" << arrivedAt << " usecs";
|
||||
|
@ -132,29 +132,29 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType);
|
||||
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
|
||||
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
|
||||
qDebug() << " atByte (in payload)=" << packet->pos();
|
||||
qDebug() << " payload size=" << packet->getPayloadSize();
|
||||
qDebug() << " atByte (in payload)=" << message->getPosition();
|
||||
qDebug() << " payload size=" << message->getSize();
|
||||
|
||||
if (!packet->bytesLeftToRead()) {
|
||||
if (!message->getBytesLeftToRead()) {
|
||||
qDebug() << " ----- UNEXPECTED ---- got a packet without any edit details!!!! --------";
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char* editData = nullptr;
|
||||
|
||||
while (packet->bytesLeftToRead() > 0) {
|
||||
while (message->getBytesLeftToRead() > 0) {
|
||||
|
||||
editData = reinterpret_cast<const unsigned char*>(packet->getPayload() + packet->pos());
|
||||
editData = reinterpret_cast<const unsigned char*>(message->getRawMessage() + message->getPosition());
|
||||
|
||||
int maxSize = packet->bytesLeftToRead();
|
||||
int maxSize = message->getBytesLeftToRead();
|
||||
|
||||
if (debugProcessPacket) {
|
||||
qDebug() << " --- inside while loop ---";
|
||||
qDebug() << " maxSize=" << maxSize;
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
|
||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
|
||||
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
|
||||
packet->pos(), maxSize);
|
||||
packetType, message->getRawMessage(), message->getSize(), editData,
|
||||
message->getPosition(), maxSize);
|
||||
}
|
||||
|
||||
quint64 startProcess, startLock = usecTimestampNow();
|
||||
|
@ -162,7 +162,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
_myServer->getOctree()->withWriteLock([&] {
|
||||
startProcess = usecTimestampNow();
|
||||
editDataBytesRead =
|
||||
_myServer->getOctree()->processEditPacketData(*packet, editData, maxSize, sendingNode);
|
||||
_myServer->getOctree()->processEditPacketData(*message, editData, maxSize, sendingNode);
|
||||
});
|
||||
quint64 endProcess = usecTimestampNow();
|
||||
|
||||
|
@ -178,12 +178,12 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
lockWaitTime += thisLockWaitTime;
|
||||
|
||||
// skip to next edit record in the packet
|
||||
packet->seek(packet->pos() + editDataBytesRead);
|
||||
message->seek(message->getPosition() + editDataBytesRead);
|
||||
|
||||
if (debugProcessPacket) {
|
||||
qDebug() << " editDataBytesRead=" << editDataBytesRead;
|
||||
qDebug() << " AFTER processEditPacketData payload position=" << packet->pos();
|
||||
qDebug() << " AFTER processEditPacketData payload size=" << packet->getPayloadSize();
|
||||
qDebug() << " AFTER processEditPacketData payload position=" << message->getPosition();
|
||||
qDebug() << " AFTER processEditPacketData payload size=" << message->getSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
if (debugProcessPacket) {
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
|
||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
|
||||
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
|
||||
packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition());
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
|
@ -293,7 +293,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
|
||||
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
|
||||
|
||||
packetsSent += nackPacketList->getNumPackets();
|
||||
packetsSent += (int)nackPacketList->getNumPackets();
|
||||
|
||||
// send the list of nack packets
|
||||
totalBytesSent += nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
virtual unsigned long getMaxWait() const;
|
||||
virtual void preProcess();
|
||||
|
|
|
@ -33,8 +33,6 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_lastTimeBagEmpty(0),
|
||||
_viewFrustumChanging(false),
|
||||
_viewFrustumJustStoppedChanging(true),
|
||||
_currentPacketIsColor(true),
|
||||
_currentPacketIsCompressed(false),
|
||||
_octreeSendThread(NULL),
|
||||
_lastClientBoundaryLevelAdjust(0),
|
||||
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
|
@ -179,15 +177,9 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
_currentPacketIsColor = getWantColor();
|
||||
_currentPacketIsCompressed = getWantCompression();
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
if (_currentPacketIsColor) {
|
||||
setAtBit(flags, PACKET_IS_COLOR_BIT);
|
||||
}
|
||||
if (_currentPacketIsCompressed) {
|
||||
setAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||
}
|
||||
setAtBit(flags, PACKET_IS_COLOR_BIT); // always color
|
||||
setAtBit(flags, PACKET_IS_COMPRESSED_BIT); // always compressed
|
||||
|
||||
_octreePacket->reset();
|
||||
|
||||
|
@ -212,10 +204,9 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
|
|||
|
||||
// compressed packets include lead bytes which contain compressed size, this allows packing of
|
||||
// multiple compressed portions together
|
||||
if (_currentPacketIsCompressed) {
|
||||
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes;
|
||||
_octreePacket->writePrimitive(sectionSize);
|
||||
}
|
||||
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes;
|
||||
_octreePacket->writePrimitive(sectionSize);
|
||||
|
||||
if (bytes <= _octreePacket->bytesAvailableForWrite()) {
|
||||
_octreePacket->write(reinterpret_cast<const char*>(buffer), bytes);
|
||||
_octreePacketWaiting = true;
|
||||
|
@ -234,6 +225,8 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
|
|||
newestViewFrustum.setPosition(getCameraPosition());
|
||||
newestViewFrustum.setOrientation(getCameraOrientation());
|
||||
|
||||
newestViewFrustum.setKeyholeRadius(getKeyholeRadius());
|
||||
|
||||
// Also make sure it's got the correct lens details from the camera
|
||||
float originalFOV = getCameraFov();
|
||||
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
||||
|
@ -371,11 +364,11 @@ const NLPacket* OctreeQueryNode::getNextNackedPacket() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void OctreeQueryNode::parseNackPacket(NLPacket& packet) {
|
||||
void OctreeQueryNode::parseNackPacket(ReceivedMessage& message) {
|
||||
// read sequence numbers
|
||||
while (packet.bytesLeftToRead()) {
|
||||
while (message.getBytesLeftToRead()) {
|
||||
OCTREE_PACKET_SEQUENCE sequenceNumber;
|
||||
packet.readPrimitive(&sequenceNumber);
|
||||
message.readPrimitive(&sequenceNumber);
|
||||
_nackedSequenceNumbers.enqueue(sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <CoverageMap.h>
|
||||
#include <NodeData.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <OctreeElementBag.h>
|
||||
|
@ -55,7 +54,6 @@ public:
|
|||
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
||||
|
||||
OctreeElementBag elementBag;
|
||||
CoverageMap map;
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
|
||||
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
|
||||
|
@ -77,12 +75,6 @@ public:
|
|||
quint64 getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
|
||||
void setLastTimeBagEmpty() { _lastTimeBagEmpty = _sceneSendStartTime; }
|
||||
|
||||
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
|
||||
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
|
||||
bool getCurrentPacketFormatMatches() {
|
||||
return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression());
|
||||
}
|
||||
|
||||
bool hasLodChanged() const { return _lodChanged; }
|
||||
|
||||
OctreeSceneStats stats;
|
||||
|
@ -108,7 +100,7 @@ public:
|
|||
|
||||
OCTREE_PACKET_SEQUENCE getSequenceNumber() const { return _sequenceNumber; }
|
||||
|
||||
void parseNackPacket(NLPacket& packet);
|
||||
void parseNackPacket(ReceivedMessage& message);
|
||||
bool hasNextNackedPacket() const;
|
||||
const NLPacket* getNextNackedPacket();
|
||||
|
||||
|
@ -135,8 +127,6 @@ private:
|
|||
quint64 _lastTimeBagEmpty;
|
||||
bool _viewFrustumChanging;
|
||||
bool _viewFrustumJustStoppedChanging;
|
||||
bool _currentPacketIsColor;
|
||||
bool _currentPacketIsCompressed;
|
||||
|
||||
OctreeSendThread* _octreeSendThread;
|
||||
|
||||
|
|
|
@ -309,37 +309,29 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging())
|
||||
bool isFullScene = ((!viewFrustumChanged) && nodeData->getViewFrustumJustStoppedChanging())
|
||||
|| nodeData->hasLodChanged();
|
||||
|
||||
bool somethingToSend = true; // assume we have something
|
||||
|
||||
// FOR NOW... node tells us if it wants to receive only view frustum deltas
|
||||
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
|
||||
|
||||
// If our packet already has content in it, then we must use the color choice of the waiting packet.
|
||||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = nodeData->getWantColor();
|
||||
bool wantCompression = nodeData->getWantCompression();
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (!nodeData->getCurrentPacketFormatMatches()) {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
} else {
|
||||
nodeData->resetOctreePacket();
|
||||
}
|
||||
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
|
||||
if (wantCompression) {
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
}
|
||||
_packetData.changeSettings(wantCompression, targetSize);
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
} else {
|
||||
nodeData->resetOctreePacket();
|
||||
}
|
||||
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
|
||||
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
_packetData.changeSettings(true, targetSize); // FIXME - eventually support only compressed packets
|
||||
|
||||
const ViewFrustum* lastViewFrustum = viewFrustumChanged ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
|
||||
// If the current view frustum has changed OR we have nothing to send, then search against
|
||||
// the current view frustum for things to send.
|
||||
|
@ -350,12 +342,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
|
||||
nodeData->dumpOutOfView();
|
||||
}
|
||||
nodeData->map.erase();
|
||||
}
|
||||
|
||||
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||
// only set our last sent time if we weren't resetting due to frustum change
|
||||
nodeData->setLastTimeBagEmpty();
|
||||
}
|
||||
|
||||
// track completed scenes and send out the stats packet accordingly
|
||||
|
@ -430,39 +416,15 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
return;
|
||||
}
|
||||
|
||||
/* TODO: Looking for a way to prevent locking and encoding a tree that is not
|
||||
// going to result in any packets being sent...
|
||||
//
|
||||
// If our node is root, and the root hasn't changed, and our view hasn't changed,
|
||||
// and we've already seen at least one duplicate packet, then we probably don't need
|
||||
// to lock the tree and encode, because the result should be that no bytes will be
|
||||
// encoded, and this will be a duplicate packet from the last one we sent...
|
||||
OctreeElementPointer root = _myServer->getOctree()->getRoot();
|
||||
bool skipEncode = false;
|
||||
if (
|
||||
(subTree == root)
|
||||
&& (nodeData->getLastRootTimestamp() == root->getLastChanged())
|
||||
&& !viewFrustumChanged
|
||||
&& (nodeData->getDuplicatePacketCount() > 0)
|
||||
) {
|
||||
qDebug() << "is root, root not changed, view not changed, already seen a duplicate!"
|
||||
<< "Can we skip it?";
|
||||
skipEncode = true;
|
||||
}
|
||||
*/
|
||||
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
|
||||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient +
|
||||
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, octreeSizeScale,
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(),
|
||||
WANT_EXISTS_BITS, DONT_CHOP, viewFrustumChanged, lastViewFrustum,
|
||||
boundaryLevelAdjust, octreeSizeScale,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
|
||||
&nodeData->extraEncodeData);
|
||||
|
@ -488,20 +450,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// the packet and send it
|
||||
completedScene = nodeData->elementBag.isEmpty();
|
||||
|
||||
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
|
||||
if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) {
|
||||
if (_packetData.hasContent() && bytesWritten == 0 &&
|
||||
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
} else {
|
||||
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
|
||||
// content or not... because in this case even if we were unable to pack any data, we want to drop
|
||||
// below to our sendNow logic, but we do want to track that we attempted to pack extra
|
||||
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
extraPackingAttempts++;
|
||||
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
}
|
||||
|
||||
nodeData->stats.encodeStopped();
|
||||
|
@ -527,15 +478,13 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
|
||||
// form actually inflated beyond our padding, and in this case we will send the current packet, then
|
||||
// write to out new packet...
|
||||
unsigned int writtenSize = _packetData.getFinalizedSize()
|
||||
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0);
|
||||
unsigned int writtenSize = _packetData.getFinalizedSize() + sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
|
||||
if (writtenSize > nodeData->getAvailable()) {
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
}
|
||||
|
||||
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
|
||||
extraPackingAttempts = 0;
|
||||
quint64 compressAndWriteEnd = usecTimestampNow();
|
||||
compressAndWriteElapsedUsec = (float)(compressAndWriteEnd - compressAndWriteStart);
|
||||
}
|
||||
|
@ -544,9 +493,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// the packet doesn't have enough space to bother attempting to pack more...
|
||||
bool sendNow = true;
|
||||
|
||||
if (nodeData->getCurrentPacketIsCompressed() &&
|
||||
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
|
||||
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
|
||||
if (!completedScene && (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
|
||||
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS)) {
|
||||
sendNow = false; // try to pack more
|
||||
}
|
||||
|
||||
|
@ -557,9 +505,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
quint64 packetSendingEnd = usecTimestampNow();
|
||||
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
|
||||
|
||||
if (wantCompression) {
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
}
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
extraPackingAttempts = 0;
|
||||
} else {
|
||||
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
|
||||
// but we've finalized the _packetData, so we want to start a new section, we will do that by
|
||||
|
@ -569,7 +516,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// a larger compressed size then uncompressed size
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
|
||||
}
|
||||
_packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset
|
||||
_packetData.changeSettings(true, targetSize); // will do reset - NOTE: Always compressed
|
||||
|
||||
}
|
||||
OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec);
|
||||
|
@ -634,7 +581,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
if (nodeData->elementBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
} // end if bag wasn't empty, and so we sent stuff...
|
||||
|
|
|
@ -210,8 +210,8 @@ void OctreeServer::trackProcessWaitTime(float time) {
|
|||
_averageProcessWaitTime.updateAverage(time);
|
||||
}
|
||||
|
||||
OctreeServer::OctreeServer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
OctreeServer::OctreeServer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_argc(0),
|
||||
_argv(NULL),
|
||||
_parsedArgV(NULL),
|
||||
|
@ -878,12 +878,12 @@ void OctreeServer::parsePayload() {
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (!_isFinished) {
|
||||
// If we got a query packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
|
||||
nodeList->updateNodeWithDataFromPacket(message, senderNode);
|
||||
|
||||
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
|
@ -892,17 +892,17 @@ void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, Shar
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
// If we got a nack packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
|
||||
if (nodeData) {
|
||||
nodeData->parseNackPacket(*packet);
|
||||
nodeData->parseNackPacket(*message);
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
_jurisdictionSender->queueReceivedPacket(packet, senderNode);
|
||||
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
_jurisdictionSender->queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
|
||||
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {
|
||||
|
|
|
@ -34,7 +34,7 @@ const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per secon
|
|||
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeServer(NLPacket& packet);
|
||||
OctreeServer(ReceivedMessage& message);
|
||||
~OctreeServer();
|
||||
|
||||
/// allows setting of run arguments
|
||||
|
@ -135,9 +135,9 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void domainSettingsRequestComplete();
|
||||
void handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
protected:
|
||||
virtual OctreePointer createTree() = 0;
|
||||
|
|
11
cmake/externals/LibOVR/CMakeLists.txt
vendored
11
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -15,17 +15,16 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
# 0.6 public
|
||||
# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
|
||||
# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
|
||||
# 0.7 alpha
|
||||
# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip
|
||||
# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8
|
||||
|
||||
# 0.8 public
|
||||
# URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
|
||||
# URL_MD5 54944b03b95149d6010f84eb701b9647
|
||||
|
||||
if (WIN32)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
|
||||
URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
|
||||
URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
|
||||
URL_MD5 54944b03b95149d6010f84eb701b9647
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
30
cmake/externals/bullet/CMakeLists.txt
vendored
30
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ if (WIN32)
|
|||
set(PLATFORM_CMAKE_ARGS "-DUSE_MSVC_RUNTIME_LIBRARY_DLL=1")
|
||||
else ()
|
||||
set(PLATFORM_CMAKE_ARGS "-DBUILD_SHARED_LIBS=1")
|
||||
|
||||
|
||||
if (ANDROID)
|
||||
list(APPEND PLATFORM_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
elseif (APPLE)
|
||||
|
@ -14,30 +14,28 @@ endif ()
|
|||
|
||||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
# URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.zip
|
||||
URL_MD5 d95b07eb120de7dd7786361c0b5a8d9f
|
||||
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
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
)
|
||||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
#URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz
|
||||
URL_MD5 fb140a4983b4109aa1c825a162aa8d64
|
||||
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
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
|
@ -55,7 +53,7 @@ if (APPLE OR UNIX OR ANDROID)
|
|||
else ()
|
||||
set(BULLET_LIB_EXT "so")
|
||||
endif ()
|
||||
|
||||
|
||||
set(LIB_PREFIX "lib")
|
||||
elseif (WIN32)
|
||||
set(BULLET_LIB_EXT "lib")
|
||||
|
@ -63,13 +61,13 @@ endif ()
|
|||
|
||||
if (DEFINED BULLET_LIB_EXT)
|
||||
set(_BULLET_LIB_PAIRS "DYNAMICS_LIBRARY\;BulletDynamics" "COLLISION_LIBRARY\;BulletCollision" "MATH_LIBRARY\;LinearMath" "SOFTBODY_LIBRARY\;BulletSoftBody")
|
||||
|
||||
|
||||
foreach(_LIB_PAIR ${_BULLET_LIB_PAIRS})
|
||||
list(GET _LIB_PAIR 0 _LIB_VAR_NAME)
|
||||
list(GET _LIB_PAIR 1 _LIB_NAME)
|
||||
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_RELEASE ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} release library location")
|
||||
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_DEBUG ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}_Debug.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} debug library location")
|
||||
else ()
|
||||
|
|
31
cmake/externals/gverb/CMakeLists.txt
vendored
31
cmake/externals/gverb/CMakeLists.txt
vendored
|
@ -1,31 +0,0 @@
|
|||
set(EXTERNAL_NAME gverb)
|
||||
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
endif ()
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/gverb-master.zip
|
||||
URL_MD5 8b16d586390a2102804e46b87820dfc6
|
||||
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
|
||||
)
|
||||
|
||||
# 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 gverb include directory")
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/gverb.lib CACHE FILEPATH "List of gverb libraries")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libgverb.a CACHE FILEPATH "List of gverb libraries")
|
||||
endif ()
|
5
cmake/externals/openvr/CMakeLists.txt
vendored
5
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,9 +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 http://hifi-public.s3.amazonaws.com/dependencies/openvr-0.9.1.zip
|
||||
URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.12.zip
|
||||
URL_MD5 c08dced68ce4e341e1467e6814ae419d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
8
cmake/externals/sdl2/CMakeLists.txt
vendored
8
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -63,7 +63,11 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
|
||||
if (APPLE)
|
||||
|
||||
# NOOP
|
||||
|
||||
elseif (WIN32)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
||||
|
||||
|
@ -75,8 +79,12 @@ elseif (WIN32)
|
|||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
||||
endif()
|
||||
|
||||
add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH})
|
||||
|
||||
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 ()
|
||||
|
|
12
cmake/externals/sixense/CMakeLists.txt
vendored
12
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -8,14 +8,14 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
#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_URL "http://hifi-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 ${SIXENSE_URL}
|
||||
|
|
27
cmake/macros/ConsolidateStackComponents.cmake
Normal file
27
cmake/macros/ConsolidateStackComponents.cmake
Normal file
|
@ -0,0 +1,27 @@
|
|||
macro(CONSOLIDATE_STACK_COMPONENTS)
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
|
||||
# Copy all the output for this target into the common deployment location
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
)
|
||||
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
elseif (TARGET_NAME STREQUAL "stack-manager")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endmacro()
|
|
@ -10,10 +10,15 @@
|
|||
#
|
||||
|
||||
macro(INCLUDE_APPLICATION_VERSION)
|
||||
#
|
||||
# We are relying on Jenkins defined environment variables to determine the origin of this build
|
||||
# and will only package if this is a PR or Release build
|
||||
if (DEFINED ENV{JOB_ID})
|
||||
set (DEPLOY_PACKAGE 1)
|
||||
set (BUILD_SEQ $ENV{JOB_ID})
|
||||
elseif (DEFINED ENV{ghprbPullId})
|
||||
set (BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}")
|
||||
set (DEPLOY_PACKAGE 1)
|
||||
set (BUILD_SEQ "PR-$ENV{ghprbPullId}")
|
||||
else ()
|
||||
set(BUILD_SEQ "dev")
|
||||
endif ()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# CopyDllsBesideWindowsExecutable.cmake
|
||||
# PackageLibrariesForDeployment.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
|
@ -15,4 +15,4 @@ macro(TARGET_BULLET)
|
|||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -88,4 +88,4 @@ find_package_handle_standard_args(Bullet "Could NOT find Bullet, try to set the
|
|||
BULLET_INCLUDE_DIRS
|
||||
BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY BULLET_SOFTBODY_LIBRARY
|
||||
BULLET_LIBRARIES
|
||||
)
|
||||
)
|
||||
|
|
|
@ -18,10 +18,16 @@ hifi_library_search_hints("leapmotion")
|
|||
find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_library(LEAPMOTION_LIBRARY_DEBUG Leapd PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
find_path(LEAPMOTION_DLL_PATH Leap.dll PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
else()
|
||||
set(ARCH_DIR "x86")
|
||||
endif()
|
||||
|
||||
find_library(LEAPMOTION_LIBRARY_DEBUG Leapd PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_path(LEAPMOTION_DLL_PATH Leap.dll PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
elseif (APPLE)
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
|
|
@ -107,7 +107,7 @@ if (WIN32 AND NOT CYGWIN)
|
|||
select_library_configurations(SSL_EAY)
|
||||
|
||||
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
|
||||
|
||||
|
||||
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
|
||||
elseif (MINGW)
|
||||
|
@ -252,6 +252,15 @@ endif ()
|
|||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${OPENSSL_DLL_PATH})
|
||||
#
|
||||
# For some reason fixup misses the following DLL and only copies libeay32. There's gotta be a better way to handle this
|
||||
# but for now resorting to the following interm solution
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/full-stack-deployment/
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS)
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# Try to find the RSSDK library
|
||||
#
|
||||
# You must provide a RSSDK_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# RSSDK_FOUND - system found RSSDK
|
||||
# RSSDK_INCLUDE_DIRS - the RSSDK include directory
|
||||
# RSSDK_LIBRARIES - Link this to use RSSDK
|
||||
#
|
||||
# Created on 12/7/2014 by Thijs Wenker
|
||||
# Copyright (c) 2014 High Fidelity
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("rssdk")
|
||||
|
||||
find_path(RSSDK_INCLUDE_DIRS pxcbase.h PATH_SUFFIXES include HINTS ${RSSDK_SEARCH_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_library(RSSDK_LIBRARY_DEBUG libpxc_d PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
|
||||
find_library(RSSDK_LIBRARY_RELEASE libpxc PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(RSSDK)
|
||||
|
||||
set(RSSDK_LIBRARIES "${RSSDK_LIBRARY}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RSSDK DEFAULT_MSG RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES)
|
||||
|
||||
mark_as_advanced(RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES RSSDK_SEARCH_DIRS)
|
|
@ -1,30 +0,0 @@
|
|||
#
|
||||
# FindRtMidi.cmake
|
||||
#
|
||||
# Try to find the RtMidi library
|
||||
#
|
||||
# You can provide a RTMIDI_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# RTMIDI_FOUND - system found RtMidi
|
||||
# RTMIDI_INCLUDE_DIRS - the RtMidi include directory
|
||||
# RTMIDI_LIBRARIES - link to this to use RtMidi
|
||||
#
|
||||
# Created on 6/30/2014 by Stephen Birarda
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("rtmidi")
|
||||
|
||||
find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RtMidi DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES)
|
||||
|
||||
mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES RTMIDI_SEARCH_DIRS)
|
|
@ -53,4 +53,4 @@ else()
|
|||
endif()
|
||||
|
||||
file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
|
||||
|
|
|
@ -38,3 +38,4 @@ endif (UNIX)
|
|||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
|
@ -51,15 +51,15 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
|||
<< NodeType::AssetServer
|
||||
<< NodeType::MessagesMixer;
|
||||
|
||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
if (packet->getPayloadSize() == 0) {
|
||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (message->getSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream packetStream(packet.data());
|
||||
QDataStream packetStream(message->getMessage());
|
||||
|
||||
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
|
||||
NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr());
|
||||
NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr());
|
||||
|
||||
if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) {
|
||||
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
|
||||
|
@ -72,7 +72,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
|||
|
||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
|
||||
<< nodeConnection.senderSockAddr;
|
||||
<< nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -87,11 +87,11 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
|||
QString username;
|
||||
QByteArray usernameSignature;
|
||||
|
||||
if (packet->bytesLeftToRead() > 0) {
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read username from packet
|
||||
packetStream >> username;
|
||||
|
||||
if (packet->bytesLeftToRead() > 0) {
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read user signature from packet
|
||||
packetStream >> usernameSignature;
|
||||
}
|
||||
|
@ -103,14 +103,14 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
|||
if (node) {
|
||||
// set the sending sock addr and node interest set on this node
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
nodeData->setSendingSockAddr(packet->getSenderSockAddr());
|
||||
nodeData->setSendingSockAddr(message->getSenderSockAddr());
|
||||
nodeData->setNodeInterestSet(nodeConnection.interestList.toSet());
|
||||
|
||||
// signal that we just connected a node so the DomainServer can get it a list
|
||||
// and broadcast its presence right away
|
||||
emit connectedNode(node);
|
||||
} else {
|
||||
qDebug() << "Refusing connection from node at" << packet->getSenderSockAddr();
|
||||
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -572,10 +572,10 @@ void DomainGatekeeper::handlePeerPingTimeout() {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket> packet) {
|
||||
void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// loop through the packet and pull out network peers
|
||||
// any peer we don't have we add to the hash, otherwise we update
|
||||
QDataStream iceResponseStream(packet.data());
|
||||
QDataStream iceResponseStream(message->getMessage());
|
||||
|
||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||
iceResponseStream >> *receivedPeer;
|
||||
|
@ -600,15 +600,15 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<NLPacket>
|
|||
}
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPingPacket(QSharedPointer<NLPacket> packet) {
|
||||
void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
|
||||
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
|
||||
QDataStream packetStream(packet.data());
|
||||
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
QDataStream packetStream(message->getMessage());
|
||||
|
||||
QUuid nodeUUID;
|
||||
packetStream >> nodeUUID;
|
||||
|
@ -617,6 +617,6 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<NLPacket> packet
|
|||
|
||||
if (sendingPeer) {
|
||||
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
||||
sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr());
|
||||
sendingPeer->activateMatchingOrNewSymmetricSocket(message->getSenderSockAddr());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,10 +41,10 @@ public:
|
|||
|
||||
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
|
||||
public slots:
|
||||
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPeerInformationPacket(QSharedPointer<NLPacket> packet);
|
||||
void processConnectRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processICEPingPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processICEPeerInformationPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
||||
|
||||
|
|
|
@ -273,7 +273,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
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
|
||||
|
@ -578,10 +578,10 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
||||
QDataStream packetStream(packet.data());
|
||||
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, packet->getSenderSockAddr(), false);
|
||||
QDataStream packetStream(message->getMessage());
|
||||
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);
|
||||
|
||||
// update this node's sockets in case they have changed
|
||||
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
|
||||
|
@ -591,7 +591,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, Sha
|
|||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||
nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet());
|
||||
|
||||
sendDomainListToNode(sendingNode, packet->getSenderSockAddr());
|
||||
sendDomainListToNode(sendingNode, message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
unsigned int DomainServer::countConnectedUsers() {
|
||||
|
@ -764,9 +764,9 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
|||
);
|
||||
}
|
||||
|
||||
void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packet) {
|
||||
void DomainServer::processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// construct the requested assignment from the packet data
|
||||
Assignment requestAssignment(*packet);
|
||||
Assignment requestAssignment(*message);
|
||||
|
||||
// Suppress these for Assignment::AgentType to once per 5 seconds
|
||||
static QElapsedTimer noisyMessageTimer;
|
||||
|
@ -784,14 +784,14 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
|
|||
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
|
||||
("Received a request for assignment type [^ ]+ from [^ ]+");
|
||||
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
|
||||
<< "from" << packet->getSenderSockAddr();
|
||||
<< "from" << message->getSenderSockAddr();
|
||||
noisyMessageTimer.restart();
|
||||
}
|
||||
|
||||
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||
|
||||
if (assignmentToDeploy) {
|
||||
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << packet->getSenderSockAddr();
|
||||
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << message->getSenderSockAddr();
|
||||
|
||||
// give this assignment out, either the type matches or the requestor said they will take any
|
||||
static std::unique_ptr<NLPacket> assignmentPacket;
|
||||
|
@ -812,7 +812,7 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
|
|||
assignmentStream << uniqueAssignment;
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
|
||||
limitedNodeList->sendUnreliablePacket(*assignmentPacket, message->getSenderSockAddr());
|
||||
|
||||
// give the information for that deployed assignment to the gatekeeper so it knows to that that node
|
||||
// in when it comes back around
|
||||
|
@ -824,7 +824,7 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packe
|
|||
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
|
||||
("Unable to fulfill assignment request of type [^ ]+ from [^ ]+");
|
||||
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
|
||||
<< "from" << packet->getSenderSockAddr();
|
||||
<< "from" << message->getSenderSockAddr();
|
||||
noisyMessageTimer.restart();
|
||||
}
|
||||
}
|
||||
|
@ -993,7 +993,7 @@ void DomainServer::sendHeartbeatToIceServer() {
|
|||
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
|
||||
}
|
||||
|
||||
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode) {
|
||||
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode) {
|
||||
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||
if (nodeData) {
|
||||
nodeData->updateJSONStats(packetList->getMessage());
|
||||
|
@ -1767,17 +1767,17 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
|
||||
void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
// this is a query for the viewpoint resulting from a path
|
||||
// first pull the query path from the packet
|
||||
|
||||
// figure out how many bytes the sender said this path is
|
||||
quint16 numPathBytes;
|
||||
packet->readPrimitive(&numPathBytes);
|
||||
message->readPrimitive(&numPathBytes);
|
||||
|
||||
if (numPathBytes <= packet->bytesLeftToRead()) {
|
||||
if (numPathBytes <= message->getBytesLeftToRead()) {
|
||||
// the number of path bytes makes sense for the sent packet - pull out the path
|
||||
QString pathQuery = QString::fromUtf8(packet->getPayload() + packet->pos(), numPathBytes);
|
||||
QString pathQuery = QString::fromUtf8(message->getRawMessage() + message->getPosition(), numPathBytes);
|
||||
|
||||
// our settings contain paths that start with a leading slash, so make sure this query has that
|
||||
if (!pathQuery.startsWith("/")) {
|
||||
|
@ -1825,7 +1825,7 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
|
|||
|
||||
// send off the packet - see if we can associate this outbound data to a particular node
|
||||
// TODO: does this senderSockAddr always work for a punched DS client?
|
||||
nodeList->sendPacket(std::move(pathResponsePacket), packet->getSenderSockAddr());
|
||||
nodeList->sendPacket(std::move(pathResponsePacket), message->getSenderSockAddr());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1837,11 +1837,11 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
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 = packet->getSourceID();
|
||||
const QUuid& nodeUUID = message->getSourceID();
|
||||
|
||||
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;
|
||||
|
||||
|
|
|
@ -56,11 +56,11 @@ public slots:
|
|||
|
||||
void restart();
|
||||
|
||||
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
|
||||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode);
|
||||
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
||||
void processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
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();
|
||||
|
|
|
@ -67,9 +67,9 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
|||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
Assignment::Type type;
|
||||
packet->readPrimitive(&type);
|
||||
message->readPrimitive(&type);
|
||||
|
||||
QJsonObject responseObject = responseObjectForType(QString::number(type));
|
||||
auto json = QJsonDocument(responseObject).toJson();
|
||||
|
@ -79,7 +79,7 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NL
|
|||
packetList->write(json);
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->sendPacketList(std::move(packetList), packet->getSenderSockAddr());
|
||||
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPManager.h>
|
||||
|
||||
#include <NLPacket.h>
|
||||
#include <ReceivedMessage.h>
|
||||
|
||||
const QString SETTINGS_PATHS_KEY = "paths";
|
||||
|
||||
|
@ -42,7 +42,7 @@ public:
|
|||
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||
|
||||
private slots:
|
||||
void processSettingsRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
private:
|
||||
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
// 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},
|
||||
|
@ -31,7 +30,6 @@ function playAwayAnimation() {
|
|||
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
|
||||
}
|
||||
|
@ -47,15 +45,14 @@ function stopAwayAnimation() {
|
|||
// 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.
|
||||
// The anim graph will trigger awayOutroOnDone when awayOutro is finished.
|
||||
var backToNormal = false;
|
||||
stopper = Script.setTimeout(function () {
|
||||
backToNormal = true;
|
||||
stopper = false;
|
||||
}, IK_WINDOW_AFTER_GOING_ACTIVE);
|
||||
stopper = true;
|
||||
function animateActive(state) {
|
||||
if (state.ikOverlayAlpha) {
|
||||
if (state.awayOutroOnDone) {
|
||||
backToNormal = true;
|
||||
stopper = false;
|
||||
} else 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);
|
||||
|
@ -63,7 +60,7 @@ function stopAwayAnimation() {
|
|||
// 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']);
|
||||
activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['ikOverlayAlpha', 'awayOutroOnDone']);
|
||||
}
|
||||
|
||||
// OVERLAY
|
||||
|
|
105
examples/baseball/assets.json
Normal file
105
examples/baseball/assets.json
Normal file
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"assets": {
|
||||
"crowd-boos.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-boos.wav",
|
||||
"atp_url": "atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"crowd-cheers-organ.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-cheers-organ.wav",
|
||||
"atp_url": "atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"crowd-medium.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/crowd-medium.wav",
|
||||
"atp_url": "atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"baseball-hitting-bat-1.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-1.wav",
|
||||
"atp_url": "atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: https://www.freesound.org/people/SocializedArtist45/"
|
||||
},
|
||||
"baseball-hitting-bat-set-1.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-1.wav",
|
||||
"atp_url": "atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
|
||||
},
|
||||
"baseball-hitting-bat-set-2.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-2.wav",
|
||||
"atp_url": "atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
|
||||
},
|
||||
"baseball-hitting-bat-set-3.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-3.wav",
|
||||
"atp_url": "atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
|
||||
},
|
||||
"baseball-hitting-bat-set-4.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/baseball-hitting-bat-set-4.wav",
|
||||
"atp_url": "atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: CGEffex - https://www.freesound.org/people/CGEffex/"
|
||||
},
|
||||
"chatter-loop.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/chatter-loop.wav",
|
||||
"atp_url": "atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"zorba-organ.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/zorba-organ.wav",
|
||||
"atp_url": "atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"charge-organ.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/charge-organ.wav",
|
||||
"atp_url": "atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"ball-game.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/ball-game.wav",
|
||||
"atp_url": "atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"clapping.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/clapping.wav",
|
||||
"atp_url": "atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: Alyssa Galindo - http://freesound.org/people/AshFox/"
|
||||
},
|
||||
"pop1.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop1.wav",
|
||||
"atp_url": "atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav"
|
||||
},
|
||||
"pop2.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop2.wav",
|
||||
"atp_url": "atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav"
|
||||
},
|
||||
"pop3.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop3.wav",
|
||||
"atp_url": "atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav"
|
||||
},
|
||||
"pop4.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/pop4.wav",
|
||||
"atp_url": "atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
|
||||
},
|
||||
"fire1.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire1.wav",
|
||||
"atp_url": "atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
|
||||
"attribution": "CC BY 3.0 - Credir: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
|
||||
},
|
||||
"fire2.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire2.wav",
|
||||
"atp_url": "atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
|
||||
},
|
||||
"fire3.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire3.wav",
|
||||
"atp_url": "atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
|
||||
},
|
||||
"fire4.wav": {
|
||||
"s3_url": "http://hifi-public.s3.amazonaws.com/birarda/baseball/fireworks/fire4.wav",
|
||||
"atp_url": "atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav",
|
||||
"attribution": "CC BY 3.0 - Credit: dcsimon - https://www.freesound.org/people/dcsimon/sounds/160720/"
|
||||
}
|
||||
}
|
||||
}
|
43
examples/baseball/baseballCrowd.js
Normal file
43
examples/baseball/baseballCrowd.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// baseballCrowd.js
|
||||
// examples/acScripts
|
||||
//
|
||||
// Created by Stephen Birarda on 10/20/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var chatter = SoundCache.getSound("atp:d9978e693035d4e2b5c7b546c8cccfb2dde5677834d9eed5206ccb2da55b4732.wav");
|
||||
var extras = [
|
||||
SoundCache.getSound("atp:1ee58f4d929fdef7c2989cd8be964952a24cdd653d80f57b6a89a2ae8e3029e1.wav"), // zorba
|
||||
SoundCache.getSound("atp:cefaba2d5f1a378382ee046716bffcf6b6a40676649b23a1e81a996efe22d7d3.wav"), // charge
|
||||
SoundCache.getSound("atp:fb41e37f8f8f7b78e546ac78800df6e39edaa09b2df4bfa0afdd8d749dac38b8.wav"), // take me out to the ball game
|
||||
SoundCache.getSound("atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav") // clapping
|
||||
];
|
||||
|
||||
var CHATTER_VOLUME = 0.20
|
||||
var EXTRA_VOLUME = 0.25
|
||||
|
||||
function playChatter() {
|
||||
if (chatter.downloaded && !chatter.isPlaying) {
|
||||
Audio.playSound(chatter, { loop: true, volume: CHATTER_VOLUME });
|
||||
}
|
||||
}
|
||||
|
||||
chatter.ready.connect(playChatter);
|
||||
|
||||
var currentInjector = null;
|
||||
|
||||
function playRandomExtras() {
|
||||
if ((!currentInjector || !currentInjector.isPlaying) && (Math.random() < (1.0 / 1800.0))) {
|
||||
// play a random extra sound about every 30s
|
||||
currentInjector = Audio.playSound(
|
||||
extras[Math.floor(Math.random() * extras.length)],
|
||||
{ volume: EXTRA_VOLUME }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(playRandomExtras);
|
43
examples/baseball/bat.js
Normal file
43
examples/baseball/bat.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// bat.js
|
||||
// examples/baseball/
|
||||
//
|
||||
// Created by Ryan Huffman on Nov 9, 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
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("pitching.js");
|
||||
|
||||
var pitchingMachine = null;
|
||||
|
||||
this.pitchAndHideAvatar = function() {
|
||||
if (!pitchingMachine) {
|
||||
pitchingMachine = getOrCreatePitchingMachine();
|
||||
Script.update.connect(function(dt) { pitchingMachine.update(dt); });
|
||||
}
|
||||
pitchingMachine.start();
|
||||
MyAvatar.shouldRenderLocally = false;
|
||||
};
|
||||
|
||||
this.startNearGrab = function() {
|
||||
// send the avatar to the baseball location so that they're ready to bat
|
||||
location = "/baseball"
|
||||
|
||||
this.pitchAndHideAvatar()
|
||||
};
|
||||
|
||||
this.continueNearGrab = function() {
|
||||
this.pitchAndHideAvatar()
|
||||
};
|
||||
|
||||
this.releaseGrab = function() {
|
||||
if (pitchingMachine) {
|
||||
pitchingMachine.stop();
|
||||
}
|
||||
MyAvatar.shouldRenderLocally = true;
|
||||
};
|
||||
});
|
76
examples/baseball/createBatButton.js
Normal file
76
examples/baseball/createBatButton.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// createBatButton.js
|
||||
// examples/baseball/moreBatsButton.js
|
||||
//
|
||||
// Created by Stephen Birarda on 10/28/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
|
||||
//
|
||||
|
||||
(function(){
|
||||
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
|
||||
if (!mouseEvent.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
this.dropBats();
|
||||
};
|
||||
this.startNearTrigger = function() {
|
||||
this.dropBats();
|
||||
};
|
||||
this.startFarTrigger = function() {
|
||||
this.dropBats();
|
||||
};
|
||||
this.dropBats = function() {
|
||||
// if the bat box is near us, grab it's position
|
||||
var nearby = Entities.findEntities(this.position, 20);
|
||||
|
||||
nearby.forEach(function(id) {
|
||||
var properties = Entities.getEntityProperties(id, ["name", "position"]);
|
||||
if (properties.name && properties.name == "Bat Box") {
|
||||
boxPosition = properties.position;
|
||||
}
|
||||
});
|
||||
|
||||
var BAT_DROP_HEIGHT = 2.0;
|
||||
|
||||
var dropPosition;
|
||||
|
||||
if (!boxPosition) {
|
||||
// we got no bat box position, drop in front of the avatar instead
|
||||
} else {
|
||||
// drop the bat above the bat box
|
||||
dropPosition = Vec3.sum(boxPosition, { x: 0.0, y: BAT_DROP_HEIGHT, z: 0.0});
|
||||
}
|
||||
|
||||
var BAT_MODEL = "atp:c47deaae09cca927f6bc9cca0e8bbe77fc618f8c3f2b49899406a63a59f885cb.fbx";
|
||||
var BAT_COLLISION_HULL = "atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj";
|
||||
var SCRIPT_URL = "http://rawgit.com/birarda/hifi/baseball/examples/baseball/bat.js"
|
||||
|
||||
var batUserData = {
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
leftRelativePosition: { x: 0.9, y: 0.05, z: -0.05 },
|
||||
rightRelativePosition: { x: 0.9, y: 0.05, z: 0.05 },
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 45)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the fresh bat at the drop position
|
||||
var bat = Entities.addEntity({
|
||||
name: 'Bat',
|
||||
type: "Model",
|
||||
modelURL: BAT_MODEL,
|
||||
position: dropPosition,
|
||||
compoundShapeURL: BAT_COLLISION_HULL,
|
||||
collisionsWillMove: true,
|
||||
velocity: { x: 0, y: 0.05, z: 0}, // workaround for gravity not taking effect on add
|
||||
gravity: { x: 0, y: -9.81, z: 0},
|
||||
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0),
|
||||
script: SCRIPT_URL,
|
||||
userData: JSON.stringify(batUserData)
|
||||
});
|
||||
};
|
||||
});
|
138
examples/baseball/firework.js
Normal file
138
examples/baseball/firework.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
//
|
||||
// firework.js
|
||||
// examples/baseball/
|
||||
//
|
||||
// Created by Ryan Huffman on Nov 9, 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("utils.js");
|
||||
|
||||
var emitters = [];
|
||||
|
||||
var smokeTrailSettings = {
|
||||
"name":"ParticlesTest Emitter",
|
||||
"type": "ParticleEffect",
|
||||
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
|
||||
"maxParticles":1000,
|
||||
"velocity": { x: 0, y: 18.0, z: 0 },
|
||||
"lifetime": 20,
|
||||
"lifespan":3,
|
||||
"emitRate":100,
|
||||
"emitSpeed":0.5,
|
||||
"speedSpread":0,
|
||||
"emitOrientation":{"x":0,"y":0,"z":0,"w":1},
|
||||
"emitDimensions":{"x":0,"y":0,"z":0},
|
||||
"emitRadiusStart":0.5,
|
||||
"polarStart":1,
|
||||
"polarFinish":1,
|
||||
"azimuthStart":0,
|
||||
"azimuthFinish":0,
|
||||
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
|
||||
"accelerationSpread":{"x":0,"y":0,"z":0},
|
||||
"particleRadius":0.03999999910593033,
|
||||
"radiusSpread":0,
|
||||
"radiusStart":0.13999999910593033,
|
||||
"radiusFinish":0.14,
|
||||
"colorSpread":{"red":0,"green":0,"blue":0},
|
||||
"colorStart":{"red":255,"green":255,"blue":255},
|
||||
"colorFinish":{"red":255,"green":255,"blue":255},
|
||||
"alpha":1,
|
||||
"alphaSpread":0,
|
||||
"alphaStart":0,
|
||||
"alphaFinish":1,
|
||||
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
|
||||
};
|
||||
|
||||
var fireworkSettings = {
|
||||
"name":"ParticlesTest Emitter",
|
||||
"type": "ParticleEffect",
|
||||
"color":{"red":205,"green":84.41176470588235,"blue":84.41176470588235},
|
||||
"maxParticles":1000,
|
||||
"lifetime": 20,
|
||||
"lifespan":4,
|
||||
"emitRate":1000,
|
||||
"emitSpeed":1.5,
|
||||
"speedSpread":1.0,
|
||||
"emitOrientation":{"x":-0.2,"y":0,"z":0,"w":0.7000000000000001},
|
||||
"emitDimensions":{"x":0,"y":0,"z":0},
|
||||
"emitRadiusStart":0.5,
|
||||
"polarStart":1,
|
||||
"polarFinish":1.2,
|
||||
"azimuthStart":-Math.PI,
|
||||
"azimuthFinish":Math.PI,
|
||||
"emitAcceleration":{"x":0,"y":-0.70000001192092896,"z":0},
|
||||
"accelerationSpread":{"x":0,"y":0,"z":0},
|
||||
"particleRadius":0.03999999910593033,
|
||||
"radiusSpread":0,
|
||||
"radiusStart":0.13999999910593033,
|
||||
"radiusFinish":0.14,
|
||||
"colorSpread":{"red":0,"green":0,"blue":0},
|
||||
"colorStart":{"red":255,"green":255,"blue":255},
|
||||
"colorFinish":{"red":255,"green":255,"blue":255},
|
||||
"alpha":1,
|
||||
"alphaSpread":0,
|
||||
"alphaStart":0,
|
||||
"alphaFinish":1,
|
||||
"textures":"https://hifi-public.s3.amazonaws.com/alan/Particles/spark_2.png",
|
||||
};
|
||||
|
||||
var popSounds = getSounds([
|
||||
"atp:a2bf79c95fe74c2c6c9188acc7230f7cd1b0f6008f2c81954ecd93eca0497ec6.wav",
|
||||
"atp:901067ebc2cda4c0d86ec02fcca2ed901e85f9097ad68bbde78b4cad8eaf2ed7.wav",
|
||||
"atp:830312930577cb1ea36ba2d743e957debbacceb441b20addead5a6faa05a3771.wav",
|
||||
"atp:62e80d0a9f084cf731bcc66ca6e9020ee88587417071a281eee3167307b53560.wav"
|
||||
]);
|
||||
|
||||
var launchSounds = getSounds([
|
||||
"atp:ee6afe565576c4546c6d6cd89c1af532484c9b60ab30574d6b40c2df022f7260.wav",
|
||||
"atp:91ef19ba1c78be82d3fd06530cd05ceb90d1e75f4204c66819c208c55da049ef.wav",
|
||||
"atp:ee56993daf775012cf49293bfd5971eec7e5c396642f8bfbea902ba8f47b56cd.wav",
|
||||
"atp:37775d267f00f82242a7e7f61f3f3d7bf64a54c5a3799e7f2540fa5f6b79bd02.wav"
|
||||
]);
|
||||
|
||||
function playRandomSound(sounds, options) {
|
||||
Audio.playSound(sounds[randomInt(sounds.length)], options);
|
||||
}
|
||||
|
||||
function shootFirework(position, color, options) {
|
||||
smokeTrailSettings.position = position;
|
||||
smokeTrailSettings.velocity = randomVec3(-5, 5, 10, 20, 10, 15);
|
||||
smokeTrailSettings.gravity = randomVec3(-5, 5, -9.8, -9.8, 20, 40);
|
||||
|
||||
playRandomSound(launchSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
|
||||
var smokeID = Entities.addEntity(smokeTrailSettings);
|
||||
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(smokeID, { emitRate: 0 });
|
||||
var position = Entities.getEntityProperties(smokeID, ['position']).position;
|
||||
fireworkSettings.position = position;
|
||||
fireworkSettings.colorStart = color;
|
||||
fireworkSettings.colorFinish = color;
|
||||
var burstID = Entities.addEntity(fireworkSettings);
|
||||
playRandomSound(popSounds, { position: {x: 0, y: 0 , z: 0}, volume: 3.0 });
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(burstID, { emitRate: 0 });
|
||||
}, 500);
|
||||
Script.setTimeout(function() {
|
||||
Entities.deleteEntity(smokeID);
|
||||
Entities.deleteEntity(burstID);
|
||||
}, 10000);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
playFireworkShow = function(position, numberOfFireworks, duration) {
|
||||
for (var i = 0; i < numberOfFireworks; i++) {
|
||||
var randomOffset = randomVec3(-15, 15, -3, 3, -1, 1);
|
||||
var randomPosition = Vec3.sum(position, randomOffset);
|
||||
Script.setTimeout(function(position) {
|
||||
return function() {
|
||||
var color = randomColor(128, 255, 128, 255, 128, 255);
|
||||
shootFirework(position, color, fireworkSettings);
|
||||
}
|
||||
}(randomPosition), Math.random() * duration)
|
||||
}
|
||||
}
|
165
examples/baseball/line.js
Normal file
165
examples/baseball/line.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
function info(message) {
|
||||
print("[INFO] " + message);
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
print("[ERROR] " + message);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* PolyLine
|
||||
*****************************************************************************/
|
||||
var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 };
|
||||
var MAX_LINE_LENGTH = 40; // This must be 2 or greater;
|
||||
var PolyLine = function(position, color, defaultStrokeWidth) {
|
||||
//info("Creating polyline");
|
||||
//Vec3.print("New line at", position);
|
||||
this.position = position;
|
||||
this.color = color;
|
||||
this.defaultStrokeWidth = 0.10;
|
||||
this.points = [
|
||||
{ x: 0, y: 0, z: 0 },
|
||||
];
|
||||
this.strokeWidths = [
|
||||
this.defaultStrokeWidth,
|
||||
]
|
||||
this.normals = [
|
||||
{ x: 1, y: 0, z: 0 },
|
||||
]
|
||||
this.entityID = Entities.addEntity({
|
||||
type: "PolyLine",
|
||||
position: position,
|
||||
linePoints: this.points,
|
||||
normals: this.normals,
|
||||
strokeWidths: this.strokeWidths,
|
||||
dimensions: LINE_DIMENSIONS,
|
||||
color: color,
|
||||
lifetime: 20,
|
||||
});
|
||||
};
|
||||
|
||||
PolyLine.prototype.enqueuePoint = function(position) {
|
||||
if (this.isFull()) {
|
||||
error("Hit max PolyLine size");
|
||||
return;
|
||||
}
|
||||
|
||||
//Vec3.print("pos", position);
|
||||
//info("Number of points: " + this.points.length);
|
||||
|
||||
position = Vec3.subtract(position, this.position);
|
||||
this.points.push(position);
|
||||
this.normals.push({ x: 1, y: 0, z: 0 });
|
||||
this.strokeWidths.push(this.defaultStrokeWidth);
|
||||
Entities.editEntity(this.entityID, {
|
||||
linePoints: this.points,
|
||||
normals: this.normals,
|
||||
strokeWidths: this.strokeWidths,
|
||||
});
|
||||
};
|
||||
|
||||
PolyLine.prototype.dequeuePoint = function() {
|
||||
if (this.points.length == 0) {
|
||||
error("Hit min PolyLine size");
|
||||
return;
|
||||
}
|
||||
|
||||
this.points = this.points.slice(1);
|
||||
this.normals = this.normals.slice(1);
|
||||
this.strokeWidths = this.strokeWidths.slice(1);
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
linePoints: this.points,
|
||||
normals: this.normals,
|
||||
strokeWidths: this.strokeWidths,
|
||||
});
|
||||
};
|
||||
|
||||
PolyLine.prototype.getFirstPoint = function() {
|
||||
return Vec3.sum(this.position, this.points[0]);
|
||||
};
|
||||
|
||||
PolyLine.prototype.getLastPoint = function() {
|
||||
return Vec3.sum(this.position, this.points[this.points.length - 1]);
|
||||
};
|
||||
|
||||
PolyLine.prototype.getSize = function() {
|
||||
return this.points.length;
|
||||
}
|
||||
|
||||
PolyLine.prototype.isFull = function() {
|
||||
return this.points.length >= MAX_LINE_LENGTH;
|
||||
};
|
||||
|
||||
PolyLine.prototype.destroy = function() {
|
||||
Entities.deleteEntity(this.entityID);
|
||||
this.points = [];
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* InfiniteLine
|
||||
*****************************************************************************/
|
||||
InfiniteLine = function(position, color) {
|
||||
this.position = position;
|
||||
this.color = color;
|
||||
this.lines = [new PolyLine(position, color)];
|
||||
this.size = 0;
|
||||
};
|
||||
|
||||
InfiniteLine.prototype.enqueuePoint = function(position) {
|
||||
var currentLine;
|
||||
|
||||
if (this.lines.length == 0) {
|
||||
currentLine = new PolyLine(position, this.color);
|
||||
this.lines.push(currentLine);
|
||||
} else {
|
||||
currentLine = this.lines[this.lines.length - 1];
|
||||
}
|
||||
|
||||
if (currentLine.isFull()) {
|
||||
//info("Current line is full, creating new line");
|
||||
//Vec3.print("Last line is", currentLine.getLastPoint());
|
||||
//Vec3.print("New line is", position);
|
||||
var newLine = new PolyLine(currentLine.getLastPoint(), this.color);
|
||||
this.lines.push(newLine);
|
||||
currentLine = newLine;
|
||||
}
|
||||
|
||||
currentLine.enqueuePoint(position);
|
||||
|
||||
++this.size;
|
||||
};
|
||||
|
||||
InfiniteLine.prototype.dequeuePoint = function() {
|
||||
if (this.lines.length == 0) {
|
||||
error("Trying to dequeue from InfiniteLine when no points are left");
|
||||
return;
|
||||
}
|
||||
|
||||
var lastLine = this.lines[0];
|
||||
lastLine.dequeuePoint();
|
||||
|
||||
if (lastLine.getSize() <= 1) {
|
||||
this.lines = this.lines.slice(1);
|
||||
}
|
||||
|
||||
--this.size;
|
||||
};
|
||||
|
||||
InfiniteLine.prototype.getFirstPoint = function() {
|
||||
return this.lines.length > 0 ? this.lines[0].getFirstPoint() : null;
|
||||
};
|
||||
|
||||
InfiniteLine.prototype.getLastPoint = function() {
|
||||
return this.lines.length > 0 ? this.lines[lines.length - 1].getLastPoint() : null;
|
||||
};
|
||||
|
||||
InfiniteLine.prototype.destroy = function() {
|
||||
for (var i = 0; i < this.lines.length; ++i) {
|
||||
this.lines[i].destroy();
|
||||
}
|
||||
|
||||
this.size = 0;
|
||||
};
|
554
examples/baseball/pitching.js
Normal file
554
examples/baseball/pitching.js
Normal file
|
@ -0,0 +1,554 @@
|
|||
//
|
||||
// pitching.js
|
||||
// examples/baseball/
|
||||
//
|
||||
// Created by Ryan Huffman on Nov 9, 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
|
||||
//
|
||||
|
||||
print("Loading pitching");
|
||||
|
||||
Script.include("../libraries/line.js");
|
||||
Script.include("firework.js");
|
||||
Script.include("utils.js");
|
||||
|
||||
var DISTANCE_BILLBOARD_NAME = "CurrentScore";
|
||||
var HIGH_SCORE_BILLBOARD_NAME = "HighScore";
|
||||
|
||||
var distanceBillboardEntityID = null;
|
||||
var highScoreBillboardEntityID = null;
|
||||
|
||||
function getDistanceBillboardEntityID() {
|
||||
if (distanceBillboardEntityID === null) {
|
||||
distanceBillboardEntityID = findEntity({name: DISTANCE_BILLBOARD_NAME }, 1000);
|
||||
}
|
||||
return distanceBillboardEntityID;
|
||||
}
|
||||
function getHighScoreBillboardEntityID() {
|
||||
if (highScoreBillboardEntityID === null) {
|
||||
highScoreBillboardEntityID = findEntity({name: HIGH_SCORE_BILLBOARD_NAME }, 1000);
|
||||
}
|
||||
return highScoreBillboardEntityID;
|
||||
}
|
||||
|
||||
var METERS_TO_FEET = 3.28084;
|
||||
|
||||
var AUDIO = {
|
||||
crowdBoos: [
|
||||
SoundCache.getSound("atp:c632c92b166ade60aa16b23ff1dfdf712856caeb83bd9311980b2d5edac821af.wav", false)
|
||||
],
|
||||
crowdCheers: [
|
||||
SoundCache.getSound("atp:0821bf2ac60dd2f356dfdd948e8bb89c23984dc3584612f6c815765154f02cae.wav", false),
|
||||
SoundCache.getSound("atp:b8044401a846ed29f881a0b9b80cf1ba41f26327180c28fc9c70d144f9b70045.wav", false),
|
||||
],
|
||||
batHit: [
|
||||
SoundCache.getSound("atp:6f0b691a0c9c9ece6557d97fe242b1faec4020fe26efc9c17327993b513c5fe5.wav", false),
|
||||
SoundCache.getSound("atp:5be5806205158ebdc5c3623ceb7ae73315028b51ffeae24292aff7042e3fa6a9.wav", false),
|
||||
SoundCache.getSound("atp:e68661374e2145c480809c26134782aad11e0de456c7802170c7abccc4028873.wav", false),
|
||||
SoundCache.getSound("atp:787e3c9af17dd3929527787176ede83d6806260e63ddd5a4cef48cd22e32c6f7.wav", false),
|
||||
SoundCache.getSound("atp:fc65383431a6238c7a4749f0f6f061f75a604ed5e17d775ab1b2955609e67ebb.wav", false),
|
||||
],
|
||||
strike: [
|
||||
SoundCache.getSound("atp:2a258076a85fffde4ba04b5ddc1de9034c7ae7d2af8c5d93d4fed0bcdef3472a.wav", false),
|
||||
SoundCache.getSound("atp:518363524af3ed9b9ae4ca2ceee61f01aecd37e266a51c5a5f5487efe2520fd5.wav", false),
|
||||
SoundCache.getSound("atp:d51d38b089574acbdfdf53ef733bfb3ab41d848fb8c0b6659c7790a785240009.wav", false),
|
||||
],
|
||||
foul: [
|
||||
SoundCache.getSound("atp:316fa18ff9eef457f670452b449a8dc5a41ccabd4e948781c50aaafaae63b0ab.wav", false),
|
||||
SoundCache.getSound("atp:c84d88352d38437edd7414b26dc74e567618712caeb59fec70822398b0c5a279.wav", false),
|
||||
]
|
||||
}
|
||||
|
||||
var PITCH_THUNK_SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/ping_pong_gun/pong_sound.wav";
|
||||
var pitchSound = SoundCache.getSound(PITCH_THUNK_SOUND_URL, false);
|
||||
updateBillboard("");
|
||||
|
||||
var PITCHING_MACHINE_URL = "atp:87d4879530b698741ecc45f6f31789aac11f7865a2c3bec5fe9b061a182c80d4.fbx";
|
||||
// This defines an offset to pitch a ball from with respect to the machine's position. The offset is a
|
||||
// percentage of the machine's dimensions. So, { x: 0.5, y: -1.0, z: 0.0 } would offset on 50% on the
|
||||
// machine's x axis, -100% on the y axis, and 0% on the z-axis. For the dimensions { x: 100, y: 100, z: 100 },
|
||||
// that would result in an offset of { x: 50, y: -100, z: 0 }. This makes it easy to calculate an offset if
|
||||
// the machine's dimensions change.
|
||||
var PITCHING_MACHINE_OUTPUT_OFFSET_PCT = {
|
||||
x: 0.0,
|
||||
y: 0.25,
|
||||
z: -1.05,
|
||||
};
|
||||
var PITCHING_MACHINE_PROPERTIES = {
|
||||
name: "Pitching Machine",
|
||||
type: "Model",
|
||||
position: {
|
||||
x: -0.93,
|
||||
y: 0.8,
|
||||
z: -19.8
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
gravity: {
|
||||
x: 0.0,
|
||||
y: -9.8,
|
||||
z: 0.0
|
||||
},
|
||||
registrationPoint: {
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
z: 0.5
|
||||
},
|
||||
rotation: Quat.fromPitchYawRollDegrees(0, 180, 0),
|
||||
modelURL: PITCHING_MACHINE_URL,
|
||||
dimensions: {
|
||||
x: 0.4,
|
||||
y: 0.61,
|
||||
z: 0.39
|
||||
},
|
||||
collisionsWillMove: false,
|
||||
shapeType: "Box"
|
||||
};
|
||||
PITCHING_MACHINE_PROPERTIES.dimensions = Vec3.multiply(2.5, PITCHING_MACHINE_PROPERTIES.dimensions);
|
||||
var DISTANCE_FROM_PLATE = PITCHING_MACHINE_PROPERTIES.position.z;
|
||||
|
||||
var PITCH_RATE = 5000;
|
||||
|
||||
getOrCreatePitchingMachine = function() {
|
||||
// Search for pitching machine
|
||||
var entities = findEntities({ name: PITCHING_MACHINE_PROPERTIES.name }, 1000);
|
||||
var pitchingMachineID = null;
|
||||
|
||||
// Create if it doesn't exist
|
||||
if (entities.length == 0) {
|
||||
pitchingMachineID = Entities.addEntity(PITCHING_MACHINE_PROPERTIES);
|
||||
} else {
|
||||
pitchingMachineID = entities[0];
|
||||
}
|
||||
|
||||
// Wrap with PitchingMachine object and return
|
||||
return new PitchingMachine(pitchingMachineID);
|
||||
}
|
||||
|
||||
// The pitching machine wraps an entity ID and uses it's position & rotation to determin where to
|
||||
// pitch the ball from and in which direction, and uses the dimensions to determine the scale of them ball.
|
||||
function PitchingMachine(pitchingMachineID) {
|
||||
this.pitchingMachineID = pitchingMachineID;
|
||||
this.enabled = false;
|
||||
this.baseball = null;
|
||||
this.injector = null;
|
||||
}
|
||||
|
||||
PitchingMachine.prototype = {
|
||||
pitchBall: function() {
|
||||
cleanupTrail();
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
print("Pitching ball");
|
||||
var machineProperties = Entities.getEntityProperties(this.pitchingMachineID, ["dimensions", "position", "rotation"]);
|
||||
var pitchFromPositionBase = machineProperties.position;
|
||||
var pitchFromOffset = vec3Mult(machineProperties.dimensions, PITCHING_MACHINE_OUTPUT_OFFSET_PCT);
|
||||
pitchFromOffset = Vec3.multiplyQbyV(machineProperties.rotation, pitchFromOffset);
|
||||
var pitchFromPosition = Vec3.sum(pitchFromPositionBase, pitchFromOffset);
|
||||
var pitchDirection = Quat.getFront(machineProperties.rotation);
|
||||
var ballScale = machineProperties.dimensions.x / PITCHING_MACHINE_PROPERTIES.dimensions.x;
|
||||
|
||||
var speed = randomFloat(BASEBALL_MIN_SPEED, BASEBALL_MAX_SPEED);
|
||||
var velocity = Vec3.multiply(speed, pitchDirection);
|
||||
|
||||
this.baseball = new Baseball(pitchFromPosition, velocity, ballScale);
|
||||
|
||||
if (!this.injector) {
|
||||
this.injector = Audio.playSound(pitchSound, {
|
||||
position: pitchFromPosition,
|
||||
volume: 1.0
|
||||
});
|
||||
} else {
|
||||
this.injector.restart();
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
if (this.enabled) {
|
||||
return;
|
||||
}
|
||||
print("Starting Pitching Machine");
|
||||
this.enabled = true;
|
||||
this.pitchBall();
|
||||
},
|
||||
stop: function() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
print("Stopping Pitching Machine");
|
||||
this.enabled = false;
|
||||
},
|
||||
update: function(dt) {
|
||||
if (this.baseball) {
|
||||
this.baseball.update(dt);
|
||||
if (this.baseball.finished()) {
|
||||
this.baseball = null;
|
||||
var self = this;
|
||||
Script.setTimeout(function() { self.pitchBall(); }, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var BASEBALL_MODEL_URL = "atp:7185099f1f650600ca187222573a88200aeb835454bd2f578f12c7fb4fd190fa.fbx";
|
||||
var BASEBALL_MIN_SPEED = 7.0;
|
||||
var BASEBALL_MAX_SPEED = 15.0;
|
||||
var BASEBALL_RADIUS = 0.07468;
|
||||
var BASEBALL_PROPERTIES = {
|
||||
name: "Baseball",
|
||||
type: "Model",
|
||||
modelURL: BASEBALL_MODEL_URL,
|
||||
shapeType: "Sphere",
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: BASEBALL_RADIUS,
|
||||
y: BASEBALL_RADIUS,
|
||||
z: BASEBALL_RADIUS
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
angularVelocity: {
|
||||
x: 17.0,
|
||||
y: 0,
|
||||
z: -8.0,
|
||||
|
||||
x: 0.0,
|
||||
y: 0,
|
||||
z: 0.0,
|
||||
},
|
||||
angularDamping: 0.0,
|
||||
damping: 0.0,
|
||||
restitution: 0.5,
|
||||
friction: 0.0,
|
||||
friction: 0.5,
|
||||
lifetime: 20,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
};
|
||||
var BASEBALL_STATE = {
|
||||
PITCHING: 0,
|
||||
HIT: 1,
|
||||
HIT_LANDED: 2,
|
||||
STRIKE: 3,
|
||||
FOUL: 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
function vec3Mult(a, b) {
|
||||
return {
|
||||
x: a.x * b.x,
|
||||
y: a.y * b.y,
|
||||
z: a.z * b.z,
|
||||
};
|
||||
}
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
function orientationOf(vector) {
|
||||
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||
var Y_AXIS = { x: 0, y: 1, z: 0 };
|
||||
var X_AXIS = { x: 1, y: 0, z: 0 };
|
||||
var direction = Vec3.normalize(vector);
|
||||
|
||||
var yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
var pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
var ACCELERATION_SPREAD = 0.35;
|
||||
|
||||
var TRAIL_COLOR = { red: 128, green: 255, blue: 89 };
|
||||
var TRAIL_LIFETIME = 20;
|
||||
|
||||
var trail = null;
|
||||
var trailInterval = null;
|
||||
function cleanupTrail() {
|
||||
if (trail) {
|
||||
Script.clearInterval(this.trailInterval);
|
||||
trailInterval = null;
|
||||
|
||||
trail.destroy();
|
||||
trail = null;
|
||||
}
|
||||
}
|
||||
|
||||
function setupTrail(entityID, position) {
|
||||
cleanupTrail();
|
||||
|
||||
var lastPosition = position;
|
||||
trail = new InfiniteLine(position, { red: 128, green: 255, blue: 89 }, 20);
|
||||
trailInterval = Script.setInterval(function() {
|
||||
var properties = Entities.getEntityProperties(entityID, ['position']);
|
||||
if (Vec3.distance(properties.position, lastPosition)) {
|
||||
var strokeWidth = Math.log(1 + trail.size) * 0.05;
|
||||
trail.enqueuePoint(properties.position, strokeWidth);
|
||||
lastPosition = properties.position;
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function Baseball(position, velocity, ballScale) {
|
||||
var self = this;
|
||||
|
||||
this.state = BASEBALL_STATE.PITCHING;
|
||||
|
||||
// Setup entity properties
|
||||
var properties = shallowCopy(BASEBALL_PROPERTIES);
|
||||
properties.position = position;
|
||||
properties.velocity = velocity;
|
||||
properties.dimensions = Vec3.multiply(ballScale, properties.dimensions);
|
||||
/*
|
||||
properties.gravity = {
|
||||
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
|
||||
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
|
||||
z: 0.0,
|
||||
};
|
||||
*/
|
||||
|
||||
// Create entity
|
||||
this.entityID = Entities.addEntity(properties);
|
||||
|
||||
this.timeSincePitched = 0;
|
||||
this.timeSinceHit = 0;
|
||||
this.hitBallAtPosition = null;
|
||||
this.distanceTravelled = 0;
|
||||
this.wasHighScore = false;
|
||||
this.landed = false;
|
||||
|
||||
// Listen for collision for the lifetime of the entity
|
||||
Script.addEventHandler(this.entityID, "collisionWithEntity", function(entityA, entityB, collision) {
|
||||
self.collisionCallback(entityA, entityB, collision);
|
||||
});
|
||||
if (Math.random() < 0.5) {
|
||||
for (var i = 0; i < 50; i++) {
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(entityID, {
|
||||
gravity: {
|
||||
x: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
|
||||
y: randomFloat(-ACCELERATION_SPREAD, ACCELERATION_SPREAD),
|
||||
z: 0.0,
|
||||
}
|
||||
})
|
||||
}, i * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the stadium billboard with the current distance or a text message and update the high score
|
||||
// if it has been beaten.
|
||||
function updateBillboard(distanceOrMessage) {
|
||||
var distanceBillboardEntityID = getDistanceBillboardEntityID();
|
||||
if (distanceBillboardEntityID) {
|
||||
Entities.editEntity(distanceBillboardEntityID, {
|
||||
text: distanceOrMessage
|
||||
});
|
||||
}
|
||||
|
||||
var highScoreBillboardEntityID = getHighScoreBillboardEntityID();
|
||||
// If a number was passed in, let's see if it is larger than the current high score
|
||||
// and update it if so.
|
||||
if (!isNaN(distanceOrMessage) && highScoreBillboardEntityID) {
|
||||
var properties = Entities.getEntityProperties(highScoreBillboardEntityID, ["text"]);
|
||||
var bestDistance = parseInt(properties.text);
|
||||
if (distanceOrMessage >= bestDistance) {
|
||||
Entities.editEntity(highScoreBillboardEntityID, {
|
||||
text: distanceOrMessage,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var FIREWORKS_SHOW_POSITION = { x: 0, y: 0, z: -78.0 };
|
||||
var FIREWORK_PER_X_FEET = 100;
|
||||
var MAX_FIREWORKS = 10;
|
||||
|
||||
Baseball.prototype = {
|
||||
finished: function() {
|
||||
return this.state == BASEBALL_STATE.FOUL
|
||||
|| this.state == BASEBALL_STATE.STRIKE
|
||||
|| this.state == BASEBALL_STATE.HIT_LANDED;
|
||||
},
|
||||
update: function(dt) {
|
||||
this.timeSincePitched += dt;
|
||||
if (this.state == BASEBALL_STATE.HIT) {
|
||||
this.timeSinceHit += dt;
|
||||
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
|
||||
var speed = Vec3.length(myProperties.velocity);
|
||||
this.distanceTravelled = Vec3.distance(this.hitBallAtPosition, myProperties.position) * METERS_TO_FEET;
|
||||
var wasHighScore = updateBillboard(Math.ceil(this.distanceTravelled));
|
||||
if (this.landed || this.timeSinceHit > 10 || speed < 1) {
|
||||
this.wasHighScore = wasHighScore;
|
||||
this.ballLanded();
|
||||
}
|
||||
} else if (this.state == BASEBALL_STATE.PITCHING) {
|
||||
if (this.timeSincePitched > 10) {
|
||||
print("TIMED OUT WHILE PITCHING");
|
||||
this.state = BASEBALL_STATE.STRIKE;
|
||||
}
|
||||
}
|
||||
},
|
||||
ballLanded: function() {
|
||||
this.state = BASEBALL_STATE.HIT_LANDED;
|
||||
var numberOfFireworks = Math.floor(this.distanceTravelled / FIREWORK_PER_X_FEET);
|
||||
if (numberOfFireworks > 0) {
|
||||
numberOfFireworks = Math.min(MAX_FIREWORKS, numberOfFireworks);
|
||||
playFireworkShow(FIREWORKS_SHOW_POSITION, numberOfFireworks, 2000);
|
||||
}
|
||||
print("Ball took " + this.timeSinceHit.toFixed(3) + " seconds to land");
|
||||
print("Ball travelled " + this.distanceTravelled + " feet")
|
||||
},
|
||||
collisionCallback: function(entityA, entityB, collision) {
|
||||
var self = this;
|
||||
var myProperties = Entities.getEntityProperties(this.entityID, ['position', 'velocity']);
|
||||
var myPosition = myProperties.position;
|
||||
var myVelocity = myProperties.velocity;
|
||||
|
||||
// Activate gravity
|
||||
Entities.editEntity(self.entityID, {
|
||||
gravity: { x: 0, y: -9.8, z: 0 }
|
||||
});
|
||||
|
||||
var name = Entities.getEntityProperties(entityB, ["name"]).name;
|
||||
if (name == "Bat") {
|
||||
if (this.state == BASEBALL_STATE.PITCHING) {
|
||||
print("HIT");
|
||||
|
||||
var FOUL_MIN_YAW = -135.0;
|
||||
var FOUL_MAX_YAW = 135.0;
|
||||
|
||||
var yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
|
||||
var foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
|
||||
|
||||
var speedMultiplier = 2;
|
||||
|
||||
if (foul && myVelocity.z > 0) {
|
||||
var TUNNELED_PITCH_RANGE = 15.0;
|
||||
var xzDist = Math.sqrt(myVelocity.x * myVelocity.x + myVelocity.z * myVelocity.z);
|
||||
var pitch = Math.atan2(myVelocity.y, xzDist) * 180 / Math.PI;
|
||||
print("Pitch: ", pitch);
|
||||
// If the pitch is going straight out the back and has a pitch in the range TUNNELED_PITCH_RANGE,
|
||||
// let's assume the ball tunneled through the bat and reverse its direction.
|
||||
if (Math.abs(pitch) < TUNNELED_PITCH_RANGE) {
|
||||
print("Reversing hit");
|
||||
myVelocity.x *= -1;
|
||||
myVelocity.y *= -1;
|
||||
myVelocity.z *= -1;
|
||||
|
||||
yaw = Math.atan2(myVelocity.x, myVelocity.z) * 180 / Math.PI;
|
||||
foul = yaw > FOUL_MIN_YAW && yaw < FOUL_MAX_YAW;
|
||||
|
||||
speedMultiplier = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Update ball velocity
|
||||
Entities.editEntity(self.entityID, {
|
||||
velocity: Vec3.multiply(speedMultiplier, myVelocity)
|
||||
});
|
||||
|
||||
// Setup line update interval
|
||||
setupTrail(self.entityID, myPosition);
|
||||
|
||||
// Setup bat hit sound
|
||||
playRandomSound(AUDIO.batHit, {
|
||||
position: myPosition,
|
||||
volume: 2.0
|
||||
});
|
||||
|
||||
// Setup crowd reaction sound
|
||||
var speed = Vec3.length(myVelocity);
|
||||
Script.setTimeout(function() {
|
||||
playRandomSound((speed < 5.0) ? AUDIO.crowdBoos : AUDIO.crowdCheers, {
|
||||
position: { x: 0 ,y: 0, z: 0 },
|
||||
volume: 1.0
|
||||
});
|
||||
}, 500);
|
||||
|
||||
if (foul) {
|
||||
print("FOUL, yaw: ", yaw);
|
||||
updateBillboard("FOUL");
|
||||
this.state = BASEBALL_STATE.FOUL;
|
||||
playRandomSound(AUDIO.foul, {
|
||||
position: myPosition,
|
||||
volume: 2.0
|
||||
});
|
||||
} else {
|
||||
print("HIT ", yaw);
|
||||
this.state = BASEBALL_STATE.HIT;
|
||||
}
|
||||
}
|
||||
} else if (name == "stadium") {
|
||||
//entityCollisionWithGround(entityB, this.entityID, collision);
|
||||
this.landed = true;
|
||||
} else if (name == "backstop") {
|
||||
if (this.state == BASEBALL_STATE.PITCHING) {
|
||||
print("STRIKE");
|
||||
this.state = BASEBALL_STATE.STRIKE;
|
||||
updateBillboard("STRIKE");
|
||||
playRandomSound(AUDIO.strike, {
|
||||
position: myPosition,
|
||||
volume: 2.0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function entityCollisionWithGround(ground, entity, collision) {
|
||||
var ZERO_VEC = { x: 0, y: 0, z: 0 };
|
||||
var dVelocityMagnitude = Vec3.length(collision.velocityChange);
|
||||
var position = Entities.getEntityProperties(entity, "position").position;
|
||||
var particleRadius = 0.3;
|
||||
var speed = map(dVelocityMagnitude, 0.05, 3, 0.02, 0.09);
|
||||
var displayTime = 400;
|
||||
var orientationChange = orientationOf(collision.velocityChange);
|
||||
|
||||
var dustEffect = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "Dust-Puff",
|
||||
position: position,
|
||||
color: {red: 195, green: 170, blue: 185},
|
||||
lifespan: 3,
|
||||
lifetime: 2,//displayTime/1000 * 2, //So we can fade particle system out gracefully
|
||||
emitRate: 5,
|
||||
emitSpeed: speed,
|
||||
emitAcceleration: ZERO_VEC,
|
||||
accelerationSpread: ZERO_VEC,
|
||||
isEmitting: true,
|
||||
polarStart: Math.PI/2,
|
||||
polarFinish: Math.PI/2,
|
||||
emitOrientation: orientationChange,
|
||||
radiusSpread: 0.1,
|
||||
radiusStart: particleRadius,
|
||||
radiusFinish: particleRadius + particleRadius / 2,
|
||||
particleRadius: particleRadius,
|
||||
alpha: 0.45,
|
||||
alphaFinish: 0.001,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Playa/Particles/Particle-Sprite-Gen.png"
|
||||
});
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
cleanupTrail();
|
||||
Entities.deleteEntity(pitchingMachineID);
|
||||
});
|
92
examples/baseball/utils.js
Normal file
92
examples/baseball/utils.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// utils.js
|
||||
// examples/baseball/
|
||||
//
|
||||
// Created by Ryan Huffman on Nov 9, 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
|
||||
//
|
||||
|
||||
randomInt = function(low, high) {
|
||||
return Math.floor(randomFloat(low, high));
|
||||
};
|
||||
|
||||
randomFloat = function(low, high) {
|
||||
if (high === undefined) {
|
||||
high = low;
|
||||
low = 0;
|
||||
}
|
||||
return low + Math.random() * (high - low);
|
||||
};
|
||||
|
||||
randomColor = function(redMin, redMax, greenMin, greenMax, blueMin, blueMax) {
|
||||
return {
|
||||
red: Math.ceil(randomFloat(redMin, redMax)),
|
||||
green: Math.ceil(randomFloat(greenMin, greenMax)),
|
||||
blue: Math.ceil(randomFloat(blueMin, blueMax)),
|
||||
}
|
||||
};
|
||||
|
||||
randomVec3 = function(xMin, xMax, yMin, yMax, zMin, zMax) {
|
||||
return {
|
||||
x: randomFloat(xMin, xMax),
|
||||
y: randomFloat(yMin, yMax),
|
||||
z: randomFloat(zMin, zMax),
|
||||
}
|
||||
};
|
||||
|
||||
getSounds = function(soundURLs) {
|
||||
var sounds = [];
|
||||
for (var i = 0; i < soundURLs.length; ++i) {
|
||||
sounds.push(SoundCache.getSound(soundURLs[i], false));
|
||||
}
|
||||
return sounds;
|
||||
};
|
||||
|
||||
playRandomSound = function(sounds, options) {
|
||||
if (options === undefined) {
|
||||
options = {
|
||||
volume: 1.0,
|
||||
position: MyAvatar.position,
|
||||
}
|
||||
}
|
||||
return Audio.playSound(sounds[randomInt(sounds.length)], options);
|
||||
}
|
||||
|
||||
shallowCopy = function(obj) {
|
||||
var copy = {}
|
||||
for (var key in obj) {
|
||||
copy[key] = obj[key];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
findEntity = function(properties, searchRadius) {
|
||||
var entities = findEntities(properties, searchRadius);
|
||||
return entities.length > 0 ? entities[0] : null;
|
||||
}
|
||||
|
||||
// Return all entities with properties `properties` within radius `searchRadius`
|
||||
findEntities = function(properties, searchRadius) {
|
||||
var entities = Entities.findEntities(MyAvatar.position, searchRadius);
|
||||
var matchedEntities = [];
|
||||
var keys = Object.keys(properties);
|
||||
for (var i = 0; i < entities.length; ++i) {
|
||||
var match = true;
|
||||
var candidateProperties = Entities.getEntityProperties(entities[i], keys);
|
||||
for (var key in properties) {
|
||||
if (candidateProperties[key] != properties[key]) {
|
||||
// This isn't a match, move to next entity
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
matchedEntities.push(entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return matchedEntities;
|
||||
}
|
|
@ -34,9 +34,10 @@ var BUMPER_ON_VALUE = 0.5;
|
|||
// distant manipulation
|
||||
//
|
||||
|
||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object
|
||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object
|
||||
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
|
||||
var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
|
||||
var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects
|
||||
|
||||
var NO_INTERSECT_COLOR = {
|
||||
red: 10,
|
||||
|
@ -200,6 +201,43 @@ function entityIsGrabbedByOther(entityID) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function getSpatialOffsetPosition(hand, spatialKey) {
|
||||
var position = Vec3.ZERO;
|
||||
|
||||
if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) {
|
||||
position = spatialKey.leftRelativePosition;
|
||||
}
|
||||
if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) {
|
||||
position = spatialKey.rightRelativePosition;
|
||||
}
|
||||
if (spatialKey.relativePosition) {
|
||||
position = spatialKey.relativePosition;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y);
|
||||
function getSpatialOffsetRotation(hand, spatialKey) {
|
||||
var rotation = Quat.IDENTITY;
|
||||
|
||||
if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) {
|
||||
rotation = spatialKey.leftRelativeRotation;
|
||||
}
|
||||
if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) {
|
||||
rotation = spatialKey.rightRelativeRotation;
|
||||
}
|
||||
if (spatialKey.relativeRotation) {
|
||||
rotation = spatialKey.relativeRotation;
|
||||
}
|
||||
|
||||
// Flip left hand
|
||||
if (hand !== RIGHT_HAND) {
|
||||
rotation = Quat.multiply(yFlip, rotation);
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
function MyController(hand) {
|
||||
this.hand = hand;
|
||||
|
@ -223,20 +261,12 @@ function MyController(hand) {
|
|||
this.triggerValue = 0; // rolling average of trigger value
|
||||
this.rawTriggerValue = 0;
|
||||
this.rawBumperValue = 0;
|
||||
|
||||
|
||||
this.overlayLine = null;
|
||||
|
||||
this.offsetPosition = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
};
|
||||
this.offsetRotation = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
w: 1.0
|
||||
};
|
||||
|
||||
this.ignoreIK = false;
|
||||
this.offsetPosition = Vec3.ZERO;
|
||||
this.offsetRotation = Quat.IDENTITY;
|
||||
|
||||
var _this = this;
|
||||
|
||||
|
@ -658,6 +688,13 @@ function MyController(hand) {
|
|||
this.currentObjectTime = now;
|
||||
this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position);
|
||||
this.handPreviousRotation = handRotation;
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
|
||||
// compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object
|
||||
this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0);
|
||||
if (this.radiusScalar < 1.0) {
|
||||
this.radiusScalar = 1.0;
|
||||
}
|
||||
|
||||
this.actionID = NULL_ACTION_ID;
|
||||
this.actionID = Entities.addAction("spring", this.grabbedEntity, {
|
||||
|
@ -689,8 +726,6 @@ function MyController(hand) {
|
|||
this.currentAvatarOrientation = MyAvatar.orientation;
|
||||
|
||||
this.overlayLineOff();
|
||||
|
||||
|
||||
};
|
||||
|
||||
this.continueDistanceHolding = function() {
|
||||
|
@ -719,8 +754,12 @@ function MyController(hand) {
|
|||
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
||||
|
||||
// the action was set up on a previous call. update the targets.
|
||||
var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) *
|
||||
DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR);
|
||||
var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) *
|
||||
this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR;
|
||||
if (radius < 1.0) {
|
||||
radius = 1.0;
|
||||
}
|
||||
|
||||
// how far did avatar move this timestep?
|
||||
var currentPosition = MyAvatar.position;
|
||||
var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition);
|
||||
|
@ -751,11 +790,11 @@ function MyController(hand) {
|
|||
var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition);
|
||||
this.handRelativePreviousPosition = handToAvatar;
|
||||
|
||||
// magnify the hand movement but not the change from avatar movement & rotation
|
||||
// magnify the hand movement but not the change from avatar movement & rotation
|
||||
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
|
||||
var superHandMoved = Vec3.multiply(handMoved, radius);
|
||||
|
||||
// Move the object by the magnified amount and then by amount from avatar movement & rotation
|
||||
// Move the object by the magnified amount and then by amount from avatar movement & rotation
|
||||
var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved);
|
||||
newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition);
|
||||
newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning);
|
||||
|
@ -777,6 +816,16 @@ function MyController(hand) {
|
|||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab");
|
||||
|
||||
// mix in head motion
|
||||
if (MOVE_WITH_HEAD) {
|
||||
var objDistance = Vec3.length(objectToAvatar);
|
||||
var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance });
|
||||
var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance });
|
||||
var change = Vec3.subtract(before, after);
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change);
|
||||
}
|
||||
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: this.currentObjectPosition,
|
||||
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
|
@ -815,13 +864,12 @@ function MyController(hand) {
|
|||
|
||||
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
|
||||
// if an object is "equipped" and has a spatialKey, use it.
|
||||
if (grabbableData.spatialKey.relativePosition) {
|
||||
this.offsetPosition = grabbableData.spatialKey.relativePosition;
|
||||
}
|
||||
if (grabbableData.spatialKey.relativeRotation) {
|
||||
this.offsetRotation = grabbableData.spatialKey.relativeRotation;
|
||||
}
|
||||
this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
} else {
|
||||
this.ignoreIK = false;
|
||||
|
||||
var objectRotation = grabbedProperties.rotation;
|
||||
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
|
@ -838,7 +886,8 @@ function MyController(hand) {
|
|||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
|
@ -922,7 +971,8 @@ function MyController(hand) {
|
|||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
}
|
||||
|
@ -946,23 +996,9 @@ function MyController(hand) {
|
|||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
|
||||
// use a spring to pull the object to where it will be when equipped
|
||||
var relativeRotation = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
w: 1.0
|
||||
};
|
||||
var relativePosition = {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
};
|
||||
if (grabbableData.spatialKey.relativePosition) {
|
||||
relativePosition = grabbableData.spatialKey.relativePosition;
|
||||
}
|
||||
if (grabbableData.spatialKey.relativeRotation) {
|
||||
relativeRotation = grabbableData.spatialKey.relativeRotation;
|
||||
}
|
||||
var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
var targetRotation = Quat.multiply(handRotation, relativeRotation);
|
||||
|
@ -977,7 +1013,8 @@ function MyController(hand) {
|
|||
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
targetRotation: targetRotation,
|
||||
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
ttl: ACTION_TTL
|
||||
ttl: ACTION_TTL,
|
||||
ignoreIK: ignoreIK
|
||||
});
|
||||
if (this.equipSpringID === NULL_ACTION_ID) {
|
||||
this.equipSpringID = null;
|
||||
|
@ -990,7 +1027,8 @@ function MyController(hand) {
|
|||
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
targetRotation: targetRotation,
|
||||
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
ttl: ACTION_TTL
|
||||
ttl: ACTION_TTL,
|
||||
ignoreIK: ignoreIK
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ var leapHands = (function () {
|
|||
hasHandAndWristJoints,
|
||||
handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position
|
||||
HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle
|
||||
handAnimationStateHandlers,
|
||||
handAnimationStateFunctions,
|
||||
handAnimationStateProperties,
|
||||
hands,
|
||||
wrists,
|
||||
NUM_HANDS = 2, // 0 = left; 1 = right
|
||||
|
@ -37,7 +40,14 @@ var leapHands = (function () {
|
|||
avatarScale,
|
||||
avatarFaceModelURL,
|
||||
avatarSkeletonModelURL,
|
||||
settingsTimer;
|
||||
settingsTimer,
|
||||
HMD_CAMERA_TO_AVATAR_ROTATION = [
|
||||
Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }),
|
||||
Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 })
|
||||
],
|
||||
DESKTOP_CAMERA_TO_AVATAR_ROTATION =
|
||||
Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })),
|
||||
LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)];
|
||||
|
||||
function printSkeletonJointNames() {
|
||||
var jointNames,
|
||||
|
@ -125,6 +135,26 @@ var leapHands = (function () {
|
|||
*/
|
||||
}
|
||||
|
||||
function animateLeftHand() {
|
||||
var ROTATION_AND_POSITION = 0;
|
||||
|
||||
return {
|
||||
leftHandType: ROTATION_AND_POSITION,
|
||||
leftHandPosition: hands[0].position,
|
||||
leftHandRotation: hands[0].rotation
|
||||
};
|
||||
}
|
||||
|
||||
function animateRightHand() {
|
||||
var ROTATION_AND_POSITION = 0;
|
||||
|
||||
return {
|
||||
rightHandType: ROTATION_AND_POSITION,
|
||||
rightHandPosition: hands[1].position,
|
||||
rightHandRotation: hands[1].rotation
|
||||
};
|
||||
}
|
||||
|
||||
function finishCalibration() {
|
||||
var avatarPosition,
|
||||
handPosition,
|
||||
|
@ -166,10 +196,8 @@ var leapHands = (function () {
|
|||
|
||||
MyAvatar.clearJointData("LeftHand");
|
||||
MyAvatar.clearJointData("LeftForeArm");
|
||||
MyAvatar.clearJointData("LeftArm");
|
||||
MyAvatar.clearJointData("RightHand");
|
||||
MyAvatar.clearJointData("RightForeArm");
|
||||
MyAvatar.clearJointData("RightArm");
|
||||
|
||||
calibrationStatus = CALIBRATED;
|
||||
print("Leap Motion: Calibrated");
|
||||
|
@ -193,12 +221,10 @@ var leapHands = (function () {
|
|||
}
|
||||
|
||||
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
|
||||
MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
|
||||
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
|
||||
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0));
|
||||
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0));
|
||||
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0));
|
||||
|
||||
// Wait for arms to assume their positions before calculating
|
||||
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
|
||||
|
@ -319,6 +345,13 @@ var leapHands = (function () {
|
|||
]
|
||||
];
|
||||
|
||||
handAnimationStateHandlers = [null, null];
|
||||
handAnimationStateFunctions = [animateLeftHand, animateRightHand];
|
||||
handAnimationStateProperties = [
|
||||
["leftHandType", "leftHandPosition", "leftHandRotation"],
|
||||
["rightHandType", "rightHandPosition", "rightHandPosition"]
|
||||
];
|
||||
|
||||
setIsOnHMD();
|
||||
|
||||
settingsTimer = Script.setInterval(checkSettings, 2000);
|
||||
|
@ -348,6 +381,12 @@ var leapHands = (function () {
|
|||
return;
|
||||
}
|
||||
|
||||
// Hand animation handlers ...
|
||||
if (handAnimationStateHandlers[h] === null) {
|
||||
handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h],
|
||||
handAnimationStateProperties[h]);
|
||||
}
|
||||
|
||||
// Hand position ...
|
||||
handOffset = hands[h].controller.getAbsTranslation();
|
||||
handRotation = hands[h].controller.getAbsRotation();
|
||||
|
@ -362,37 +401,41 @@ var leapHands = (function () {
|
|||
|
||||
// Hand offset in camera coordinates ...
|
||||
handOffset = {
|
||||
x: hands[h].zeroPosition.x - handOffset.x,
|
||||
y: hands[h].zeroPosition.y - handOffset.z,
|
||||
z: hands[h].zeroPosition.z + handOffset.y
|
||||
x: -handOffset.x,
|
||||
y: -handOffset.z,
|
||||
z: -handOffset.y - hands[h].zeroPosition.z
|
||||
};
|
||||
handOffset.z = -handOffset.z;
|
||||
|
||||
// Hand offset in world coordinates ...
|
||||
cameraOrientation = Camera.getOrientation();
|
||||
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
|
||||
|
||||
// Hand offset in avatar coordinates ...
|
||||
// Hand offset in avatar coordinates ...
|
||||
inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation);
|
||||
handOffset = Vec3.subtract(handOffset, MyAvatar.position);
|
||||
handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset);
|
||||
handOffset.z = -handOffset.z;
|
||||
handOffset.x = -handOffset.x;
|
||||
|
||||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: -handRotation.x,
|
||||
x: -handRotation.y,
|
||||
y: -handRotation.z,
|
||||
z: -handRotation.y,
|
||||
z: -handRotation.x,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
|
||||
cameraOrientation.x = -cameraOrientation.x;
|
||||
cameraOrientation.z = -cameraOrientation.z;
|
||||
handRotation = Quat.multiply(cameraOrientation, handRotation);
|
||||
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
|
||||
handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation);
|
||||
cameraOrientation = {
|
||||
x: cameraOrientation.z,
|
||||
y: cameraOrientation.y,
|
||||
z: cameraOrientation.x,
|
||||
w: cameraOrientation.w
|
||||
};
|
||||
cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation));
|
||||
handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!!
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -411,18 +454,19 @@ var leapHands = (function () {
|
|||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: -handRotation.x,
|
||||
y: -handRotation.z,
|
||||
z: -handRotation.y,
|
||||
x: handRotation.z,
|
||||
y: handRotation.y,
|
||||
z: handRotation.x,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
|
||||
handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation);
|
||||
}
|
||||
|
||||
// Set hand position and orientation ...
|
||||
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
|
||||
// Set hand position and orientation for animation state handler ...
|
||||
hands[h].position = handOffset;
|
||||
hands[h].rotation = handRotation;
|
||||
|
||||
// Set finger joints ...
|
||||
for (i = 0; i < NUM_FINGERS; i += 1) {
|
||||
|
@ -436,6 +480,10 @@ var leapHands = (function () {
|
|||
z: side * -locRotation.x,
|
||||
w: locRotation.w
|
||||
};
|
||||
if (j === 0) {
|
||||
// Adjust avatar thumb root joint rotation to make avatar hands look better
|
||||
locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation);
|
||||
}
|
||||
} else {
|
||||
locRotation = {
|
||||
x: -locRotation.x,
|
||||
|
@ -458,14 +506,9 @@ var leapHands = (function () {
|
|||
hands[h].inactiveCount += 1;
|
||||
|
||||
if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) {
|
||||
if (h === 0) {
|
||||
MyAvatar.clearJointData("LeftHand");
|
||||
MyAvatar.clearJointData("LeftForeArm");
|
||||
MyAvatar.clearJointData("LeftArm");
|
||||
} else {
|
||||
MyAvatar.clearJointData("RightHand");
|
||||
MyAvatar.clearJointData("RightForeArm");
|
||||
MyAvatar.clearJointData("RightArm");
|
||||
if (handAnimationStateHandlers[h] !== null) {
|
||||
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
|
||||
handAnimationStateHandlers[h] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -483,6 +526,9 @@ var leapHands = (function () {
|
|||
for (h = 0; h < NUM_HANDS; h += 1) {
|
||||
Controller.releaseInputController(hands[h].controller);
|
||||
Controller.releaseInputController(wrists[h].controller);
|
||||
if (handAnimationStateHandlers[h] !== null) {
|
||||
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
|
||||
}
|
||||
for (i = 0; i < NUM_FINGERS; i += 1) {
|
||||
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
|
||||
if (fingers[h][i][j].controller !== null) {
|
||||
|
|
226
examples/data_visualization/earthquakes_live.js
Normal file
226
examples/data_visualization/earthquakes_live.js
Normal file
|
@ -0,0 +1,226 @@
|
|||
// earthquakes_live.js
|
||||
//
|
||||
// exploratory implementation in prep for abstract latlong to earth graphing tool for VR
|
||||
// shows all of the quakes in the past 24 hours reported by the USGS
|
||||
//
|
||||
// created by james b. pollack @imgntn on 12/5/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
|
||||
//
|
||||
// working notes: maybe try doing markers as boxes,rotated to the sphere normal, and with the height representing some value
|
||||
|
||||
Script.include('../libraries/promise.js');
|
||||
var Promise = loadPromise();
|
||||
|
||||
Script.include('../libraries/tinyColor.js');
|
||||
var tinyColor = loadTinyColor();
|
||||
|
||||
//you could make it the size of the actual earth.
|
||||
var EARTH_SPHERE_RADIUS = 6371;
|
||||
var EARTH_SPHERE_RADIUS = 2;
|
||||
|
||||
var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(EARTH_SPHERE_RADIUS, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx';
|
||||
|
||||
var SHOULD_SPIN=false;
|
||||
var POLL_FOR_CHANGES = true;
|
||||
//USGS updates the data every five minutes
|
||||
var CHECK_QUAKE_FREQUENCY = 5 * 60 * 1000;
|
||||
|
||||
var QUAKE_MARKER_DIMENSIONS = {
|
||||
x: 0.01,
|
||||
y: 0.01,
|
||||
z: 0.01
|
||||
};
|
||||
|
||||
function createEarth() {
|
||||
var earthProperties = {
|
||||
name: 'Earth',
|
||||
type: 'Model',
|
||||
modelURL: EARTH_MODEL_URL,
|
||||
position: EARTH_CENTER_POSITION,
|
||||
dimensions: {
|
||||
x: EARTH_SPHERE_RADIUS,
|
||||
y: EARTH_SPHERE_RADIUS,
|
||||
z: EARTH_SPHERE_RADIUS
|
||||
},
|
||||
rotation: Quat.fromPitchYawRollDegrees(0, 90, 0),
|
||||
// collisionsWillMove: true,
|
||||
//if you have a shapetype it blocks the smaller markers
|
||||
// shapeType:'sphere'
|
||||
// userData: JSON.stringify({
|
||||
// grabbableKey: {
|
||||
// grabbable: false
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
return Entities.addEntity(earthProperties)
|
||||
}
|
||||
|
||||
function latLongToVector3(lat, lon, radius, height) {
|
||||
var phi = (lat) * Math.PI / 180;
|
||||
var theta = (lon - 180) * Math.PI / 180;
|
||||
|
||||
var x = -(radius + height) * Math.cos(phi) * Math.cos(theta);
|
||||
var y = (radius + height) * Math.sin(phi);
|
||||
var z = (radius + height) * Math.cos(phi) * Math.sin(theta);
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
z: z
|
||||
};
|
||||
}
|
||||
|
||||
function getQuakePosition(earthquake) {
|
||||
var longitude = earthquake.geometry.coordinates[0];
|
||||
var latitude = earthquake.geometry.coordinates[1];
|
||||
var depth = earthquake.geometry.coordinates[2];
|
||||
|
||||
var latlng = latLongToVector3(latitude, longitude, EARTH_SPHERE_RADIUS / 2, 0);
|
||||
|
||||
var position = EARTH_CENTER_POSITION;
|
||||
var finalPosition = Vec3.sum(position, latlng);
|
||||
|
||||
//print('finalpos::' + JSON.stringify(finalPosition))
|
||||
return finalPosition
|
||||
}
|
||||
|
||||
var QUAKE_URL = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson'
|
||||
|
||||
function get(url) {
|
||||
print('getting' + url)
|
||||
// Return a new promise.
|
||||
return new Promise(function(resolve, reject) {
|
||||
// Do the usual XHR stuff
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', url);
|
||||
req.onreadystatechange = function() {
|
||||
print('req status:: ' + JSON.stringify(req.status))
|
||||
|
||||
if (req.readyState == 4 && req.status == 200) {
|
||||
var myArr = JSON.parse(req.responseText);
|
||||
resolve(myArr);
|
||||
}
|
||||
};
|
||||
|
||||
req.send();
|
||||
});
|
||||
}
|
||||
|
||||
function createQuakeMarker(earthquake) {
|
||||
var markerProperties = {
|
||||
name: earthquake.properties.place,
|
||||
type: 'Sphere',
|
||||
parentID:earth,
|
||||
dimensions: QUAKE_MARKER_DIMENSIONS,
|
||||
position: getQuakePosition(earthquake),
|
||||
ignoreForCollisions:true,
|
||||
lifetime: 6000,
|
||||
color: getQuakeMarkerColor(earthquake)
|
||||
}
|
||||
|
||||
// print('marker properties::' + JSON.stringify(markerProperties))
|
||||
return Entities.addEntity(markerProperties);
|
||||
}
|
||||
|
||||
function getQuakeMarkerColor(earthquake) {
|
||||
var color = {};
|
||||
var magnitude = earthquake.properties.mag;
|
||||
//realistic but will never get full red coloring and will probably be pretty dull for most. must experiment
|
||||
var sValue = scale(magnitude, 0, 10, 0, 100);
|
||||
var HSL_string = "hsl(0, " + sValue + "%, 50%)"
|
||||
var color = tinyColor(HSL_string);
|
||||
var finalColor = {
|
||||
red: color._r,
|
||||
green: color._g,
|
||||
blue: color._b
|
||||
}
|
||||
|
||||
return finalColor
|
||||
}
|
||||
|
||||
function scale(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
function processQuakes(earthquakes) {
|
||||
print('quakers length' + earthquakes.length)
|
||||
earthquakes.forEach(function(quake) {
|
||||
// print('PROCESSING A QUAKE')
|
||||
var marker = createQuakeMarker(quake);
|
||||
markers.push(marker);
|
||||
})
|
||||
print('markers length:' + markers.length)
|
||||
}
|
||||
|
||||
var quakes;
|
||||
var markers = [];
|
||||
|
||||
var earth = createEarth();
|
||||
|
||||
function getThenProcessQuakes() {
|
||||
get(QUAKE_URL).then(function(response) {
|
||||
print('got it::' + response.features.length)
|
||||
quakes = response.features;
|
||||
processQuakes(quakes);
|
||||
//print("Success!" + JSON.stringify(response));
|
||||
}, function(error) {
|
||||
print('error getting quakes')
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupMarkers() {
|
||||
print('CLEANING UP MARKERS')
|
||||
while (markers.length > 0) {
|
||||
Entities.deleteEntity(markers.pop());
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupEarth() {
|
||||
Entities.deleteEntity(earth);
|
||||
Script.update.disconnect(spinEarth);
|
||||
}
|
||||
|
||||
function cleanupInterval() {
|
||||
if (pollingInterval !== null) {
|
||||
Script.clearInterval(pollingInterval)
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanupMarkers);
|
||||
Script.scriptEnding.connect(cleanupEarth);
|
||||
Script.scriptEnding.connect(cleanupInterval);
|
||||
|
||||
getThenProcessQuakes();
|
||||
|
||||
var pollingInterval = null;
|
||||
|
||||
if (POLL_FOR_CHANGES === true) {
|
||||
pollingInterval = Script.setInterval(function() {
|
||||
cleanupMarkers();
|
||||
getThenProcessQuakes()
|
||||
}, CHECK_QUAKE_FREQUENCY)
|
||||
}
|
||||
|
||||
|
||||
function spinEarth(){
|
||||
Entities.editEntity(earth,{
|
||||
angularVelocity:{
|
||||
x:0,
|
||||
y:0.25,
|
||||
z:0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(SHOULD_SPIN===true){
|
||||
Script.update.connect(spinEarth);
|
||||
}
|
55
examples/drylake/createAvatarDetector.js
Normal file
55
examples/drylake/createAvatarDetector.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// createAvatarDetector.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 12/7/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script if you want the rats to run away from you.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var avatarDetector = null;
|
||||
|
||||
function createAvatarDetector() {
|
||||
|
||||
var detectorProperties = {
|
||||
name: 'Hifi-Avatar-Detector',
|
||||
type: 'Box',
|
||||
position: MyAvatar.position,
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 1
|
||||
},
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
visible: false,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
}
|
||||
}
|
||||
|
||||
avatarDetector = Entities.addEntity(detectorProperties);
|
||||
};
|
||||
|
||||
var updateAvatarDetector = function() {
|
||||
// print('updating detector position' + JSON.stringify(MyAvatar.position))
|
||||
Entities.editEntity(avatarDetector, {
|
||||
position: MyAvatar.position
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var cleanup = function() {
|
||||
Script.update.disconnect(updateAvatarDetector);
|
||||
Entities.deleteEntity(avatarDetector);
|
||||
}
|
||||
|
||||
createAvatarDetector();
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(updateAvatarDetector);
|
471
examples/drylake/ratCreator.js
Normal file
471
examples/drylake/ratCreator.js
Normal file
|
@ -0,0 +1,471 @@
|
|||
//
|
||||
// ratCreator.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 12/7/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script spawns some rats that have simple steering behaviors applied to them.
|
||||
// Run it in the 'drylake' environment, or adjust all object locations to match your scene.
|
||||
//
|
||||
// Steering bevhaviors from ratSteer.js:
|
||||
// The rats will move from a spawning point toward their nest.
|
||||
// They will avoid avoider blocks moving across the alley
|
||||
// They will avoid avatars running createAvatarDetector.js
|
||||
//
|
||||
// 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('ratSteer.js');
|
||||
var steer = loadSteer();
|
||||
Script.include('../libraries/tween.js');
|
||||
var TWEEN = loadTween();
|
||||
|
||||
var USE_CONSTANT_SPAWNER = true;
|
||||
|
||||
var RAT_SPAWNER_LOCATION = {
|
||||
x: 1000.5,
|
||||
y: 98,
|
||||
z: 1040
|
||||
};
|
||||
|
||||
var RAT_NEST_LOCATION = {
|
||||
x: 1003.5,
|
||||
y: 99,
|
||||
z: 964.2
|
||||
};
|
||||
|
||||
var RAT_DIMENSIONS = {
|
||||
x: 0.22,
|
||||
y: 0.32,
|
||||
z: 1.14
|
||||
};
|
||||
|
||||
var RAT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/models/rat_model.fbx';
|
||||
var RAT_IDLE_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/idle.fbx';
|
||||
var RAT_WALKING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/walk.fbx';
|
||||
var RAT_RUNNING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/run.fbx';
|
||||
var RAT_DEATH_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/death.fbx';
|
||||
|
||||
var RAT_IN_NEST_DISTANCE = 4;
|
||||
|
||||
//how many milliseconds between rats
|
||||
var RAT_SPAWN_RATE = 2500;
|
||||
|
||||
var RAT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Rats_Squeaks_Active.wav';
|
||||
var ratRunningSound = SoundCache.getSound(RAT_SOUND_URL);
|
||||
|
||||
function playRatRunningAnimation(rat) {
|
||||
var animationSettings = JSON.stringify({
|
||||
running: true
|
||||
});
|
||||
Entities.editEntity(rat, {
|
||||
animationURL: RAT_RUNNING_ANIMATION_URL,
|
||||
animation: {
|
||||
url: RAT_RUNNING_ANIMATION_URL,
|
||||
running: true,
|
||||
fps: 30
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function playRatDeathAnimation(rat) {
|
||||
var animationSettings = JSON.stringify({
|
||||
running: true
|
||||
});
|
||||
|
||||
Entities.editEntity(rat, {
|
||||
animationURL: RAT_DEATH_ANIMATION_URL,
|
||||
animationSettings: animationSettings
|
||||
});
|
||||
}
|
||||
|
||||
var modelRatProperties = {
|
||||
name: 'rat',
|
||||
type: 'Model',
|
||||
modelURL: RAT_MODEL_URL,
|
||||
dimensions: RAT_DIMENSIONS,
|
||||
position: RAT_SPAWNER_LOCATION,
|
||||
shapeType: 'Box',
|
||||
damping: 0.8,
|
||||
angularDamping: 0.99,
|
||||
friction: 0.75,
|
||||
collisionsWillMove: true,
|
||||
ignoreForCollisions: false,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
lifetime: 30,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
var targetProperties = {
|
||||
name: 'Hifi-Rat-Nest',
|
||||
type: 'Box',
|
||||
color: {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
},
|
||||
visible: false,
|
||||
position: RAT_NEST_LOCATION
|
||||
};
|
||||
|
||||
var target = Entities.addEntity(targetProperties);
|
||||
|
||||
function addRat() {
|
||||
var rat = Entities.addEntity(modelRatProperties);
|
||||
return rat;
|
||||
}
|
||||
|
||||
//every sixth rat will play a sound
|
||||
var RAT_SOUND_RATE = 6;
|
||||
|
||||
//spawn rate will be multiplied by this to clear any sounds hanging around
|
||||
var RAT_SOUND_CLEAR_RATE = 3;
|
||||
|
||||
var rats = [];
|
||||
var metaRats = [];
|
||||
var ratCount = 0;
|
||||
|
||||
var AVOIDER_Y_HEIGHT = 99;
|
||||
var FIRST_AVOIDER_START_POSITION = {
|
||||
x: 1004,
|
||||
y: AVOIDER_Y_HEIGHT,
|
||||
z: 1019
|
||||
};
|
||||
var FIRST_AVOIDER_FINISH_POSITION = {
|
||||
x: 997,
|
||||
y: AVOIDER_Y_HEIGHT,
|
||||
z: 1019
|
||||
};
|
||||
var SECOND_AVOIDER_START_POSITION = {
|
||||
x: 998,
|
||||
y: AVOIDER_Y_HEIGHT,
|
||||
z: 998
|
||||
};
|
||||
var SECOND_AVOIDER_FINISH_POSITION = {
|
||||
x: 1005,
|
||||
y: AVOIDER_Y_HEIGHT,
|
||||
z: 999
|
||||
};
|
||||
var THIRD_AVOIDER_START_POSITION = {
|
||||
x: 1001.5,
|
||||
y: 100,
|
||||
z: 978
|
||||
};
|
||||
var THIRD_AVOIDER_FINISH_POSITION = {
|
||||
x: 1005,
|
||||
y: 100,
|
||||
z: 974
|
||||
};
|
||||
|
||||
cleanupLeftoverAvoidersBeforeStart();
|
||||
|
||||
var avoiders = [];
|
||||
addAvoiderBlock(FIRST_AVOIDER_START_POSITION);
|
||||
addAvoiderBlock(SECOND_AVOIDER_START_POSITION);
|
||||
addAvoiderBlock(THIRD_AVOIDER_START_POSITION);
|
||||
|
||||
function addAvoiderBlock(position) {
|
||||
var avoiderProperties = {
|
||||
name: 'Hifi-Rat-Avoider',
|
||||
type: 'Box',
|
||||
color: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 255
|
||||
},
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
},
|
||||
position: position,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
visible: false
|
||||
};
|
||||
|
||||
var avoider = Entities.addEntity(avoiderProperties);
|
||||
avoiders.push(avoider);
|
||||
}
|
||||
|
||||
|
||||
tweenAvoider(avoiders[0], FIRST_AVOIDER_START_POSITION, FIRST_AVOIDER_FINISH_POSITION);
|
||||
tweenAvoider(avoiders[1], SECOND_AVOIDER_START_POSITION, SECOND_AVOIDER_FINISH_POSITION);
|
||||
tweenAvoider(avoiders[2], THIRD_AVOIDER_START_POSITION, THIRD_AVOIDER_FINISH_POSITION);
|
||||
|
||||
function tweenAvoider(entityID, startPosition, endPosition) {
|
||||
var ANIMATION_DURATION = 4200;
|
||||
|
||||
var begin = {
|
||||
x: startPosition.x,
|
||||
y: startPosition.y,
|
||||
z: startPosition.z
|
||||
};
|
||||
|
||||
var end = endPosition;
|
||||
|
||||
var original = startPosition;
|
||||
|
||||
var tweenHead = new TWEEN.Tween(begin).to(end, ANIMATION_DURATION);
|
||||
|
||||
function updateTo() {
|
||||
Entities.editEntity(entityID, {
|
||||
position: {
|
||||
x: begin.x,
|
||||
y: begin.y,
|
||||
z: begin.z
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateBack() {
|
||||
Entities.editEntity(entityID, {
|
||||
position: {
|
||||
x: begin.x,
|
||||
y: begin.y,
|
||||
z: begin.z
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var tweenBack = new TWEEN.Tween(begin).to(original, ANIMATION_DURATION).onUpdate(updateBack);
|
||||
|
||||
tweenHead.onUpdate(function() {
|
||||
updateTo()
|
||||
});
|
||||
|
||||
tweenHead.chain(tweenBack);
|
||||
tweenBack.chain(tweenHead);
|
||||
tweenHead.start();
|
||||
|
||||
}
|
||||
|
||||
function updateTweens() {
|
||||
TWEEN.update();
|
||||
}
|
||||
|
||||
function createRatSoundInjector() {
|
||||
var audioOptions = {
|
||||
volume: 0.05,
|
||||
loop: false
|
||||
};
|
||||
|
||||
var injector = Audio.playSound(ratRunningSound, audioOptions);
|
||||
|
||||
return injector;
|
||||
}
|
||||
|
||||
function moveRats() {
|
||||
rats.forEach(function(rat) {
|
||||
|
||||
//remove the rat if its near the nest
|
||||
checkDistanceFromNest(rat);
|
||||
|
||||
//see if there are avatars to run from
|
||||
var avatarFlightVectors = steer.fleeAllAvatars(rat);
|
||||
var averageAvatarFlight;
|
||||
var i;
|
||||
for (i = 0; i < avatarFlightVectors.length; i++) {
|
||||
if (i === 0) {
|
||||
averageAvatarFlight = avatarFlightVectors[0];
|
||||
} else {
|
||||
averageAvatarFlight = Vec3.sum(avatarFlightVectors[i - 1], avatarFlightVectors[i]);
|
||||
}
|
||||
}
|
||||
averageAvatarFlight = Vec3.multiply(averageAvatarFlight, 1 / avatarFlightVectors.length);
|
||||
|
||||
//see if there are avoiders to flee
|
||||
var avoidBlockVectors = steer.fleeAvoiderBlocks(rat);
|
||||
var averageAvoiderFlight;
|
||||
var j;
|
||||
for (j = 0; j < avoidBlockVectors.length; j++) {
|
||||
if (j === 0) {
|
||||
averageAvoiderFlight = avoidBlockVectors[0];
|
||||
} else {
|
||||
averageAvoiderFlight = Vec3.sum(avoidBlockVectors[j - 1], avoidBlockVectors[j]);
|
||||
}
|
||||
};
|
||||
averageAvoiderFlight = Vec3.multiply(averageAvoiderFlight, 1 / avoidBlockVectors.length);
|
||||
|
||||
//add all of the vectors and divide them by total to get average vector
|
||||
//start by trying to go toward the nest
|
||||
var seek = steer.arrive(rat, target);
|
||||
var averageVector = seek;
|
||||
var divisorCount = 1;
|
||||
|
||||
//if there are avatars to run away from
|
||||
if (avatarFlightVectors.length > 0) {
|
||||
divisorCount++;
|
||||
averageVector = Vec3.sum(averageVector, averageAvatarFlight);
|
||||
}
|
||||
|
||||
//or if there are avoider blocks to run away from
|
||||
if (avoidBlockVectors.length > 0) {
|
||||
divisorCount++;
|
||||
averageVector = Vec3.sum(averageVector, averageAvoiderFlight);
|
||||
}
|
||||
|
||||
averageVector = Vec3.multiply(averageVector, 1 / divisorCount);
|
||||
|
||||
var thisRatProps = Entities.getEntityProperties(rat, ["position", "rotation"]);
|
||||
var ratPosition = thisRatProps.position;
|
||||
var ratToNest = Vec3.subtract(RAT_NEST_LOCATION, ratPosition);
|
||||
var ratRotation = Quat.rotationBetween(Vec3.UNIT_Z, ratToNest);
|
||||
var eulerAngle = Quat.safeEulerAngles(ratRotation);
|
||||
eulerAngle.x = 0;
|
||||
eulerAngle.z = 0;
|
||||
var constrainedRotation = Quat.fromVec3Degrees(eulerAngle);
|
||||
|
||||
Entities.editEntity(rat, {
|
||||
velocity: averageVector,
|
||||
rotation: constrainedRotation,
|
||||
});
|
||||
|
||||
//have to make a 'meta' rat object to keep track of rats for updating sound injector locations. parenting sounds would make this easy.
|
||||
var metaRat = getMetaRatByRat(rat);
|
||||
if (metaRat !== undefined) {
|
||||
if (metaRat.injector !== undefined) {
|
||||
if (metaRat.injector.isPlaying === true) {
|
||||
metaRat.injector.options = {
|
||||
loop: true,
|
||||
position: ratPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Script.update.connect(moveRats)
|
||||
Script.update.connect(updateTweens);
|
||||
|
||||
function checkDistanceFromNest(rat) {
|
||||
var ratProps = Entities.getEntityProperties(rat, "position");
|
||||
var distance = Vec3.distance(ratProps.position, RAT_NEST_LOCATION);
|
||||
if (distance < RAT_IN_NEST_DISTANCE) {
|
||||
//at nest
|
||||
removeRatFromScene(rat);
|
||||
}
|
||||
}
|
||||
|
||||
function removeRatFromScene(rat) {
|
||||
|
||||
var index = rats.indexOf(rat);
|
||||
if (index > -1) {
|
||||
rats.splice(index, 1);
|
||||
Entities.deleteEntity(rat);
|
||||
|
||||
}
|
||||
|
||||
var metaRatIndex = findWithAttr(metaRats, 'rat', rat);
|
||||
if (metaRatIndex > -1) {
|
||||
metaRats[index].injector.stop();
|
||||
metaRats.splice(index, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function popRatFromStack(entityID) {
|
||||
|
||||
var index = rats.indexOf(entityID);
|
||||
if (index > -1) {
|
||||
rats.splice(index, 1);
|
||||
}
|
||||
|
||||
var metaRatIndex = findWithAttr(metaRats, 'rat', entityID);
|
||||
if (metaRatIndex > -1) {
|
||||
metaRats[index].injector.stop();
|
||||
metaRats.splice(index, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function findWithAttr(array, attr, value) {
|
||||
for (var i = 0; i < array.length; i += 1) {
|
||||
if (array[i][attr] === value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMetaRatByRat(rat) {
|
||||
var result = metaRats.filter(function(metaRat) {
|
||||
return rat === metaRat.rat;
|
||||
});
|
||||
return result[0];
|
||||
}
|
||||
|
||||
Entities.deletingEntity.connect(popRatFromStack);
|
||||
|
||||
|
||||
function cleanupLeftoverAvoidersBeforeStart() {
|
||||
//sometimes if we crash or something there could be extra avoider blocks around. clear them out.
|
||||
var nearbyEntities = Entities.findEntities(RAT_SPAWNER_LOCATION, 100);
|
||||
var entityIndex;
|
||||
for (entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
|
||||
var entityID = nearbyEntities[entityIndex];
|
||||
var entityProps = Entities.getEntityProperties(entityID);
|
||||
if (entityProps.name === 'Hifi-Rat-Avoider') {
|
||||
Entities.deleteEntity(entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
while (rats.length > 0) {
|
||||
Entities.deleteEntity(rats.pop());
|
||||
}
|
||||
|
||||
while (avoiders.length > 0) {
|
||||
Entities.deleteEntity(avoiders.pop());
|
||||
}
|
||||
|
||||
Entities.deleteEntity(target);
|
||||
Script.update.disconnect(moveRats);
|
||||
Script.update.disconnect(updateTweens);
|
||||
Entities.deletingEntity.disconnect(popRatFromStack);
|
||||
Script.clearInterval(ratSpawnerInterval);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
var ratSpawnerInterval;
|
||||
|
||||
if (USE_CONSTANT_SPAWNER === true) {
|
||||
ratSpawnerInterval = Script.setInterval(function() {
|
||||
var rat = addRat();
|
||||
playRatRunningAnimation(rat);
|
||||
rats.push(rat);
|
||||
ratCount++;
|
||||
if (ratCount % RAT_SOUND_RATE === 0) {
|
||||
var metaRat = {
|
||||
rat: rat,
|
||||
injector: createRatSoundInjector()
|
||||
}
|
||||
metaRats.push(metaRat);
|
||||
|
||||
Script.setTimeout(function() {
|
||||
//if we have too many injectors hanging around there are problems
|
||||
metaRat.injector.stop();
|
||||
delete metaRat.injector;
|
||||
}, RAT_SPAWN_RATE * RAT_SOUND_CLEAR_RATE;
|
||||
}
|
||||
|
||||
}, RAT_SPAWN_RATE);
|
||||
}
|
183
examples/drylake/ratSteer.js
Normal file
183
examples/drylake/ratSteer.js
Normal file
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// ratSteer.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 12/7/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example of steering behaviors that can be applied entities.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function flee(thisEntity, target) {
|
||||
var targetPosition = Entities.getEntityProperties(target, "position").position;
|
||||
var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]);
|
||||
var location = properties.position;
|
||||
var velocity = properties.velocity;
|
||||
var MAX_SPEED = 1;
|
||||
var MAX_FORCE = 1;
|
||||
var FLEE_RANGE = 2;
|
||||
var desired = Vec3.subtract(location, targetPosition);
|
||||
var d = Vec3.length(desired);
|
||||
desired = Vec3.normalize(desired);
|
||||
desired = Vec3.multiply(MAX_SPEED, desired);
|
||||
if (d < FLEE_RANGE) {
|
||||
var steer = Vec3.subtract(desired, velocity);
|
||||
|
||||
var steerVector = new V3(desired.x, 0, desired.z);
|
||||
steer = steerVector.limit(MAX_FORCE);
|
||||
|
||||
return steer;
|
||||
} else {
|
||||
//target too far away to flee
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function fleeAllAvatars(thisEntity) {
|
||||
//print('FLEE AVATARS');
|
||||
var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]);
|
||||
var location = properties.position;
|
||||
var velocity = properties.velocity;
|
||||
|
||||
var nearbyEntities = Entities.findEntities(location, 3);
|
||||
var flightVectors = [];
|
||||
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
|
||||
var entityID = nearbyEntities[entityIndex];
|
||||
var entityProps = Entities.getEntityProperties(entityID);
|
||||
if (entityProps.name === 'Hifi-Avatar-Detector') {
|
||||
//found an avatar to flee
|
||||
|
||||
var MAX_SPEED = 8;
|
||||
var MAX_FORCE = 8;
|
||||
var FLEE_AVATAR_RANGE = 3;
|
||||
var desired = Vec3.subtract(location, entityProps.position);
|
||||
var d = Vec3.length(desired);
|
||||
desired = Vec3.normalize(desired);
|
||||
desired = Vec3.multiply(MAX_SPEED, desired);
|
||||
if (d < FLEE_AVATAR_RANGE) {
|
||||
var steer = Vec3.subtract(desired, velocity);
|
||||
var steerVector = new V3(desired.x, 0, desired.z);
|
||||
steer = steerVector.limit(MAX_FORCE);
|
||||
flightVectors.push(steer);
|
||||
} else {
|
||||
// target too far away from this avatar to flee
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return flightVectors;
|
||||
}
|
||||
|
||||
function fleeAvoiderBlocks(thisEntity) {
|
||||
// print('FLEE AVOIDER BLOCKS');
|
||||
var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]);
|
||||
var location = properties.position;
|
||||
var velocity = properties.velocity;
|
||||
|
||||
var nearbyEntities = Entities.findEntities(location, 2);
|
||||
var flightVectors = [];
|
||||
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
|
||||
var entityID = nearbyEntities[entityIndex];
|
||||
var entityProps = Entities.getEntityProperties(entityID);
|
||||
if (entityProps.name === 'Hifi-Rat-Avoider') {
|
||||
//found an avoiderblock to flee
|
||||
|
||||
var MAX_SPEED = 11;
|
||||
var MAX_FORCE = 6;
|
||||
var FLEE_AVOIDER_RANGE = 5;
|
||||
|
||||
var desired = Vec3.subtract(location, entityProps.position);
|
||||
var d = Vec3.length(desired);
|
||||
desired = Vec3.normalize(desired);
|
||||
desired = Vec3.multiply(MAX_SPEED, desired);
|
||||
|
||||
if (d < FLEE_AVOIDER_RANGE) {
|
||||
var steer = Vec3.subtract(desired, velocity);
|
||||
var steerVector = new V3(desired.x, 0, desired.z);
|
||||
steer = steerVector.limit(MAX_FORCE);
|
||||
flightVectors.push(steer);
|
||||
} else {
|
||||
//target too far away from this avoider to flee
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return flightVectors;
|
||||
}
|
||||
|
||||
function arrive(thisEntity, target) {
|
||||
|
||||
var targetPosition = Entities.getEntityProperties(target, "position").position;
|
||||
var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]);
|
||||
var location = properties.position;
|
||||
var velocity = properties.velocity;
|
||||
var MAX_SPEED = 10;
|
||||
var MAX_FORCE = 6;
|
||||
var ARRIVAL_DISTANCE = 2;
|
||||
|
||||
var desired = Vec3.subtract(targetPosition, location);
|
||||
var d = Vec3.length(desired);
|
||||
desired = Vec3.normalize(desired);
|
||||
|
||||
if (d < ARRIVAL_DISTANCE) {
|
||||
var m = scale(d, 0, ARRIVAL_DISTANCE, 0, MAX_SPEED);
|
||||
} else {
|
||||
desired = Vec3.multiply(MAX_SPEED, desired);
|
||||
|
||||
}
|
||||
|
||||
var steer = Vec3.subtract(desired, velocity);
|
||||
var steerVector = new V3(desired.x, 0, desired.z);
|
||||
steer = steerVector.limit(MAX_FORCE);
|
||||
|
||||
return steer;
|
||||
}
|
||||
|
||||
|
||||
function V3(x, y, z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return
|
||||
}
|
||||
|
||||
V3.prototype.length = function() {
|
||||
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
};
|
||||
|
||||
V3.prototype.limit = function(s) {
|
||||
var len = this.length();
|
||||
|
||||
if (len > s && len > 0) {
|
||||
this.scale(s / len);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
V3.prototype.scale = function(f) {
|
||||
this.x *= f;
|
||||
this.y *= f;
|
||||
this.z *= f;
|
||||
return this;
|
||||
};
|
||||
|
||||
var v3 = new V3();
|
||||
|
||||
var scale = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
loadSteer = function() {
|
||||
return {
|
||||
flee: flee,
|
||||
fleeAllAvatars: fleeAllAvatars,
|
||||
fleeAvoiderBlocks: fleeAvoiderBlocks,
|
||||
arrive: arrive
|
||||
};
|
||||
}
|
|
@ -392,6 +392,11 @@ var toolBar = (function() {
|
|||
url,
|
||||
file;
|
||||
|
||||
if (!event.isLeftButton) {
|
||||
// if another mouse button than left is pressed ignore it
|
||||
return false;
|
||||
}
|
||||
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
|
@ -997,7 +1002,8 @@ function setupModelMenus() {
|
|||
menuName: "Edit",
|
||||
menuItemName: "Models",
|
||||
isSeparator: true,
|
||||
beforeItem: "Physics"
|
||||
beforeItem: "Physics",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
if (!Menu.menuItemExists("Edit", "Delete")) {
|
||||
print("no delete... adding ours");
|
||||
|
@ -1007,7 +1013,8 @@ function setupModelMenus() {
|
|||
shortcutKeyEvent: {
|
||||
text: "backspace"
|
||||
},
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
modelMenuAddedDelete = true;
|
||||
} else {
|
||||
|
@ -1018,7 +1025,8 @@ function setupModelMenus() {
|
|||
menuName: "Edit",
|
||||
menuItemName: "Entity List...",
|
||||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
|
@ -1026,7 +1034,8 @@ function setupModelMenus() {
|
|||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Entity List...",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
isChecked: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
|
@ -1034,79 +1043,91 @@ function setupModelMenus() {
|
|||
shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Selecting of Large Models",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
isChecked: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Allow Selecting of Lights",
|
||||
shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models",
|
||||
isCheckable: true
|
||||
isCheckable: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities In Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+A",
|
||||
afterItem: "Allow Selecting of Lights"
|
||||
afterItem: "Allow Selecting of Lights",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities Touching Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+T",
|
||||
afterItem: "Select All Entities In Box"
|
||||
afterItem: "Select All Entities In Box",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Models",
|
||||
isSeparator: true,
|
||||
beforeItem: "Settings"
|
||||
beforeItem: "Settings",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Export Entities",
|
||||
shortcutKey: "CTRL+META+E",
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Import Entities",
|
||||
shortcutKey: "CTRL+META+I",
|
||||
afterItem: "Export Entities"
|
||||
afterItem: "Export Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Import Entities from URL",
|
||||
shortcutKey: "CTRL+META+U",
|
||||
afterItem: "Import Entities"
|
||||
afterItem: "Import Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_AUTO_FOCUS_ON_SELECT,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true"
|
||||
isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_EASE_ON_FOCUS,
|
||||
afterItem: MENU_AUTO_FOCUS_ON_SELECT,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true"
|
||||
isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
|
||||
afterItem: MENU_EASE_ON_FOCUS,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true"
|
||||
isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE,
|
||||
afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true"
|
||||
isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Entities.setLightsArePickable(false);
|
||||
|
@ -1133,10 +1154,10 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("File", "Import Entities");
|
||||
Menu.removeMenuItem("File", "Import Entities from URL");
|
||||
|
||||
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS);
|
||||
Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
|
|
|
@ -32,7 +32,7 @@ function randVector(a, b) {
|
|||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var NATURAL_SIZE_OF_BUTTERFLY = { x: 1.0, y: 0.4, z: 0.2 };
|
||||
var NATURAL_SIZE_OF_BUTTERFLY = { x:0.5, y: 0.2, z: 0.1 };
|
||||
|
||||
var lifeTime = 3600; // One hour lifespan
|
||||
var range = 7.0; // Over what distance in meters do you want the flock to fly around
|
||||
|
@ -65,8 +65,8 @@ function addButterfly() {
|
|||
var color = { red: 100, green: 100, blue: 100 };
|
||||
var size = 0;
|
||||
|
||||
var MINSIZE = 0.06;
|
||||
var RANGESIZE = 0.2;
|
||||
var MINSIZE = 0.01;
|
||||
var RANGESIZE = 0.05;
|
||||
var maxSize = MINSIZE + RANGESIZE;
|
||||
|
||||
size = MINSIZE + Math.random() * RANGESIZE;
|
||||
|
@ -74,7 +74,7 @@ function addButterfly() {
|
|||
var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize));
|
||||
|
||||
var GRAVITY = -0.2;
|
||||
var newFrameRate = 20 + Math.random() * 30;
|
||||
var newFrameRate = 29 + Math.random() * 30;
|
||||
var properties = {
|
||||
type: "Model",
|
||||
lifetime: lifeTime,
|
||||
|
@ -86,17 +86,13 @@ function addButterfly() {
|
|||
dimensions: dimensions,
|
||||
color: color,
|
||||
animation: {
|
||||
url: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx",
|
||||
firstFrame: 0,
|
||||
url: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx",
|
||||
fps: newFrameRate,
|
||||
currentFrame: 0,
|
||||
hold: false,
|
||||
lastFrame: 10000,
|
||||
loop: true,
|
||||
running: true,
|
||||
startAutomatically:false
|
||||
},
|
||||
modelURL: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx"
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx"
|
||||
};
|
||||
butterflies.push(Entities.addEntity(properties));
|
||||
}
|
||||
|
|
|
@ -223,7 +223,10 @@
|
|||
var elDimensionsZ = document.getElementById("property-dim-z");
|
||||
var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
|
||||
var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
|
||||
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
|
||||
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
|
||||
|
||||
var elParentID = document.getElementById("property-parent-id");
|
||||
var elParentJointIndex = document.getElementById("property-parent-joint-index");
|
||||
|
||||
var elRegistrationX = document.getElementById("property-reg-x");
|
||||
var elRegistrationY = document.getElementById("property-reg-y");
|
||||
|
@ -295,7 +298,6 @@
|
|||
var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
|
||||
var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
|
||||
var elModelAnimationHold = document.getElementById("property-model-animation-hold");
|
||||
var elModelAnimationStartAutomatically = document.getElementById("property-model-animation-start-automatically");
|
||||
var elModelTextures = document.getElementById("property-model-textures");
|
||||
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
|
||||
|
||||
|
@ -453,6 +455,9 @@
|
|||
elDimensionsY.value = properties.dimensions.y.toFixed(2);
|
||||
elDimensionsZ.value = properties.dimensions.z.toFixed(2);
|
||||
|
||||
elParentID.value = properties.parentID;
|
||||
elParentJointIndex.value = properties.parentJointIndex;
|
||||
|
||||
elRegistrationX.value = properties.registrationPoint.x.toFixed(2);
|
||||
elRegistrationY.value = properties.registrationPoint.y.toFixed(2);
|
||||
elRegistrationZ.value = properties.registrationPoint.z.toFixed(2);
|
||||
|
@ -526,7 +531,6 @@
|
|||
elModelAnimationLastFrame.value = properties.animation.lastFrame;
|
||||
elModelAnimationLoop.checked = properties.animation.loop;
|
||||
elModelAnimationHold.checked = properties.animation.hold;
|
||||
elModelAnimationStartAutomatically.checked = properties.animation.startAutomatically;
|
||||
elModelTextures.value = properties.textures;
|
||||
elModelOriginalTextures.value = properties.originalTextures;
|
||||
} else if (properties.type == "Web") {
|
||||
|
@ -666,6 +670,9 @@
|
|||
elDimensionsY.addEventListener('change', dimensionsChangeFunction);
|
||||
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
|
||||
|
||||
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
|
||||
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
|
||||
|
||||
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
|
||||
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
|
||||
elRegistrationX.addEventListener('change', registrationChangeFunction);
|
||||
|
@ -776,7 +783,6 @@
|
|||
elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
|
||||
elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
|
||||
elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
|
||||
elModelAnimationStartAutomatically.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'startAutomatically'));
|
||||
|
||||
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
|
||||
|
||||
|
@ -1055,6 +1061,19 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label" style="float: left; margin-right: 6px">ParentID</span>
|
||||
<div class="value" style="overflow: hidden;">
|
||||
<input type="text" id="property-parent-id">
|
||||
</div>
|
||||
</div>
|
||||
<div class="property">
|
||||
<span class="label" style="float: left; margin-right: 6px">ParentJointIndex</span>
|
||||
<div class="value" style="overflow: hidden;">
|
||||
<input type="text" id="property-parent-joint-index">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Registration</div>
|
||||
<div class="value">
|
||||
|
@ -1326,12 +1345,6 @@
|
|||
<input type='checkbox' id="property-model-animation-hold">
|
||||
</span>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<span class="label">Animation Start Automatically</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-model-animation-start-automatically">
|
||||
</span>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<div class="label">Textures</div>
|
||||
<div class="value">
|
||||
|
|
|
@ -2340,6 +2340,11 @@ SelectionDisplay = (function () {
|
|||
|
||||
that.mousePressEvent = function(event) {
|
||||
|
||||
if (!event.isLeftButton) {
|
||||
// if another mouse button than left is pressed ignore it
|
||||
return false;
|
||||
}
|
||||
|
||||
var somethingClicked = false;
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
|
|
1155
examples/libraries/tinyColor.js
Normal file
1155
examples/libraries/tinyColor.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -74,7 +74,9 @@ var keysToIgnore = [
|
|||
'shapeType',
|
||||
'isEmitting',
|
||||
'sittingPoints',
|
||||
'originalTextures'
|
||||
'originalTextures',
|
||||
'parentJointIndex',
|
||||
'parentID'
|
||||
];
|
||||
|
||||
var individualKeys = [];
|
||||
|
|
|
@ -128,7 +128,6 @@ function setUp() {
|
|||
blue: 255
|
||||
},
|
||||
lifespan: 5.0,
|
||||
visible: false,
|
||||
locked: false,
|
||||
isEmitting: true,
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
|
|
|
@ -47,7 +47,12 @@ function makeBasketball() {
|
|||
modelURL: basketballURL,
|
||||
restitution: 1.0,
|
||||
damping: 0.00001,
|
||||
shapeType: "sphere"
|
||||
shapeType: "sphere",
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
originalPosition = position;
|
||||
}
|
||||
|
|
|
@ -66,16 +66,6 @@
|
|||
max2: 15
|
||||
}
|
||||
|
||||
var BOW_SPATIAL_KEY = {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0.06,
|
||||
z: 0.11
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
|
||||
}
|
||||
|
||||
|
||||
var USE_DEBOUNCE = false;
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
|
@ -163,11 +153,9 @@
|
|||
var handToDisable = this.initialHand === 'right' ? 'left' : 'right';
|
||||
Messages.sendMessage('Hifi-Hand-Disabler', handToDisable);
|
||||
|
||||
setEntityCustomData('grabbableKey', this.entityID, {
|
||||
grabbable: false,
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: BOW_SPATIAL_KEY
|
||||
});
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = false;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
|
||||
},
|
||||
continueNearGrab: function() {
|
||||
|
@ -221,11 +209,10 @@
|
|||
this.isGrabbed = false;
|
||||
this.stringDrawn = false;
|
||||
this.deleteStrings();
|
||||
setEntityCustomData('grabbableKey', this.entityID, {
|
||||
grabbable: true,
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: BOW_SPATIAL_KEY
|
||||
});
|
||||
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = true;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
Entities.deleteEntity(this.preNotchString);
|
||||
Entities.deleteEntity(this.arrow);
|
||||
this.aiming = false;
|
||||
|
|
|
@ -48,12 +48,17 @@ var bow = Entities.addEntity({
|
|||
grabbableKey: {
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0.06,
|
||||
z: 0.11
|
||||
leftRelativePosition: {
|
||||
x: -0.02,
|
||||
y: 0.08,
|
||||
z: 0.09
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, 90)
|
||||
relativePosition: {
|
||||
x: 0.02,
|
||||
y: 0.08,
|
||||
z: 0.09
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -53,8 +53,13 @@ var wand = Entities.addEntity({
|
|||
y: 0.1,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 90)
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, -90)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function scriptEnding() {
|
||||
Entities.deleteEntity(wand);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -15,12 +15,20 @@ function createDoll() {
|
|||
|
||||
var scriptURL = Script.resolvePath("doll.js");
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var naturalDimensions = { x: 1.63, y: 1.67, z: 0.26};
|
||||
var naturalDimensions = {
|
||||
x: 1.63,
|
||||
y: 1.67,
|
||||
z: 0.26
|
||||
};
|
||||
|
||||
var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15);
|
||||
|
||||
|
||||
var doll = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "doll",
|
||||
|
@ -39,7 +47,12 @@ function createDoll() {
|
|||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
collisionsWillMove: true
|
||||
collisionsWillMove: true,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
return doll;
|
||||
}
|
||||
|
|
|
@ -18,14 +18,27 @@ var scriptURL = Script.resolvePath('flashlight.js');
|
|||
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: { x: 0.08, y: 0.30, z: 0.08},
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.30,
|
||||
z: 0.08
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
script: scriptURL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
|
@ -25,7 +25,7 @@
|
|||
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
|
||||
var startTime = Date.now();
|
||||
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
|
||||
var LIFETIME = 25;
|
||||
var LIFETIME = 100;
|
||||
var MSEC_PER_SEC = 1000.0;
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
|
@ -85,9 +85,14 @@
|
|||
this.hand = 'LEFT';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
startNearGrab: function(entityID) {
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
|
@ -97,6 +102,7 @@
|
|||
y: 2,
|
||||
z: 20
|
||||
},
|
||||
parentID: this.entityID,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
|
@ -105,7 +111,9 @@
|
|||
intensity: 2,
|
||||
exponent: 0.3,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME
|
||||
lifetime: LIFETIME,
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
});
|
||||
|
||||
//this light creates the effect of a bulb at the end of the flashlight
|
||||
|
@ -116,6 +124,7 @@
|
|||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
parentID: this.entityID,
|
||||
isSpotlight: false,
|
||||
color: {
|
||||
red: 255,
|
||||
|
@ -123,8 +132,11 @@
|
|||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
lifetime: LIFETIME,
|
||||
cutoff: 90, // in degrees
|
||||
lifetime: LIFETIME
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
|
||||
});
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
@ -142,7 +154,6 @@
|
|||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
} else {
|
||||
this.updateLightPositions();
|
||||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
|
@ -159,29 +170,7 @@
|
|||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateLightPositions: function() {
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
||||
//move the two lights along the vectors we set above
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
//move them with the entity model
|
||||
Entities.editEntity(this.spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
|
||||
if (flashLightHand === 'LEFT') {
|
||||
|
|
|
@ -35,7 +35,12 @@ var pingPongGun = Entities.addEntity({
|
|||
z: 0.47
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function cleanUp() {
|
||||
|
|
|
@ -28,10 +28,10 @@ var pistol = Entities.addEntity({
|
|||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
y: 0.05,
|
||||
z: -0.08
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(45, 90, 0)
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
|
|
|
@ -12,31 +12,57 @@
|
|||
Script.include("cookies.js");
|
||||
|
||||
var audioOptions = new AudioEffectOptions({
|
||||
maxRoomSize: 50,
|
||||
bandwidth: 10000,
|
||||
preDelay: 20,
|
||||
lateDelay: 0,
|
||||
reverbTime: 2,
|
||||
earlyDiffusion: 100,
|
||||
lateDiffusion: 100,
|
||||
roomSize: 50,
|
||||
reverbTime: 4,
|
||||
damping: 0.50,
|
||||
inputBandwidth: 0.8,
|
||||
earlyLevel: 0,
|
||||
tailLevel: 0,
|
||||
dryLevel: -6,
|
||||
wetLevel: -6
|
||||
density: 100,
|
||||
bassMult: 1.5,
|
||||
bassFreq: 250,
|
||||
highGain: -6,
|
||||
highFreq: 3000,
|
||||
modRate: 2.3,
|
||||
modDepth: 50,
|
||||
earlyGain: 0,
|
||||
lateGain: 0,
|
||||
earlyMixLeft: 20,
|
||||
earlyMixRight: 20,
|
||||
lateMixLeft: 90,
|
||||
lateMixRight: 90,
|
||||
wetDryMix: 50,
|
||||
});
|
||||
|
||||
AudioDevice.setReverbOptions(audioOptions);
|
||||
AudioDevice.setReverb(true);
|
||||
print("Reverb is ON.");
|
||||
|
||||
var panel = new Panel(10, 200);
|
||||
var panel = new Panel(10, 160);
|
||||
|
||||
var parameters = [
|
||||
{ name: "roomSize", min: 0, max: 100, units: " feet" },
|
||||
{ name: "reverbTime", min: 0, max: 10, units: " sec" },
|
||||
{ name: "damping", min: 0, max: 1, units: " " },
|
||||
{ name: "inputBandwidth", min: 0, max: 1, units: " " },
|
||||
{ name: "earlyLevel", min: -48, max: 0, units: " dB" },
|
||||
{ name: "tailLevel", min: -48, max: 0, units: " dB" },
|
||||
{ name: "wetLevel", min: -48, max: 0, units: " dB" },
|
||||
{ name: "bandwidth", min: 1000, max: 12000, units: " Hz" },
|
||||
{ name: "preDelay", min: 0, max: 333, units: " ms" },
|
||||
{ name: "lateDelay", min: 0, max: 166, units: " ms" },
|
||||
{ name: "reverbTime", min: 0.1, max: 10, units: " seconds" },
|
||||
{ name: "earlyDiffusion", min: 0, max: 100, units: " percent" },
|
||||
{ name: "lateDiffusion", min: 0, max: 100, units: " percent" },
|
||||
{ name: "roomSize", min: 0, max: 100, units: " percent" },
|
||||
{ name: "density", min: 0, max: 100, units: " percent" },
|
||||
{ name: "bassMult", min: 0.1, max: 4, units: " ratio" },
|
||||
{ name: "bassFreq", min: 10, max: 500, units: " Hz" },
|
||||
{ name: "highGain", min: -24, max: 0, units: " dB" },
|
||||
{ name: "highFreq", min: 1000, max: 12000, units: " Hz" },
|
||||
{ name: "modRate", min: 0.1, max: 10, units: " Hz" },
|
||||
{ name: "modDepth", min: 0, max: 100, units: " percent" },
|
||||
{ name: "earlyGain", min: -96, max: 24, units: " dB" },
|
||||
{ name: "lateGain", min: -96, max: 24, units: " dB" },
|
||||
{ name: "earlyMixLeft", min: 0, max: 100, units: " percent" },
|
||||
{ name: "earlyMixRight", min: 0, max: 100, units: " percent" },
|
||||
{ name: "lateMixLeft", min: 0, max: 100, units: " percent" },
|
||||
{ name: "lateMixRight", min: 0, max: 100, units: " percent" },
|
||||
{ name: "wetDryMix", min: 0, max: 100, units: " percent" },
|
||||
]
|
||||
|
||||
function setter(name) {
|
||||
|
@ -48,7 +74,7 @@ function getter(name) {
|
|||
}
|
||||
|
||||
function displayer(units) {
|
||||
return function(value) { return (value).toFixed(1) + units; };
|
||||
return function(value) { return (value).toFixed(1) + units; }
|
||||
}
|
||||
|
||||
// create a slider for each parameter
|
||||
|
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME interface)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK")
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion")
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")
|
||||
|
@ -100,6 +100,15 @@ else()
|
|||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif()
|
||||
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
# binary directories get added to the fixup path
|
||||
add_dependency_external_projects(sixense)
|
||||
add_dependency_external_projects(sdl2)
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(OpenVR)
|
||||
endif()
|
||||
|
||||
|
||||
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||
# This will prevent the following linker warnings
|
||||
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
|
||||
|
@ -161,13 +170,6 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
|||
endif ()
|
||||
endforeach()
|
||||
|
||||
# special OS X modifications for RtMidi library
|
||||
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
|
||||
find_library(CoreMIDI CoreMIDI)
|
||||
add_definitions(-D__MACOSX_CORE__)
|
||||
target_link_libraries(${TARGET_NAME} ${CoreMIDI})
|
||||
endif ()
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src")
|
||||
|
||||
|
@ -201,7 +203,6 @@ else (APPLE)
|
|||
|
||||
# link target to external libraries
|
||||
if (WIN32)
|
||||
# target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
|
||||
target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
|
||||
else (WIN32)
|
||||
# Nothing else required on linux apparently
|
||||
|
@ -209,3 +210,4 @@ else (APPLE)
|
|||
endif (APPLE)
|
||||
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
|
|
9
interface/external/rssdk/readme.txt
vendored
9
interface/external/rssdk/readme.txt
vendored
|
@ -1,9 +0,0 @@
|
|||
|
||||
Instructions for adding the Intel Realsense (RSSDK) to Interface
|
||||
Thijs Wenker, December 19, 2014
|
||||
|
||||
This is Windows only for now.
|
||||
|
||||
1. Download the SDK at https://software.intel.com/en-us/intel-realsense-sdk/download
|
||||
|
||||
2. Copy the `lib` and `include` folder inside this directory
|
43
interface/external/rtmidi/readme.txt
vendored
43
interface/external/rtmidi/readme.txt
vendored
|
@ -1,43 +0,0 @@
|
|||
|
||||
Instructions for adding the RtMidi library to Interface
|
||||
Stephen Birarda, June 30, 2014
|
||||
|
||||
1. Download the RtMidi tarball from High Fidelity S3.
|
||||
http://public.highfidelity.io/dependencies/rtmidi-2.1.0.tar.gz
|
||||
|
||||
2. Copy RtMidi.h to externals/rtmidi/include.
|
||||
|
||||
3. Compile the RtMidi library.
|
||||
|
||||
3. Copy either librtmidi.dylib (dynamic) or librtmidi.a (static) to externals/rtmidi/lib
|
||||
|
||||
4. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
||||
=========================
|
||||
|
||||
RtMidi: realtime MIDI i/o C++ classes<BR>
|
||||
Copyright (c) 2003-2014 Gary P. Scavone
|
||||
|
||||
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.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
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.
|
|
@ -13,12 +13,11 @@
|
|||
{ "from": "Hydra.RB", "to": "Standard.RB" },
|
||||
{ "from": "Hydra.RS", "to": "Standard.RS" },
|
||||
|
||||
{ "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" },
|
||||
{ "from": [ "Hydra.L1", "Hydra.L2", "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": [ "Hydra.L0" ], "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Hydra.R1", "Hydra.R2", "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": [ "Hydra.R0" ], "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
|
||||
|
|
|
@ -45,7 +45,12 @@ Item {
|
|||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Framerate: " + root.framerate
|
||||
text: "Render Rate: " + root.renderrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Present Rate: " + root.presentrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
|
|
|
@ -47,8 +47,9 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
implicitHeight: label.implicitHeight * 1.5
|
||||
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
|
||||
implicitWidth: label.implicitWidth + label.height * 2.5
|
||||
visible: source.visible
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
@ -66,6 +67,7 @@ Item {
|
|||
color: label.color
|
||||
text: checkText()
|
||||
size: label.height
|
||||
visible: source.visible
|
||||
font.pixelSize: size
|
||||
function checkText() {
|
||||
if (!source || source.type != 1 || !source.checkable) {
|
||||
|
@ -89,6 +91,7 @@ Item {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
enabled: source.enabled && source.visible
|
||||
visible: source.visible
|
||||
function typedText() {
|
||||
if (source) {
|
||||
switch (source.type) {
|
||||
|
@ -109,7 +112,7 @@ Item {
|
|||
x: listView.width - width - 4
|
||||
size: label.height
|
||||
width: implicitWidth
|
||||
visible: source.type == 2
|
||||
visible: source.visible && (source.type == 2)
|
||||
text: "\uF0DA"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: label.color
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue