Merge branch 'ordered-sending' into atp-server

This commit is contained in:
Ryan Huffman 2015-08-19 12:23:31 -07:00
commit 5884fcb17b
540 changed files with 23967 additions and 14624 deletions

View file

@ -5,6 +5,7 @@
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
* [zlib](http://www.zlib.net/)
####CMake External Project Dependencies

View file

@ -1,7 +1,7 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
###Homebrew
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple.
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple.
brew tap highfidelity/homebrew-formulas
brew install cmake openssl

View file

@ -75,6 +75,16 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
####zlib
Install zlib from
[Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm)
and fix a header file, as described here:
[zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/)
###Build High Fidelity using Visual Studio
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.

View file

@ -186,13 +186,10 @@ option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
option(GET_GLEW "Get GLEW library automatically as external project" 1)
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
if (WIN32)
option(GET_GLEW "Get GLEW library automatically as external project" 1)
endif ()
option(GET_SDL2 "Get SDL2 library automatically as external project" 0)
if (WIN32)

View file

@ -95,6 +95,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
}
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
_assignmentServerSocket.setObjectName("AssigmentServer");
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
qDebug() << "Assignment server socket is" << _assignmentServerSocket;
@ -119,6 +120,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
// did we get an assignment-client monitor port?
if (assignmentMonitorPort > 0) {
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;

View file

@ -97,7 +97,7 @@ AudioMixer::AudioMixer(NLPacket& packet) :
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
QSet<PacketType::Value> nodeAudioPackets {
QSet<PacketType> nodeAudioPackets {
PacketType::MicrophoneAudioNoEcho, PacketType::MicrophoneAudioWithEcho,
PacketType::InjectAudio, PacketType::SilentAudioFrame,
PacketType::AudioStreamStats
@ -652,9 +652,6 @@ void AudioMixer::run() {
auto nodeList = DependencyManager::get<NodeList>();
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [](Node* node) {

View file

@ -50,7 +50,7 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
}
int AudioMixerClientData::parseData(NLPacket& packet) {
PacketType::Value packetType = packet.getType();
PacketType packetType = packet.getType();
if (packetType == PacketType::AudioStreamStats) {

View file

@ -18,7 +18,7 @@ AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Se
{
}
int AvatarAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int readBytes = 0;
if (type == PacketType::SilentAudioFrame) {

View file

@ -25,7 +25,7 @@ private:
AvatarAudioStream(const AvatarAudioStream&);
AvatarAudioStream& operator= (const AvatarAudioStream&);
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
};
#endif // hifi_AvatarAudioStream_h

View file

@ -259,8 +259,8 @@ void AvatarMixer::broadcastAvatarData() {
return;
}
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData);
uint16_t lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
uint16_t lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
if (lastSeqToReceiver > lastSeqFromSender) {
// Did we somehow get out of order packets from the sender?
@ -289,7 +289,7 @@ void AvatarMixer::broadcastAvatarData() {
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData));
otherNodeData->getLastReceivedSequenceNumber());
// start a new segment in the PacketList for this avatar
avatarPacketList.startSegment();

View file

@ -14,7 +14,10 @@
#include "AvatarMixerClientData.h"
int AvatarMixerClientData::parseData(NLPacket& packet) {
// compute the offset to the data payload
// pull the sequence number from the data first
packet.readPrimitive(&_lastReceivedSequenceNumber);
// read the remaining data
return _avatar.parseDataFromBuffer(packet.read(packet.bytesLeftToRead()));
}
@ -24,13 +27,13 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
return oldValue;
}
PacketSequenceNumber AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
// return the matching PacketSequenceNumber, or the default if we don't have it
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
if (nodeMatch != _lastBroadcastSequenceNumbers.end()) {
return nodeMatch->second;
} else {
return DEFAULT_SEQUENCE_NUMBER;
return 0;
}
}

View file

@ -36,10 +36,12 @@ public:
bool checkAndSetHasReceivedFirstPackets();
PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
@ -77,8 +79,9 @@ public:
void loadJSONStats(QJsonObject& jsonObject) const;
private:
AvatarData _avatar;
std::unordered_map<QUuid, PacketSequenceNumber, UUIDHasher> _lastBroadcastSequenceNumbers;
uint16_t _lastReceivedSequenceNumber { 0 };
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
bool _hasReceivedFirstPackets = false;
quint64 _billboardChangeTimestamp = 0;

View file

@ -22,7 +22,7 @@ public:
OctreeQueryNode(),
_lastDeletedEntitiesSentAt(0) { }
virtual PacketType::Value getMyPacketType() const { return PacketType::EntityData; }
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }

View file

@ -28,11 +28,11 @@ public:
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode();
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
virtual PacketType::Value getMyEditNackType() const { return PacketType::EntityEditNack; }
virtual PacketType getMyEditNackType() const { return PacketType::EntityEditNack; }
virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); }
// subclass may implement these method

View file

@ -89,7 +89,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
// Ask our tree subclass if it can handle the incoming packet...
PacketType::Value packetType = packet->getType();
PacketType packetType = packet->getType();
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
@ -128,7 +128,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
if (debugProcessPacket) {
qDebug() << " numBytesPacketHeader=" << packet->totalHeadersSize();
qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType);
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
}
@ -153,7 +153,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
if (debugProcessPacket) {
qDebug() << " --- inside while loop ---";
qDebug() << " maxSize=" << maxSize;
qDebug("OctreeInboundPacketProcessor::processPacket() %c "
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
packet->pos(), maxSize);
@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
}
@ -210,7 +210,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
} else {
qDebug("unknown packet ignored... packetType=%d", packetType);
qDebug("unknown packet ignored... packetType=%hhu", packetType);
}
}

View file

@ -24,7 +24,7 @@ OctreeQueryNode::OctreeQueryNode() :
_viewSent(false),
_octreePacket(),
_octreePacketWaiting(false),
_lastOctreePayload(new char[MAX_PACKET_SIZE]),
_lastOctreePayload(new char[udt::MAX_PACKET_SIZE]),
_lastOctreePacketLength(0),
_duplicatePacketCount(0),
_firstSuppressedPacket(usecTimestampNow()),

View file

@ -34,7 +34,7 @@ public:
virtual ~OctreeQueryNode();
void init(); // called after creation to set up some virtual items
virtual PacketType::Value getMyPacketType() const = 0;
virtual PacketType getMyPacketType() const = 0;
void resetOctreePacket(); // resets octree packet to after "V" header
@ -150,7 +150,7 @@ private:
quint64 _lastRootTimestamp;
PacketType::Value _myPacketType;
PacketType _myPacketType;
bool _isShuttingDown;
SentPacketHistory _sentPacketHistory;

View file

@ -219,7 +219,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
packetSent = true;
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacket().getDataSize();
_totalPackets++;
@ -251,7 +251,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
packetSent = true;
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
int thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
int thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += packetSizeWithHeader;
_totalPackets++;
@ -598,7 +598,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
_totalBytes += packet->getDataSize();
_totalPackets++;
_totalWastedBytes += MAX_PACKET_SIZE - packet->getDataSize();
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
}

View file

@ -971,12 +971,7 @@ void OctreeServer::readConfiguration() {
strcpy(_persistFilename, qPrintable(persistFilename));
qDebug("persistFilename=%s", _persistFilename);
QString persistAsFileType;
if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) {
persistAsFileType = "svo";
}
_persistAsFileType = persistAsFileType;
qDebug() << "persistAsFileType=" << _persistAsFileType;
_persistAsFileType = "json.gz";
_persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);

View file

@ -63,11 +63,11 @@ public:
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
virtual char getMyNodeType() const = 0;
virtual PacketType::Value getMyQueryMessageType() const = 0;
virtual PacketType getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
virtual PacketType::Value getMyEditNackType() const = 0;
virtual PacketType getMyEditNackType() const = 0;
virtual QString getMyDomainSettingsKey() const { return QString("octree_server_settings"); }
// subclass may implement these method

View file

@ -9,8 +9,8 @@ if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://static.oculus.com/sdk-downloads/0.6.0.0/1431634088/ovr_sdk_win_0.6.0.0.zip
URL_MD5 a3dfdab037a854fdcf7e6033fa8d7028
URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -1,34 +1,34 @@
if (WIN32)
set(EXTERNAL_NAME glew)
set(EXTERNAL_NAME glew)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew-1.10.0-win32.zip
URL_MD5 37514e4e595a3b3dc587eee8f7e8ec2f
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple.zip
URL_MD5 0507dc08337a82a5e7ecbc5417f92cc1
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of glew include directories")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIB_DIR ${SOURCE_DIR}/lib/Release/x64)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/x64 CACHE FILEPATH "Location of GLEW DLL")
else()
set(_LIB_DIR ${SOURCE_DIR}/lib/Release/Win32)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/Win32 CACHE FILEPATH "Location of GLEW DLL")
endif()
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glew include directories")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/glew32.lib CACHE FILEPATH "Location of GLEW release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of GLEW debug library")
if (UNIX)
set(LIB_PREFIX "lib")
set(LIB_EXT "a")
elseif (WIN32)
set(LIB_EXT "lib")
endif ()
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glew_d.${LIB_EXT} CACHE FILEPATH "Path to glew debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library")

View file

@ -26,16 +26,19 @@ if (WIN32)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
elseif(APPLE)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
elseif(NOT ANDROID)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux32/libopenvr_api.so CACHE TYPE INTERNAL)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux64/libopenvr_api.so CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux64)
endif()

60
cmake/externals/sixense/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,60 @@
include(ExternalProject)
include(SelectLibraryConfigurations)
set(EXTERNAL_NAME Sixense)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add(
${EXTERNAL_NAME}
URL ./SixenseSDK_062612.zip
URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
if (APPLE)
find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
elseif (UNIX)
find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
# find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
elseif (WIN32)
endif ()
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
if (WIN32)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(ARCH_DIR "x64")
set(ARCH_SUFFIX "_x64")
else()
set(ARCH_DIR "Win32")
set(ARCH_SUFFIX "")
endif()
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
elseif(APPLE)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
elseif(NOT ANDROID)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux32/libopenvr_api.so CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
endif()

28
cmake/externals/zlib/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,28 @@
if (WIN32)
set(EXTERNAL_NAME zlib)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://zlib.net/zlib128.zip
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of zlib include directories")
ExternalProject_Get_Property(${EXTERNAL_NAME} BINARY_DIR)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${BINARY_DIR}/Release CACHE FILEPATH "Location of GLEW DLL")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${BINARY_DIR}/Release/zlib.lib CACHE FILEPATH "Location of ZLib release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of ZLib debug library")
endif ()

View file

@ -0,0 +1,16 @@
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(GroupSources curdir)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
GroupSources(${curdir}/${child})
else()
string(REPLACE "/" "\\" groupname ${curdir})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
endforeach()
endmacro()

View file

@ -12,7 +12,7 @@ macro(SETUP_HIFI_LIBRARY)
project(${TARGET_NAME})
# grab the implemenation and header files
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp")
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
# create a library and set the property so it can be referenced later

View file

@ -10,11 +10,6 @@ macro(SETUP_HIFI_OPENGL)
elseif (WIN32)
add_dependency_external_projects(glew)
find_package(GLEW REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} opengl32.lib)
if (USE_NSIGHT)
# try to find the Nsight package and add it to the build if we find it
find_package(NSIGHT)

View file

@ -0,0 +1,38 @@
#
# Find3DConnexionClient.cmake
#
# Once done this will define
# 3DCONNEXIONCLIENT_FOUND - system found 3DConnexion
# 3DCONNEXIONCLIENT_INCLUDE_DIRS - the 3DConnexion include directory
# 3DCONNEXIONCLIENT_LIBRARY - Link this to use 3DConnexion
#
# Created on 10/06/2015 by Marcel Verhagen
# Copyright 2015 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("connexionclient")
if (APPLE)
find_library(3DCONNEXIONCLIENT_LIBRARIES NAMES 3DConnexionClient HINTS 3DCONNEXIONCLIENT_SEARCH_DIRS)
if(EXISTS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_FOUND true)
set(3DCONNEXIONCLIENT_INCLUDE_DIRS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_LIBRARY ${3DConnexionClient})
message(STATUS "Found 3DConnexion at " ${3DConnexionClient})
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIR 3DCONNEXIONCLIENT_LIBRARY)
else ()
message(STATUS "Could NOT find 3DConnexionClient")
endif()
elseif (WIN32)
find_path(3DCONNEXIONCLIENT_INCLUDE_DIRS I3dMouseParams.h PATH_SUFFIXES include HINTS ${3DCONNEXIONCLIENT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(3DConnexionClient DEFAULT_MSG 3DCONNEXIONCLIENT_INCLUDE_DIRS)
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIRS 3DCONNEXIONCLIENT_SEARCH_DIRS)
endif()

View file

@ -1,7 +1,7 @@
#
# FindGLEW.cmake
#
# Try to find GLEW library and include path.
# Try to find GLEW library and include path. Note that this only handles static GLEW.
# Once done this will define
#
# GLEW_FOUND
@ -18,39 +18,18 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
if (WIN32)
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("glew")
find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${GLEW_SEARCH_DIRS})
find_library(GLEW_LIBRARY_RELEASE glew32 PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
find_library(GLEW_LIBRARY_DEBUG glew32d PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
find_path(GLEW_DLL_PATH glew32.dll PATH_SUFFIXES "bin/Release/Win32" HINTS ${GLEW_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(GLEW)
set(GLEW_LIBRARIES ${GLEW_LIBRARY})
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("glew")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_DLL_PATH)
add_paths_to_fixup_libs(${GLEW_DLL_PATH})
elseif (APPLE)
else ()
find_path(GLEW_INCLUDE_DIR GL/glew.h)
find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew32s PATH_SUFFIXES lib64)
find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${GLEW_SEARCH_DIRS})
set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR})
set(GLEW_LIBRARIES ${GLEW_LIBRARY})
find_library(GLEW_LIBRARY_RELEASE glew32 PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
find_library(GLEW_LIBRARY_DEBUG glew32d PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW
REQUIRED_VARS GLEW_INCLUDE_DIR GLEW_LIBRARY)
include(SelectLibraryConfigurations)
select_library_configurations(GLEW)
mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
message(STATUS "Found GLEW - Assuming that GLEW is static and defining GLEW_STATIC")

View file

@ -1,38 +0,0 @@
#
# FindconnexionClient.cmake
#
# Once done this will define
#
# 3DCONNEXIONCLIENT_INCLUDE_DIRS
#
# Created on 10/06/2015 by Marcel Verhagen
# 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
#
# setup hints for 3DCONNEXIONCLIENT search
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("connexionclient")
if (APPLE)
find_library(3DconnexionClient 3DconnexionClient)
if(EXISTS ${3DconnexionClient})
set(CONNEXIONCLIENT_FOUND true)
set(CONNEXIONCLIENT_INCLUDE_DIR ${3DconnexionClient})
set(CONNEXIONCLIENT_LIBRARY ${3DconnexionClient})
set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-weak_framework 3DconnexionClient")
message(STATUS "Found 3Dconnexion")
mark_as_advanced(CONNEXIONCLIENT_INCLUDE_DIR CONNEXIONCLIENT_LIBRARY)
endif()
endif()
if (WIN32)
find_path(CONNEXIONCLIENT_INCLUDE_DIRS I3dMouseParams.h PATH_SUFFIXES Inc HINTS ${CONNEXIONCLIENT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(connexionClient DEFAULT_MSG CONNEXIONCLIENT_INCLUDE_DIRS)
mark_as_advanced(CONNEXIONCLIENT_INCLUDE_DIRS CONNEXIONCLIENT_SEARCH_DIRS)
endif()

View file

@ -0,0 +1,66 @@
#
# FindiViewHMD.cmake
#
# Try to find the SMI iViewHMD eye tracker library
#
# You must provide a IVIEWHMD_ROOT_DIR which contains 3rdParty, include, and libs directories
#
# Once done this will define
#
# IVIEWHMD_FOUND - system found iViewHMD
# IVIEWHMD_INCLUDE_DIRS - the iViewHMD include directory
# IVIEWHMD_LIBRARIES - link this to use iViewHMD
#
# Created on 27 Jul 2015 by David Rowe
# Copyright 2015 High Fidelity, Inc.
#
if (WIN32)
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("iViewHMD")
find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})
find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)
set(IVIEWHMD_DLLS
avcodec-53.dll
avformat-53.dll
avutil-51.dll
libboost_filesystem-mgw45-mt-1_49.dll
libboost_system-mgw45-mt-1_49.dll
libboost_thread-mgw45-mt-1_49.dll
libgcc_s_dw2-1.dll
libiViewNG-LibCore.dll
libopencv_calib3d244.dll
libopencv_core244.dll
libopencv_features2d244.dll
libopencv_flann244.dll
libopencv_highgui244.dll
libopencv_imgproc244.dll
libopencv_legacy244.dll
libopencv_ml244.dll
libopencv_video244.dll
libstdc++-6.dll
opencv_core220.dll
opencv_highgui220.dll
opencv_imgproc220.dll
swscale-2.dll
)
foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS})
find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH)
list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH})
endforeach()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})
add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS})
mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)
endif()

View file

@ -371,26 +371,8 @@
"name": "persistFilename",
"label": "Entities Filename",
"help": "the path to the file entities are stored in. Make sure the path exists.",
"placeholder": "resources/models.svo",
"default": "resources/models.svo",
"advanced": true
},
{
"name": "persistAsFileType",
"label": "File format for entity server's persistent data",
"help": "This defines how the entity server will save entities to disk.",
"default": "svo",
"type": "select",
"options": [
{
"value": "svo",
"label": "Entity server persists data as SVO"
},
{
"value": "json",
"label": "Entity server persists data as JSON"
}
],
"placeholder": "resources/models.json.gz",
"default": "resources/models.json.gz",
"advanced": true
},
{

View file

@ -259,7 +259,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getNodeSocket().localPort());
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getSocketLocalPort());
// store our local http ports in shared memory
quint16 localHttpPort = DOMAIN_SERVER_HTTP_PORT;
@ -289,6 +289,9 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket");
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, this, "processICEPeerInformationPacket");
// NodeList won't be available to the settings manager when it is created, so call registerListener here
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
// add whatever static assignments that have been parsed to the queue
addStaticAssignmentsToQueue();
@ -580,7 +583,6 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
NodeType_t nodeType;
HifiSockAddr publicSockAddr, localSockAddr;
if (packet->getPayloadSize() == 0) {
return;
}
@ -625,31 +627,62 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
return;
}
}
}
QList<NodeType_t> nodeInterestList;
QString username;
QByteArray usernameSignature;
packetStream >> nodeInterestList >> username >> usernameSignature;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
packetStream >> nodeInterestList;
if (packet->bytesLeftToRead() > 0) {
// try to verify username
packetStream >> username;
}
bool isRestrictingAccess =
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
// we always let in a user who is sending a packet from our local socket or from the localhost address
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
if (isRestrictingAccess && !isLocalUser) {
if (!username.isEmpty()) {
// if there's a username, try to unpack username signature
packetStream >> usernameSignature;
if (usernameSignature.isEmpty()) {
// if user didn't include usernameSignature in connect request, send a connectionToken packet
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
if (connectionToken.isNull()) {
connectionToken = QUuid::createUuid();
}
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
connectionTokenPacket->reset();
connectionTokenPacket->write(connectionToken.toRfc4122());
limitedNodeList->sendUnreliablePacket(*connectionTokenPacket, packet->getSenderSockAddr());
return;
}
}
}
QString reason;
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) {
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
QByteArray utfString = reason.toUtf8();
quint16 payloadSize = utfString.size();
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize));
connectionDeniedPacket->writePrimitive(payloadSize);
connectionDeniedPacket->write(utfString);
if (payloadSize > 0) {
connectionDeniedPacket->writePrimitive(payloadSize);
connectionDeniedPacket->write(utfString);
}
// tell client it has been refused.
limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
return;
}
@ -736,6 +769,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
}
}
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
NodeType_t throwawayNodeType;
@ -766,52 +800,61 @@ unsigned int DomainServer::countConnectedUsers() {
}
bool DomainServer::verifyUsersKey(const QString& username,
const QByteArray& usernameSignature,
QString& reasonReturn) {
bool DomainServer::verifyUserSignature(const QString& username,
const QByteArray& usernameSignature,
QString& reasonReturn) {
// it's possible this user can be allowed to connect, but we need to check their username signature
QByteArray publicKeyArray = _userPublicKeys.value(username);
if (!publicKeyArray.isEmpty()) {
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
// if we do have a public key for the user, check for a signature match
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
// first load up the public key into an RSA struct
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
QByteArray lowercaseUsername = username.toLower().toUtf8();
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
QCryptographicHash::Sha256);
if (rsaPublicKey) {
QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
int decryptResult =
RSA_public_decrypt(usernameSignature.size(),
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
reinterpret_cast<unsigned char*>(decryptedArray.data()),
rsaPublicKey, RSA_PKCS1_PADDING);
int decryptResult = RSA_verify(NID_sha256,
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
usernameWithToken.size(),
reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
usernameSignature.size(),
rsaPublicKey);
if (decryptResult == 1) {
qDebug() << "Username signature matches for" << username << "- allowing connection.";
if (decryptResult != -1) {
if (username.toLower() == decryptedArray) {
qDebug() << "Username signature matches for" << username << "- allowing connection.";
// free up the public key and remove connection token before we return
RSA_free(rsaPublicKey);
_connectionTokenHash.remove(username);
// free up the public key before we return
RSA_free(rsaPublicKey);
return true;
} else {
qDebug() << "Username signature did not match for" << username << "- denying connection.";
reasonReturn = "Username signature did not match.";
}
return true;
} else {
qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection.";
reasonReturn = "Couldn't decrypt user signature.";
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
reasonReturn = "Error decrypting username signature.";
// free up the public key, we don't need it anymore
RSA_free(rsaPublicKey);
}
// free up the public key, we don't need it anymore
RSA_free(rsaPublicKey);
} else {
// we can't let this user in since we couldn't convert their public key to an RSA key we could use
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
reasonReturn = "Couldn't convert data to RSA key.";
}
} else {
qDebug() << "Insufficient data to decrypt username signature - denying connection.";
reasonReturn = "Insufficient data";
}
requestUserPublicKey(username); // no joy. maybe next time?
@ -823,41 +866,40 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr,
QString& reasonReturn) {
//TODO: improve flow so these bools aren't declared twice
bool isRestrictingAccess =
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
// we always let in a user who is sending a packet from our local socket or from the localhost address
if (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress()
|| senderSockAddr.getAddress() == QHostAddress::LocalHost) {
return true;
}
if (isRestrictingAccess) {
if (isRestrictingAccess && !isLocalUser) {
QStringList allowedUsers =
_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
if (!verifyUsersKey(username, usernameSignature, reasonReturn)) {
if (username.isEmpty()) {
qDebug() << "Connect request denied - no username provided.";
reasonReturn = "No username provided";
return false;
}
if (!verifyUserSignature(username, usernameSignature, reasonReturn)) {
return false;
}
} else {
qDebug() << "Connect request denied for user" << username << "not in allowed users list.";
reasonReturn = "User not on whitelist.";
return false;
}
}
// either we aren't restricting users, or this user is in the allowed list
// if this user is in the editors list, exempt them from the max-capacity check
const QVariant* allowedEditorsVariant =
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
if (allowedEditors.contains(username)) {
if (verifyUsersKey(username, usernameSignature, reasonReturn)) {
if (verifyUserSignature(username, usernameSignature, reasonReturn)) {
return true;
}
}

View file

@ -90,7 +90,7 @@ private:
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
unsigned int countConnectedUsers();
bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr, QString& reasonReturn);
@ -149,6 +149,8 @@ private:
QSet<QUuid> _webAuthenticationStateSet;
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
QHash<QString, QUuid> _connectionTokenHash;
QHash<QString, QByteArray> _userPublicKeys;

View file

@ -22,6 +22,7 @@
#include <Assignment.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
#include <NLPacketList.h>
#include "DomainServerSettingsManager.h"
@ -66,6 +67,21 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NLPacket> packet) {
Assignment::Type type;
packet->readPrimitive(&type);
QJsonObject responseObject = responseObjectForType(QString::number(type));
auto json = QJsonDocument(responseObject).toJson();
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::DomainSettings, QByteArray(), true, true));
packetList->write(json);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacketList(std::move(packetList), packet->getSenderSockAddr());
}
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
_configMap.loadMasterAndUserConfig(argumentList);

View file

@ -18,6 +18,8 @@
#include <HifiConfigVariantMap.h>
#include <HTTPManager.h>
#include <NLPacket.h>
const QString SETTINGS_PATHS_KEY = "paths";
const QString SETTINGS_PATH = "/settings";
@ -38,6 +40,10 @@ public:
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
private slots:
void processSettingsRequestPacket(QSharedPointer<NLPacket> packet);
private:
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant);

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>

View file

@ -5,7 +5,7 @@
// Created by Zander Otavka on 7/15/15.
// Copyright 2015 High Fidelity, Inc.
//
// Shows a few common controls in a FloatingUIPanel on right click.
// Shows a few common controls in a OverlayPanel on right click.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -22,14 +22,13 @@ var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg";
var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg";
var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg";
var panel = new FloatingUIPanel({
anchorPosition: {
bind: "myAvatar"
},
offsetPosition: { x: 0, y: 0.4, z: 1 }
var panel = new OverlayPanel({
anchorPositionBinding: { avatar: "MyAvatar" },
offsetPosition: { x: 0, y: 0.4, z: -1 },
visible: false
});
var background = new BillboardOverlay({
var background = new Image3DOverlay({
url: BG_IMAGE_URL,
dimensions: {
x: 0.5,
@ -37,11 +36,12 @@ var background = new BillboardOverlay({
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false
ignoreRayIntersection: false,
visible: false
});
panel.addChild(background);
var closeButton = new BillboardOverlay({
var closeButton = new Image3DOverlay({
url: CLOSE_IMAGE_URL,
dimensions: {
x: 0.15,
@ -51,17 +51,18 @@ var closeButton = new BillboardOverlay({
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.1,
x: 0.1,
y: 0.1,
z: -0.001
}
z: 0.001
},
visible: false
});
closeButton.onClick = function(event) {
panel.visible = false;
};
panel.addChild(closeButton);
var micMuteButton = new BillboardOverlay({
var micMuteButton = new Image3DOverlay({
url: MIC_IMAGE_URL,
subImage: {
x: 0,
@ -77,17 +78,18 @@ var micMuteButton = new BillboardOverlay({
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
x: -0.1,
y: 0.1,
z: -0.001
}
z: 0.001
},
visible: false
});
micMuteButton.onClick = function(event) {
AudioDevice.toggleMute();
};
panel.addChild(micMuteButton);
var faceMuteButton = new BillboardOverlay({
var faceMuteButton = new Image3DOverlay({
url: FACE_IMAGE_URL,
subImage: {
x: 0,
@ -102,43 +104,47 @@ var faceMuteButton = new BillboardOverlay({
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
y: -0.1,
z: -0.001
}
});
faceMuteButton.onClick = function(event) {
FaceTracker.toggleMute();
};
panel.addChild(faceMuteButton);
var addressBarButton = new BillboardOverlay({
url: ADDRESS_BAR_IMAGE_URL,
subImage: {
x: 0,
y: 0,
width: 45,
height: 45
},
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.1,
y: -0.1,
z: -0.001
}
z: 0.001
},
visible: false
});
faceMuteButton.onClick = function(event) {
FaceTracker.toggleMute();
};
panel.addChild(faceMuteButton);
var addressBarButton = new Image3DOverlay({
url: ADDRESS_BAR_IMAGE_URL,
subImage: {
x: 0,
y: 0,
width: 45,
height: 45
},
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
y: -0.1,
z: 0.001
},
visible: false
});
addressBarButton.onClick = function(event) {
DialogsManager.toggleAddressBar();
};
panel.addChild(addressBarButton);
panel.setChildrenVisible();
function onMicMuteToggled() {
var offset;
@ -181,6 +187,16 @@ function onMouseDown(event) {
if (event.isRightButton) {
mouseDown.pos = { x: event.x, y: event.y };
}
mouseDown.maxDistance = 0;
}
function onMouseMove(event) {
if (mouseDown.maxDistance !== undefined) {
var dist = Vec3.distance(mouseDown.pos, { x: event.x, y: event.y });
if (dist > mouseDown.maxDistance) {
mouseDown.maxDistance = dist;
}
}
}
function onMouseUp(event) {
@ -190,13 +206,10 @@ function onMouseUp(event) {
overlay.onClick(event);
}
}
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
if (event.isRightButton && mouseDown.maxDistance < 10) {
panel.setProperties({
visible: !panel.visible,
offsetRotation: {
bind: "quat",
value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 })
}
anchorRotation: MyAvatar.orientation
});
}
@ -208,6 +221,7 @@ function onScriptEnd(event) {
}
Controller.mousePressEvent.connect(onMouseDown);
Controller.mouseMoveEvent.connect(onMouseMove);
Controller.mouseReleaseEvent.connect(onMouseUp);
AudioDevice.muteToggled.connect(onMicMuteToggled);
FaceTracker.muteToggled.connect(onFaceMuteToggled);

View file

@ -0,0 +1,79 @@
//
// squeezeHands.js
// examples
//
// Created by Philip Rosedale on June 4, 2014
// 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
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var rightHandAnimation = HIFI_PUBLIC_BUCKET + "animations/RightHandAnimPhilip.fbx";
var leftHandAnimation = HIFI_PUBLIC_BUCKET + "animations/LeftHandAnimPhilip.fbx";
var LEFT = 0;
var RIGHT = 1;
var lastLeftFrame = 0;
var lastRightFrame = 0;
var leftDirection = true;
var rightDirection = true;
var LAST_FRAME = 15.0; // What is the number of the last frame we want to use in the animation?
var SMOOTH_FACTOR = 0.0;
var MAX_FRAMES = 30.0;
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
Script.update.connect(function(deltaTime) {
var leftTriggerValue = Controller.getActionValue(LEFT_HAND_CLICK);
var rightTriggerValue = Controller.getActionValue(RIGHT_HAND_CLICK);
var leftFrame, rightFrame;
// Average last few trigger frames together for a bit of smoothing
leftFrame = (leftTriggerValue * LAST_FRAME) * (1.0 - SMOOTH_FACTOR) + lastLeftFrame * SMOOTH_FACTOR;
rightFrame = (rightTriggerValue * LAST_FRAME) * (1.0 - SMOOTH_FACTOR) + lastRightFrame * SMOOTH_FACTOR;
if (!leftDirection) {
leftFrame = MAX_FRAMES - leftFrame;
}
if (!rightDirection) {
rightFrame = MAX_FRAMES - rightFrame;
}
if ((leftTriggerValue == 1.0) && (leftDirection == true)) {
leftDirection = false;
lastLeftFrame = MAX_FRAMES - leftFrame;
} else if ((leftTriggerValue == 0.0) && (leftDirection == false)) {
leftDirection = true;
lastLeftFrame = leftFrame;
}
if ((rightTriggerValue == 1.0) && (rightDirection == true)) {
rightDirection = false;
lastRightFrame = MAX_FRAMES - rightFrame;
} else if ((rightTriggerValue == 0.0) && (rightDirection == false)) {
rightDirection = true;
lastRightFrame = rightFrame;
}
if ((leftFrame != lastLeftFrame) && leftHandAnimation.length){
MyAvatar.startAnimation(leftHandAnimation, 30.0, 1.0, false, true, leftFrame, leftFrame);
}
if ((rightFrame != lastRightFrame) && rightHandAnimation.length) {
MyAvatar.startAnimation(rightHandAnimation, 30.0, 1.0, false, true, rightFrame, rightFrame);
}
lastLeftFrame = leftFrame;
lastRightFrame = rightFrame;
});
Script.scriptEnding.connect(function() {
MyAvatar.stopAnimation(leftHandAnimation);
MyAvatar.stopAnimation(rightHandAnimation);
});

View file

@ -0,0 +1,430 @@
// handGrab.js
// examples
//
// Created by Sam Gondelman on 8/3/2015
// Copyright 2015 High Fidelity, Inc.
//
// Allow avatar to grab the closest object to each hand and throw them
//
// 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("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var nullActionID = "00000000-0000-0000-0000-000000000000";
var controllerID;
var controllerActive;
var leftHandObjectID = null;
var rightHandObjectID = null;
var leftHandActionID = nullActionID;
var rightHandActionID = nullActionID;
var TRIGGER_THRESHOLD = 0.2;
var GRAB_RADIUS = 0.15;
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
var ACTION1 = Controller.findAction("ACTION1");
var ACTION2 = Controller.findAction("ACTION2");
var rightHandGrabAction = RIGHT_HAND_CLICK;
var leftHandGrabAction = LEFT_HAND_CLICK;
var rightHandGrabValue = 0;
var leftHandGrabValue = 0;
var prevRightHandGrabValue = 0
var prevLeftHandGrabValue = 0;
var grabColor = { red: 0, green: 255, blue: 0};
var releaseColor = { red: 0, green: 0, blue: 255};
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.toybox.toolbar", function() {
return {
x: 100,
y: 380
};
});
var BUTTON_SIZE = 32;
var SWORD_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/sword.svg"; // TODO: replace this with a table icon
var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png";
var tableButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: SWORD_IMAGE,
alpha: 1
});
var cleanupButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: CLEANUP_IMAGE,
alpha: 1
});
var overlays = false;
var leftHandOverlay;
var rightHandOverlay;
if (overlays) {
leftHandOverlay = Overlays.addOverlay("sphere", {
position: MyAvatar.getLeftPalmPosition(),
size: GRAB_RADIUS,
color: releaseColor,
alpha: 0.5,
solid: false
});
rightHandOverlay = Overlays.addOverlay("sphere", {
position: MyAvatar.getRightPalmPosition(),
size: GRAB_RADIUS,
color: releaseColor,
alpha: 0.5,
solid: false
});
}
var OBJECT_HEIGHT_OFFSET = 0.5;
var MIN_OBJECT_SIZE = 0.05;
var MAX_OBJECT_SIZE = 0.3;
var TABLE_DIMENSIONS = {
x: 10.0,
y: 0.2,
z: 5.0
};
var GRAVITY = {
x: 0.0,
y: -2.0,
z: 0.0
}
var LEFT = 0;
var RIGHT = 1;
var tableCreated = false;
var NUM_OBJECTS = 100;
var tableEntities = Array(NUM_OBJECTS + 1); // Also includes table
var VELOCITY_MAG = 0.3;
var entitiesToResize = [];
var MODELS = Array(
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx" },
{ modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Vehicles/clara/spaceshuttle.fbx" },
{ modelURL: "https://s3.amazonaws.com/hifi-public/cozza13/apartment/Stargate.fbx" },
{ modelURL: "https://dl.dropboxusercontent.com/u/17344741/kelectricguitar10/kelectricguitar10.fbx" },
{ modelURL: "https://dl.dropboxusercontent.com/u/17344741/ktoilet10/ktoilet10.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/models/props/MidCenturyModernLivingRoom/Interior/BilliardsTable.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/avatars/robotMedic/robotMedicRed/robotMedicRed.fst" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/avatars/robotMedic/robotMedicFaceRig/robotMedic.fst" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/029db3d4-da2c-4cb2-9c08-b9612ba576f5/02949063e7c4aed42ad9d1a58461f56d.fst?1427169842" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/models/props/MidCenturyModernLivingRoom/Interior/Bar.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/96124d04-d603-4707-a5b3-e03bf47a53b2/1431770eba362c1c25c524126f2970fb.fst?1436924721" }
// Complex models:
// { modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Architecture/sketchfab/cudillero.fbx" },
// { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/musicality/musicality.fbx" },
// { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/statelyHome/statelyHome.fbx" }
);
var COLLISION_SOUNDS = Array(
"http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/pingpong_TableBounceMono.wav",
"http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"
);
var RESIZE_TIMER = 0.0;
var RESIZE_WAIT = 0.05; // 50 milliseconds
var leftFist = Entities.addEntity( {
type: "Sphere",
shapeType: 'sphere',
position: MyAvatar.getLeftPalmPosition(),
dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS },
rotation: MyAvatar.getLeftPalmRotation(),
visible: false,
collisionsWillMove: false,
ignoreForCollisions: true
});
var rightFist = Entities.addEntity( {
type: "Sphere",
shapeType: 'sphere',
position: MyAvatar.getRightPalmPosition(),
dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS },
rotation: MyAvatar.getRightPalmRotation(),
visible: false,
collisionsWillMove: false,
ignoreForCollisions: true
});
function letGo(hand) {
var actionIDToRemove = (hand == LEFT) ? leftHandActionID : rightHandActionID;
var entityIDToEdit = (hand == LEFT) ? leftHandObjectID : rightHandObjectID;
var handVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmVelocity() : MyAvatar.getRightPalmVelocity();
var handAngularVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmAngularVelocity() :
MyAvatar.getRightPalmAngularVelocity();
if (actionIDToRemove != nullActionID && entityIDToEdit != null) {
Entities.deleteAction(entityIDToEdit, actionIDToRemove);
// TODO: upon successful letGo, restore collision groups
if (hand == LEFT) {
leftHandObjectID = null;
leftHandActionID = nullActionID;
} else {
rightHandObjectID = null;
rightHandActionID = nullActionID;
}
}
}
function setGrabbedObject(hand) {
var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition();
var entities = Entities.findEntities(handPosition, GRAB_RADIUS);
var objectID = null;
var minDistance = GRAB_RADIUS;
for (var i = 0; i < entities.length; i++) {
// Don't grab the object in your other hands, your fists, or the table
if ((hand == LEFT && entities[i] == rightHandObjectID) ||
(hand == RIGHT && entities[i] == leftHandObjectID) ||
entities[i] == leftFist || entities[i] == rightFist ||
(tableCreated && entities[i] == tableEntities[0])) {
continue;
} else {
var distance = Vec3.distance(Entities.getEntityProperties(entities[i]).position, handPosition);
if (distance <= minDistance) {
objectID = entities[i];
minDistance = distance;
}
}
}
if (objectID == null) {
return false;
}
if (hand == LEFT) {
leftHandObjectID = objectID;
} else {
rightHandObjectID = objectID;
}
return true;
}
function grab(hand) {
if (!setGrabbedObject(hand)) {
// If you don't grab an object, make a fist
Entities.editEntity((hand == LEFT) ? leftFist : rightFist, { ignoreForCollisions: false } );
return;
}
var objectID = (hand == LEFT) ? leftHandObjectID : rightHandObjectID;
var handRotation = (hand == LEFT) ? MyAvatar.getLeftPalmRotation() : MyAvatar.getRightPalmRotation();
var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition();
var objectRotation = Entities.getEntityProperties(objectID).rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
var objectPosition = Entities.getEntityProperties(objectID).position;
var offset = Vec3.subtract(objectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
// print(JSON.stringify(offsetPosition));
var actionID = Entities.addAction("hold", objectID, {
relativePosition: { x: 0, y: 0, z: 0 },
relativeRotation: offsetRotation,
hand: (hand == LEFT) ? "left" : "right",
timeScale: 0.05
});
if (actionID == nullActionID) {
if (hand == LEFT) {
leftHandObjectID = null;
} else {
rightHandObjectID = null;
}
} else {
// TODO: upon successful grab, add to collision group so object doesn't collide with immovable entities
if (hand == LEFT) {
leftHandActionID = actionID;
} else {
rightHandActionID = actionID;
}
}
}
function resizeModels() {
var newEntitiesToResize = [];
for (var i = 0; i < entitiesToResize.length; i++) {
var naturalDimensions = Entities.getEntityProperties(entitiesToResize[i]).naturalDimensions;
if (naturalDimensions.x != 1.0 || naturalDimensions.y != 1.0 || naturalDimensions.z != 1.0) {
// bigger range of sizes for models
var dimensions = Vec3.multiply(randFloat(MIN_OBJECT_SIZE, 3.0*MAX_OBJECT_SIZE), Vec3.normalize(naturalDimensions));
Entities.editEntity(entitiesToResize[i], {
dimensions: dimensions,
shapeType: "box"
});
} else {
newEntitiesToResize.push(entitiesToResize[i]);
}
}
entitiesToResize = newEntitiesToResize;
}
function update(deltaTime) {
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { position: MyAvatar.getLeftPalmPosition() });
Overlays.editOverlay(rightHandOverlay, { position: MyAvatar.getRightPalmPosition() });
}
// if (tableCreated && RESIZE_TIMER < RESIZE_WAIT) {
// RESIZE_TIMER += deltaTime;
// } else if (tableCreated) {
// resizeModels();
// }
rightHandGrabValue = Controller.getActionValue(rightHandGrabAction);
leftHandGrabValue = Controller.getActionValue(leftHandGrabAction);
Entities.editEntity(leftFist, { position: MyAvatar.getLeftPalmPosition() });
Entities.editEntity(rightFist, { position: MyAvatar.getRightPalmPosition() });
if (rightHandGrabValue > TRIGGER_THRESHOLD &&
prevRightHandGrabValue < TRIGGER_THRESHOLD) {
if (overlays) {
Overlays.editOverlay(rightHandOverlay, { color: grabColor });
}
grab(RIGHT);
} else if (rightHandGrabValue < TRIGGER_THRESHOLD &&
prevRightHandGrabValue > TRIGGER_THRESHOLD) {
Entities.editEntity(rightFist, { ignoreForCollisions: true } );
if (overlays) {
Overlays.editOverlay(rightHandOverlay, { color: releaseColor });
}
letGo(RIGHT);
}
if (leftHandGrabValue > TRIGGER_THRESHOLD &&
prevLeftHandGrabValue < TRIGGER_THRESHOLD) {
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { color: grabColor });
}
grab(LEFT);
} else if (leftHandGrabValue < TRIGGER_THRESHOLD &&
prevLeftHandGrabValue > TRIGGER_THRESHOLD) {
Entities.editEntity(leftFist, { ignoreForCollisions: true } );
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { color: releaseColor });
}
letGo(LEFT);
}
prevRightHandGrabValue = rightHandGrabValue;
prevLeftHandGrabValue = leftHandGrabValue;
}
function cleanUp() {
letGo(RIGHT);
letGo(LEFT);
if (overlays) {
Overlays.deleteOverlay(leftHandOverlay);
Overlays.deleteOverlay(rightHandOverlay);
}
Entities.deleteEntity(leftFist);
Entities.deleteEntity(rightFist);
removeTable();
toolBar.cleanup();
}
function onClick(event) {
if (event.deviceID != 0) {
return;
}
switch (Overlays.getOverlayAtPoint(event)) {
case tableButton:
if (!tableCreated) {
createTable();
tableCreated = true;
}
break;
case cleanupButton:
if (tableCreated) {
removeTable();
tableCreated = false;
}
break;
}
}
randFloat = function(low, high) {
return low + Math.random() * (high - low);
}
randInt = function(low, high) {
return Math.floor(randFloat(low, high));
}
function createTable() {
var tablePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(MyAvatar.orientation)));
tableEntities[0] = Entities.addEntity( {
type: "Model",
shapeType: 'box',
position: tablePosition,
dimensions: TABLE_DIMENSIONS,
rotation: MyAvatar.orientation,
// color: { red: 102, green: 51, blue: 0 },
modelURL: HIFI_PUBLIC_BUCKET + 'eric/models/woodFloor.fbx',
collisionSoundURL: "http://public.highfidelity.io/sounds/dice/diceCollide.wav"
});
for (var i = 1; i < NUM_OBJECTS + 1; i++) {
var objectOffset = { x: TABLE_DIMENSIONS.x/2.0 * randFloat(-1, 1),
y: OBJECT_HEIGHT_OFFSET,
z: TABLE_DIMENSIONS.z/2.0 * randFloat(-1, 1) };
var objectPosition = Vec3.sum(tablePosition, Vec3.multiplyQbyV(MyAvatar.orientation, objectOffset));
var type;
var randType = randInt(0, 3);
switch (randType) {
case 0:
type = "Box";
break;
case 1:
type = "Sphere";
// break;
case 2:
type = "Model";
break;
}
tableEntities[i] = Entities.addEntity( {
type: type,
position: objectPosition,
velocity: { x: randFloat(-VELOCITY_MAG, VELOCITY_MAG),
y: randFloat(-VELOCITY_MAG, VELOCITY_MAG),
z: randFloat(-VELOCITY_MAG, VELOCITY_MAG) },
dimensions: { x: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE),
y: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE),
z: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE) },
rotation: MyAvatar.orientation,
gravity: GRAVITY,
damping: 0.1,
restitution: 0.01,
density: 0.5,
collisionsWillMove: true,
color: { red: randInt(0, 255), green: randInt(0, 255), blue: randInt(0, 255) },
// collisionSoundURL: COLLISION_SOUNDS[randInt(0, COLLISION_SOUNDS.length)]
});
if (type == "Model") {
var randModel = randInt(0, MODELS.length);
Entities.editEntity(tableEntities[i], {
shapeType: "box",
modelURL: MODELS[randModel].modelURL
});
entitiesToResize.push(tableEntities[i]);
}
}
}
function removeTable() {
RESIZE_TIMER = 0.0;
for (var i = 0; i < tableEntities.length; i++) {
Entities.deleteEntity(tableEntities[i]);
}
}
Script.scriptEnding.connect(cleanUp);
Script.update.connect(update);
Controller.mousePressEvent.connect(onClick);

View file

@ -574,8 +574,14 @@ function findClickedEntity(event) {
}
var mouseHasMovedSincePress = false;
var mousePressStartTime = 0;
var mousePressStartPosition = { x: 0, y: 0 };
var mouseDown = false;
function mousePressEvent(event) {
mouseDown = true;
mousePressStartPosition = { x: event.x, y: event.y };
mousePressStartTime = Date.now();
mouseHasMovedSincePress = false;
mouseCapturedByTool = false;
@ -595,6 +601,8 @@ var highlightedEntityID = null;
var mouseCapturedByTool = false;
var lastMousePosition = null;
var idleMouseTimerId = null;
var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms
var CLICK_MOVE_DISTANCE_THRESHOLD = 8;
var IDLE_MOUSE_TIMEOUT = 200;
var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0;
@ -603,7 +611,21 @@ function mouseMoveEventBuffered(event) {
lastMouseMoveEvent = event;
}
function mouseMove(event) {
mouseHasMovedSincePress = true;
if (mouseDown && !mouseHasMovedSincePress) {
var timeSincePressMicro = Date.now() - mousePressStartTime;
var dX = mousePressStartPosition.x - event.x;
var dY = mousePressStartPosition.y - event.y;
var sqDist = (dX * dX) + (dY * dY);
// If less than CLICK_TIME_THRESHOLD has passed since the mouse click AND the mouse has moved
// less than CLICK_MOVE_DISTANCE_THRESHOLD distance, then don't register this as a mouse move
// yet. The goal is to provide mouse clicks that are more lenient to small movements.
if (timeSincePressMicro < CLICK_TIME_THRESHOLD && sqDist < CLICK_MOVE_DISTANCE_THRESHOLD) {
return;
}
mouseHasMovedSincePress = true;
}
if (placingEntityID) {
var pickRay = Camera.computePickRay(event.x, event.y);
@ -670,6 +692,8 @@ function highlightEntityUnderCursor(position, accurateRay) {
function mouseReleaseEvent(event) {
mouseDown = false;
if (lastMouseMoveEvent) {
mouseMove(lastMouseMoveEvent);
lastMouseMoveEvent = null;

View file

@ -178,7 +178,7 @@
modelProperties.sittingPoints[seatIndex].rotation);
this.scale = MyAvatar.scale / 3;
this.sphere = Overlays.addOverlay("billboard", {
this.sphere = Overlays.addOverlay("image3d", {
subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight},
url: buttonImageUrl,
position: this.position,

View file

@ -252,7 +252,7 @@ function SpriteBillboard(sprite_properties, overlay) {
}
var christmastree_loader = null;
christmastree_loader = new OverlayPreloader("billboard",
christmastree_loader = new OverlayPreloader("image3d",
{url: CHRISTMAS_TREE_SPRITES_URL, alpha: 0}, function() {
for (var i = 0; i < NUM_OF_TREES; i++) {
var clonedOverlay = Overlays.cloneOverlay(christmastree_loader.overlay);
@ -269,7 +269,7 @@ christmastree_loader = new OverlayPreloader("billboard",
);
var santa_loader = null;
santa_loader = new OverlayPreloader("billboard",
santa_loader = new OverlayPreloader("image3d",
{url: SANTA_SPRITES_URL, alpha: 0}, function() {
for (var i = 0; i < NUM_OF_SANTAS; i++) {
var clonedOverlay = Overlays.cloneOverlay(santa_loader.overlay);

View file

@ -19,7 +19,9 @@ Script.include('../utilities/tools/vector.js');
var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/";
SatelliteGame = function() {
SatelliteCreator = function() {
print("initializing satellite game");
var MAX_RANGE = 50.0;
var Y_AXIS = {
x: 0,
@ -36,6 +38,10 @@ SatelliteGame = function() {
var ZONE_DIM = 100.0;
var LIGHT_INTENSITY = 1.5;
var center, distance;
var earth;
Earth = function(position, size) {
this.earth = Entities.addEntity({
type: "Model",
@ -68,7 +74,7 @@ SatelliteGame = function() {
this.clouds = Entities.addEntity({
type: "Model",
shapeType: 'sphere',
modelURL: URL + "clouds.fbx?i=2",
modelURL: URL + "clouds.fbx",
position: position,
dimensions: {
x: size + CLOUDS_OFFSET,
@ -101,16 +107,42 @@ SatelliteGame = function() {
});
this.cleanup = function() {
print('cleaning up earth models');
Entities.deleteEntity(this.clouds);
Entities.deleteEntity(this.earth);
Entities.deleteEntity(this.zone);
}
}
// Create earth model
var center = Vec3.sum(Camera.getPosition(), Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation())));
var distance = Vec3.length(Vec3.subtract(center, Camera.getPosition()));
var earth = new Earth(center, EARTH_SIZE);
this.init = function() {
if (this.isActive) {
this.quitGame();
}
var confirmed = Window.confirm("Start satellite game?");
if (!confirmed) {
return false;
}
this.isActive = true;
MyAvatar.position = {
x: 1000,
y: 1000,
z: 1000
};
Camera.setPosition({
x: 1000,
y: 1000,
z: 1000
});
// Create earth model
center = Vec3.sum(Camera.getPosition(), Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation())));
distance = Vec3.length(Vec3.subtract(center, Camera.getPosition()));
earth = new Earth(center, EARTH_SIZE);
return true;
};
var satellites = [];
var SATELLITE_SIZE = 2.0;
@ -257,12 +289,16 @@ SatelliteGame = function() {
}
}
this.endGame = function() {
this.quitGame = function() {
print("ending satellite game");
this.isActive = false;
for (var i = 0; i < satellites.length; i++) {
Entities.deleteEntity(satellites[i].satellite);
satellites[i].arrow.cleanup();
}
earth.cleanup();
}
@ -283,6 +319,7 @@ SatelliteGame = function() {
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Script.update.connect(update);
Script.scriptEnding.connect(this.endGame);
Script.scriptEnding.connect(this.quitGame);
}
}

View file

@ -13,11 +13,15 @@
/*jslint vars: true*/
var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar, Settings; // Referenced globals provided by High Fidelity.
Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
var zombieGameScriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/zombieFight.js?v2";
// var zombieGameScriptURL = "zombieFight.js";
Script.include(zombieGameScriptURL);
var zombieFight;
var zombieFight = new ZombieFight();
var hand = "right";
var zombieFight;
var nullActionID = "00000000-0000-0000-0000-000000000000";
var controllerID;
var controllerActive;
@ -78,7 +82,7 @@ var cleanupButton = toolBar.addOverlay("image", {
var flasher;
var leftTriggerButton = 0;
var leftHandClick = 14;
var leftTriggerValue = 0;
var prevLeftTriggerValue = 0;
@ -88,7 +92,7 @@ var RIGHT = 1;
var leftPalm = 2 * LEFT;
var rightPalm = 2 * RIGHT;
var rightTriggerButton = 1;
var rightHandClick = 15;
var prevRightTriggerValue = 0;
var rightTriggerValue = 0;
var TRIGGER_THRESHOLD = 0.2;
@ -357,8 +361,8 @@ function update() {
}
function updateControllerState() {
rightTriggerValue = Controller.getTriggerValue(rightTriggerButton);
leftTriggerValue = Controller.getTriggerValue(leftTriggerButton);
rightTriggerValue = Controller.getActionValue(rightHandClick);
leftTriggerValue = Controller.getActionValue(leftHandClick);
if (rightTriggerValue > TRIGGER_THRESHOLD && !swordHeld) {
grabSword("right")
@ -470,4 +474,4 @@ function onClick(event) {
Script.scriptEnding.connect(cleanUp);
Script.update.connect(update);
Controller.mousePressEvent.connect(onClick);
Controller.mousePressEvent.connect(onClick);

View file

@ -0,0 +1,225 @@
//
// hydraPaint.js
// examples
//
// Created by Eric Levin on 5/14/15.
// Copyright 2014 High Fidelity, Inc.
//
// This script allows you to paint with the hydra!
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var LEFT = 0;
var RIGHT = 1;
var LASER_WIDTH = 3;
var LASER_COLOR = {
red: 50,
green: 150,
blue: 200
};
var TRIGGER_THRESHOLD = .1;
var MAX_POINTS_PER_LINE = 40;
var LIFETIME = 6000;
var DRAWING_DEPTH = 1;
var LINE_DIMENSIONS = 20;
var MIN_POINT_DISTANCE = 0.01;
var MIN_BRUSH_RADIUS = 0.08;
var MAX_BRUSH_RADIUS = 0.1;
var RIGHT_BUTTON_1 = 7
var RIGHT_BUTTON_2 = 8
var RIGHT_BUTTON_3 = 9;
var RIGHT_BUTTON_4 = 10
var LEFT_BUTTON_1 = 1;
var LEFT_BUTTON_2 = 2;
var LEFT_BUTTON_3 = 3;
var LEFT_BUTTON_4 = 4;
var colorPalette = [{
red: 250,
green: 0,
blue: 0
}, {
red: 214,
green: 91,
blue: 67
}, {
red: 192,
green: 41,
blue: 66
}, {
red: 84,
green: 36,
blue: 55
}, {
red: 83,
green: 119,
blue: 122
}];
var MIN_STROKE_WIDTH = 0.002;
var MAX_STROKE_WIDTH = 0.05;
function controller(side, cycleColorButton) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.palm = 2 * side;
this.tip = 2 * side + 1;
this.trigger = side;
this.cycleColorButton = cycleColorButton;
this.points = [];
this.normals = [];
this.strokeWidths = [];
this.currentColorIndex = 0;
this.currentColor = colorPalette[this.currentColorIndex];
var self = this;
this.brush = Entities.addEntity({
type: 'Sphere',
position: {
x: 0,
y: 0,
z: 0
},
color: this.currentColor,
dimensions: {
x: MIN_BRUSH_RADIUS,
y: MIN_BRUSH_RADIUS,
z: MIN_BRUSH_RADIUS
}
});
this.cycleColor = function() {
this.currentColor = colorPalette[++this.currentColorIndex];
if (this.currentColorIndex === colorPalette.length - 1) {
this.currentColorIndex = -1;
}
}
this.newLine = function(position) {
this.linePosition = position;
this.line = Entities.addEntity({
position: position,
type: "PolyLine",
color: this.currentColor,
dimensions: {
x: LINE_DIMENSIONS,
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
lifetime: LIFETIME
});
this.points = [];
this.normals = []
this.strokeWidths = [];
}
this.update = function(deltaTime) {
this.updateControllerState();
var newBrushPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH);
var newBrushPos = Vec3.sum(this.palmPosition, newBrushPosOffset);
var brushRadius = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_BRUSH_RADIUS, MAX_BRUSH_RADIUS)
Entities.editEntity(this.brush, {
position: newBrushPos,
color: this.currentColor,
dimensions: {
x: brushRadius,
y: brushRadius,
z: brushRadius
}
});
if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) {
this.newLine(newBrushPos);
this.drawing = true;
} else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) {
this.drawing = false;
}
if (this.drawing && this.points.length < MAX_POINTS_PER_LINE) {
var localPoint = Vec3.subtract(newBrushPos, this.linePosition);
if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) {
//Need a minimum distance to avoid binormal NANs
return;
}
this.points.push(localPoint);
var normal = computeNormal(newBrushPos, Camera.getPosition());
this.normals.push(normal);
var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH);
this.strokeWidths.push(strokeWidth);
Entities.editEntity(this.line, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
color: this.currentColor
});
}
}
this.updateControllerState = function() {
this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
this.palmNormal = Controller.getSpatialControlNormal(this.palm);
this.triggerValue = Controller.getTriggerValue(this.trigger);
if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
this.cycleColor();
Entities.editEntity(this.brush, {
// color: this.currentColor
});
}
this.prevCycleColorButtonPressed = this.cycleColorButtonPressed;
}
this.cleanup = function() {
Entities.deleteEntity(self.brush);
}
}
function computeNormal(p1, p2) {
return Vec3.normalize(Vec3.subtract(p2, p1));
}
function update(deltaTime) {
leftController.update(deltaTime);
rightController.update(deltaTime);
}
function scriptEnding() {
leftController.cleanup();
rightController.cleanup();
}
function vectorIsZero(v) {
return v.x === 0 && v.y === 0 && v.z === 0;
}
var rightController = new controller(RIGHT, RIGHT_BUTTON_4);
var leftController = new controller(LEFT, LEFT_BUTTON_4);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}

View file

@ -0,0 +1,194 @@
//
// mousePaint.js
// examples
//
// Created by Eric Levin on 6/4/15.
// Copyright 2014 High Fidelity, Inc.
//
// This script allows you to paint with the hydra or mouse!
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var LINE_DIMENSIONS = 10;
var LIFETIME = 6000;
var EVENT_CHANGE_THRESHOLD = 200;
var LINE_WIDTH = .07;
var MAX_POINTS_PER_LINE = 40;
var points = [];
var normals = [];
var deletedLines = [];
var strokeWidths = [];
var count = 0;
var prevEvent = {x: 0, y: 0};
var eventChange;
var MIN_POINT_DISTANCE = .01;
var colorPalette = [{
red: 250,
green: 0,
blue: 0
}, {
red: 214,
green: 91,
blue: 67
}, {
red: 192,
green: 41,
blue: 66
}, {
red: 84,
green: 36,
blue: 55
}, {
red: 83,
green: 119,
blue: 122
}];
var currentColorIndex = 0;
var currentColor = colorPalette[currentColorIndex];
function cycleColor() {
currentColor = colorPalette[++currentColorIndex];
if (currentColorIndex === colorPalette.length - 1) {
currentColorIndex = -1;
}
}
MousePaint();
function MousePaint() {
var DRAWING_DISTANCE = 5;
var lines = [];
var isDrawing = false;
var line, linePosition;
var BRUSH_SIZE = .05;
var brush = Entities.addEntity({
type: 'Sphere',
position: {
x: 0,
y: 0,
z: 0
},
color: currentColor,
dimensions: {
x: BRUSH_SIZE,
y: BRUSH_SIZE,
z: BRUSH_SIZE
}
});
function newLine(position) {
linePosition = position;
line = Entities.addEntity({
position: position,
type: "PolyLine",
color: currentColor,
dimensions: {
x: LINE_DIMENSIONS,
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
linePoints: [],
lifetime: LIFETIME
});
points = [];
normals = []
strokeWidths = [];
lines.push(line);
}
function mouseMoveEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
count++;
var worldPoint = computeWorldPoint(pickRay);
Entities.editEntity(brush, {
position: worldPoint
});
eventChange = Math.sqrt(Math.pow(event.x - prevEvent.x, 2) + Math.pow(event.y - prevEvent.y, 2));
localPoint = computeLocalPoint(worldPoint);
if (!isDrawing || points.length > MAX_POINTS_PER_LINE || eventChange > EVENT_CHANGE_THRESHOLD ||
Vec3.distance(points[points.length - 1], localPoint) < MIN_POINT_DISTANCE) {
return;
}
points.push(localPoint)
normals.push(computeNormal(worldPoint, pickRay.origin));
strokeWidths.push(LINE_WIDTH);
Entities.editEntity(line, {
strokeWidths: strokeWidths,
linePoints: points,
normals: normals,
});
prevEvent = event;
}
function computeNormal(p1, p2) {
return Vec3.normalize(Vec3.subtract(p2, p1));
}
function computeWorldPoint(pickRay) {
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
return Vec3.sum(pickRay.origin, addVector);
}
function computeLocalPoint(worldPoint) {
var localPoint = Vec3.subtract(worldPoint, linePosition);
return localPoint;
}
function mousePressEvent(event) {
if (!event.isLeftButton) {
isDrawing = false;
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
prevEvent = {x: event.x, y:event.y};
var worldPoint = computeWorldPoint(pickRay);
newLine(worldPoint);
var localPoint = computeLocalPoint(worldPoint);
points.push(localPoint);
normals.push(computeNormal(worldPoint, pickRay.origin));
strokeWidths.push(0.07);
isDrawing = true;
}
function mouseReleaseEvent() {
isDrawing = false;
}
function keyPressEvent(event) {
if (event.text === "SPACE") {
cycleColor();
Entities.editEntity(brush, {
color: currentColor
});
}
}
function cleanup() {
lines.forEach(function(line) {
// Entities.deleteEntity(line);
});
Entities.deleteEntity(brush);
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Script.scriptEnding.connect(cleanup);
Controller.keyPressEvent.connect(keyPressEvent);
}

View file

@ -0,0 +1,515 @@
//
// widgets-example.js
// games
//
// 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 ICONS_URL = 'https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/images/';
var panelX = 1250;
var panelY = 500;
var panelWidth = 50;
var panelHeight = 210;
Script.include('../libraries/uiwidgets.js');
UI.setDefaultVisibility(true);
var ICON_WIDTH = 40.0;
var ICON_HEIGHT = 40.0;
var ICON_COLOR = UI.rgba(45, 45, 45, 0.7);
var FOCUSED_COLOR = UI.rgba(250, 250, 250, 1.0);
var PANEL_BACKGROUND_COLOR = UI.rgba(120, 120, 120, 0.8);
var PANEL_PADDING = 7.0;
var PANEL_BORDER = 12.0;
var SUBPANEL_GAP = 1.0;
var icons = [];
function addImage(panel, iconId) {
var icon = panel.add(new UI.Image({
'imageURL': ICONS_URL + iconId + '.svg',
'width': ICON_WIDTH,
'height': ICON_HEIGHT,
'color': ICON_COLOR,
'alpha': ICON_COLOR.a
}));
icons.push(icon);
return icon;
}
var panels = [];
function addPanel(properties) {
properties.background = properties.background || {};
properties.background.backgroundColor = properties.background.backgroundColor ||
PANEL_BACKGROUND_COLOR;
properties.background.backgroundAlpha = properties.background.backgroundAlpha ||
PANEL_BACKGROUND_COLOR.a;
properties.padding = properties.padding || {
x: PANEL_PADDING,
y: PANEL_PADDING
};
properties.border = properties.border || {
x: PANEL_BORDER,
y: PANEL_BORDER
};
var panel = new UI.WidgetStack(properties);
panels.push(panel);
return panel;
}
function makeDraggable(panel, target) {
if (!target) {
target = panel;
}
var dragStart = null;
var initialPos = null;
panel.addAction('onDragBegin', function(event) {
dragStart = {
x: event.x,
y: event.y
};
initialPos = {
x: target.position.x,
y: target.position.y
};
});
panel.addAction('onDragUpdate', function(event) {
target.setPosition(
initialPos.x + event.x - dragStart.x,
initialPos.y + event.y - dragStart.y
);
UI.updateLayout();
});
panel.addAction('onDragEnd', function() {
dragStart = dragEnd = null;
});
}
function setText(text) {
return function() {
demoLabel.setText(text);
UI.updateLayout();
};
}
function join(obj) {
var s = "{";
var sep = "\n";
for (var k in obj) {
s += sep + k + ": " + ("" + obj[k]).replace("\n", "\n");
sep = ",\n";
}
if (s.length > 1)
return s + " }";
return s + "}";
}
setText = undefined;
var tooltipWidget = new UI.Label({
text: "<tooltip>",
width: 500,
height: 20,
visible: false
});
function addTooltip(widget, text) {
widget.addAction('onMouseOver', function(event, widget) {
tooltipWidget.setVisible(true);
tooltipWidget.setPosition(widget.position.x + widget.getWidth() + 20, widget.position.y + 10);
tooltipWidget.setText(text);
UI.updateLayout();
});
widget.addAction('onMouseExit', function() {
tooltipWidget.setVisible(false);
UI.updateLayout();
});
}
var mainPanel = addPanel({
dir: '+y'
});
makeDraggable(mainPanel);
mainPanel.setPosition(1200, 250);
mainPanel.setVisible(true);
var systemViewButton = addImage(mainPanel, 'solarsystems');
var zoomButton = addImage(mainPanel, 'magnifier');
var satelliteButton = addImage(mainPanel, 'satellite');
var settingsButton = addImage(mainPanel, 'settings');
var stopButton = addImage(mainPanel, 'close');
addTooltip(systemViewButton, "system view");
addTooltip(zoomButton, "zoom");
addTooltip(satelliteButton, "satelite view");
addTooltip(settingsButton, "settings");
addTooltip(stopButton, "exit");
var systemViewPanel = addPanel({
dir: '+x',
visible: false
});
var restartButton = addImage(systemViewPanel, 'refresh');
var pauseButton = addImage(systemViewPanel, 'playpause');
var rideButton = addImage(systemViewPanel, 'rocket');
var tweening, tweeningPaused;
Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js');
pauseButton.addAction('onClick', function() {
if (tweening) {
if (!tweeningPaused) {
tweeningPaused = true;
} else {
tweeningPaused = false;
}
return;
}
if (!paused) {
pause();
} else {
resume();
}
});
// Allow to toggle pause with spacebar
function keyPressEvent(event) {
if (event.text == "SPACE") {
if (!paused) {
pause();
} else {
resume();
}
}
}
rideButton.addAction('onClick', function() {
if (!paused) {
pause();
}
if (tweening) {
tweening = false;
tweeningPaused = true;
restart();
return;
}
var confirmed = Window.confirm('Ride through the solar system?');
if (confirmed) {
init();
tweening = true;
tweeningPaused = false;
}
});
restartButton.addAction('onClick', function() {
restart();
tweening = false;
});
var zoomPanel = addPanel({
dir: '+x',
visible: false
});
var zoomButtons = [];
for (var i = 0; i < planets.length; ++i) {
var label = zoomPanel.add(new UI.Label({
text: planets[i].name,
width: 80,
height: 20
}));
zoomButtons.push(label);
UI.updateLayout();
}
UI.updateLayout();
var zoomView = false;
zoomButtons.forEach(function(button, i) {
var planet = planets[i];
button.addAction('onClick', function() {
if (!planets[i].isZoomed) {
planet.zoom();
planet.isZoomed = true;
zoomView = true;
} else {
MyAvatar.position = startingPosition;
Camera.setPosition(cameraStart);
planet.isZoomed = false;
zoomView = false;
}
});
});
var settingsPanel = addPanel({
dir: '+y',
visible: false
});
function addCheckbox(parent, label, labelWidth, enabled, onValueChanged) {
var layout = parent.add(new UI.WidgetStack({
dir: '+x',
visible: true,
backgroundAlpha: 0.0
}));
var label = layout.add(new UI.Label({
text: label,
width: labelWidth,
height: 20,
backgroundAlpha: 0.0
}));
var defaultColor = UI.rgb(10, 10, 10);
var checkbox = layout.add(new UI.Checkbox({
width: 20,
height: 20,
padding: {
x: 3,
y: 3
},
backgroundColor: defaultColor,
backgroundAlpha: 0.9,
checked: enabled,
onValueChanged: onValueChanged
}));
checkbox.label = label;
checkbox.layout = layout;
checkbox.setValue = function(value) {
checkbox.setChecked(value);
}
return checkbox;
}
function addSlider(parent, label, labelWidth, defaultValue, min, max, valueChanged) {
var layout = parent.add(new UI.WidgetStack({
dir: '+x',
visible: true
}));
var label = layout.add(new UI.Label({
text: label,
width: labelWidth,
height: 27
}));
var display = layout.add(new UI.Label({
text: " ",
width: 50,
height: 27
}));
var slider = layout.add(new UI.Slider({
value: defaultValue,
maxValue: max,
minValue: min,
width: 300,
height: 20,
backgroundColor: UI.rgb(10, 10, 10),
backgroundAlpha: 1.0,
slider: { // slider knob
width: 30,
height: 18,
backgroundColor: UI.rgb(120, 120, 120),
backgroundAlpha: 1.0
}
}));
slider.addAction('onDoubleClick', function() {
slider.setValue(defaultValue);
UI.updateLayout();
});
display.setText("" + (+slider.getValue().toFixed(2)));
slider.onValueChanged = function(value) {
valueChanged(value);
display.setText("" + (+value.toFixed(2)));
UI.updateLayout();
}
slider.label = label;
slider.layout = layout;
return slider;
}
settingsPanel.showTrailsButton = addCheckbox(settingsPanel, "show trails", 120, trailsEnabled, function(value) {
trailsEnabled = value;
if (trailsEnabled) {
for (var i = 0; i < planets.length; ++i) {
planets[i].resetTrails();
}
//if trails are off and we've already created trails, remove existing trails
} else {
for (var i = 0; i < planets.length; ++i) {
planets[i].clearTrails();
}
}
});
var g_multiplier = 1.0;
settingsPanel.gravitySlider = addSlider(settingsPanel, "gravity scale ", 200, g_multiplier, 0.0, 5.0, function(value) {
g_multiplier = value;
GRAVITY = REFERENCE_GRAVITY * g_multiplier;
});
var period_multiplier = 1.0;
var last_alpha = period_multiplier;
settingsPanel.periodSlider = addSlider(settingsPanel, "orbital period scale ", 200, period_multiplier, 0.0, 3.0, function(value) {
period_multiplier = value;
changePeriod(period_multiplier);
});
function changePeriod(alpha) {
var ratio = last_alpha / alpha;
GRAVITY = Math.pow(ratio, 2.0) * GRAVITY;
for (var i = 0; i < planets.length; ++i) {
planets[i].period = ratio * planets[i].period;
planets[i].velocity = Vec3.multiply(ratio, planets[i].velocity);
planets[i].resetTrails();
}
last_alpha = alpha;
}
var satelliteGame;
satelliteButton.addAction('onClick', function() {
if (satelliteGame && satelliteGame.isActive) {
MyAvatar.position = startingPosition;
satelliteGame.quitGame();
if (paused) {
resume();
}
} else {
pause();
satelliteGame = new SatelliteCreator();
satelliteGame.init();
}
});
var subpanels = [systemViewPanel, zoomPanel, settingsPanel];
function hideSubpanelsExcept(panel) {
subpanels.forEach(function(x) {
if (x != panel) {
x.setVisible(false);
}
});
}
function attachPanel(panel, button) {
button.addAction('onClick', function() {
hideSubpanelsExcept(panel);
panel.setVisible(!panel.isVisible());
UI.updateLayout();
})
UI.addAttachment(panel, button, function(target, rel) {
target.setPosition(
rel.position.x - (target.getWidth() + target.border.x + SUBPANEL_GAP),
rel.position.y - target.border.y
);
});
}
attachPanel(systemViewPanel, systemViewButton);
attachPanel(zoomPanel, zoomButton);
attachPanel(settingsPanel, settingsButton);
var addColorToggle = function(widget) {
widget.addAction('onMouseOver', function() {
widget.setColor(FOCUSED_COLOR);
});
widget.addAction('onMouseExit', function() {
widget.setColor(ICON_COLOR);
});
}
systemViewPanel.addAction('onMouseOver', function() {
hideSubpanelsExcept(systemViewPanel);
UI.updateLayout();
});
zoomButton.addAction('onClick', function() {
if (zoomView) {
restart();
}
hideSubpanelsExcept(zoomPanel);
UI.updateLayout();
});
UI.updateLayout();
stopButton.addAction('onClick', function() {
teardown();
Script.stop();
});
// Panel drag behavior
// (click + drag on border to drag)
(function() {
var dragged = null;
this.startDrag = function(dragAction) {
dragged = dragAction;
}
this.updateDrag = function(event) {
if (dragged) {
print("Update drag");
dragged.updateDrag(event);
}
}
this.clearDrag = function(event) {
if (dragged)
print("End drag");
dragged = null;
}
})();
var buttons = icons;
buttons.map(addColorToggle);
panels.map(function(panel) {
makeDraggable(panel, mainPanel);
});
// Cleanup script resources
function teardown() {
UI.teardown();
if (satelliteGame) {
satelliteGame.quitGame();
}
};
UI.debug.setVisible(false);
var inputHandler = {
onMouseMove: function(event) {
updateDrag(event);
UI.handleMouseMove(event);
},
onMousePress: function(event) {
UI.handleMousePress(event);
},
onMouseRelease: function(event) {
clearDrag(event);
UI.handleMouseRelease(event);
},
onMouseDoublePress: function(event) {
UI.handleMouseDoublePress(event);
}
};
Controller.mousePressEvent.connect(inputHandler.onMousePress);
Controller.mouseMoveEvent.connect(inputHandler.onMouseMove);
Controller.mouseReleaseEvent.connect(inputHandler.onMouseRelease);
Controller.mouseDoublePressEvent.connect(inputHandler.onMouseDoublePress);
Controller.keyPressEvent.connect(keyPressEvent);
Script.scriptEnding.connect(teardown);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
//
// floatingUI.js
// overlayPanelExample.js
// examples/example/ui
//
// Created by Alexander Otavka
@ -18,19 +18,18 @@ var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg";
var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg";
var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg";
var mainPanel = new FloatingUIPanel({
offsetRotation: {
bind: "quat",
value: { w: 1, x: 0, y: 0, z: 0 }
},
offsetPosition: { x: 0, y: 0.4, z: 1 }
var mainPanel = new OverlayPanel({
anchorPositionBinding: { avatar: "MyAvatar" },
offsetPosition: { x: 0, y: 0.4, z: -1 },
isFacingAvatar: false
});
var bluePanel = mainPanel.addChild(new FloatingUIPanel ({
offsetPosition: { x: 0.1, y: 0.1, z: -0.2 }
var bluePanel = mainPanel.addChild(new OverlayPanel ({
offsetPosition: { x: 0.1, y: 0.1, z: 0.2 },
offsetScale: 0.5
}));
var mainPanelBackground = new BillboardOverlay({
var mainPanelBackground = new Image3DOverlay({
url: BG_IMAGE_URL,
dimensions: {
x: 0.5,
@ -42,20 +41,44 @@ var mainPanelBackground = new BillboardOverlay({
offsetPosition: {
x: 0,
y: 0,
z: 0.001
z: -0.001
}
});
var bluePanelBackground = mainPanelBackground.clone();
bluePanelBackground.dimensions = {
x: 0.3,
y: 0.3
};
mainPanel.addChild(mainPanelBackground);
bluePanel.addChild(bluePanelBackground);
var redDot = mainPanel.addChild(new BillboardOverlay({
var textWidth = .25;
var textHeight = .1;
var numberOfLines = 1;
var textMargin = 0.00625;
var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines;
var text = mainPanel.addChild(new Text3DOverlay({
text: "TEXT",
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
y: -0.15,
z: 0.001
},
dimensions: { x: textWidth, y: textHeight },
backgroundColor: { red: 0, green: 0, blue: 0 },
color: { red: 255, green: 255, blue: 255 },
topMargin: textMargin,
leftMargin: textMargin,
bottomMargin: textMargin,
rightMargin: textMargin,
lineHeight: lineHeight,
alpha: 0.9,
backgroundAlpha: 0.9
}));
var redDot = mainPanel.addChild(new Image3DOverlay({
url: RED_DOT_IMAGE_URL,
dimensions: {
x: 0.1,
@ -71,7 +94,7 @@ var redDot = mainPanel.addChild(new BillboardOverlay({
}
}));
var redDot2 = mainPanel.addChild(new BillboardOverlay({
var redDot2 = mainPanel.addChild(new Image3DOverlay({
url: RED_DOT_IMAGE_URL,
dimensions: {
x: 0.1,
@ -87,46 +110,45 @@ var redDot2 = mainPanel.addChild(new BillboardOverlay({
}
}));
var blueSquare = bluePanel.addChild(new BillboardOverlay({
var blueSquare = bluePanel.addChild(new Image3DOverlay({
url: BLUE_SQUARE_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.055,
y: -0.055,
x: 0.09,
y: -0.09,
z: 0
}
}));
var blueSquare2 = bluePanel.addChild(new BillboardOverlay({
var blueSquare2 = bluePanel.addChild(new Image3DOverlay({
url: BLUE_SQUARE_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.055,
y: 0.055,
x: 0.09,
y: 0.09,
z: 0
}
}));
var blueSquare3 = blueSquare2.clone();
blueSquare3.offsetPosition = {
x: -0.055,
y: 0.055,
x: -0.09,
y: 0.09,
z: 0
};
var mouseDown = {};
function onMouseDown(event) {
@ -136,26 +158,30 @@ function onMouseDown(event) {
if (event.isRightButton) {
mouseDown.pos = { x: event.x, y: event.y };
}
mouseDown.maxDistance = 0;
}
function onMouseMove(event) {
if (mouseDown.maxDistance !== undefined) {
var dist = Vec3.distance(mouseDown.pos, { x: event.x, y: event.y });
if (dist > mouseDown.maxDistance) {
mouseDown.maxDistance = dist;
}
}
}
function onMouseUp(event) {
if (event.isLeftButton) {
var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
if (overlay === mouseDown.overlay) {
if (overlay.attachedPanel === bluePanel) {
if (overlay && overlay === mouseDown.overlay) {
if (overlay.parentPanel === bluePanel) {
overlay.destroy();
} else if (overlay) {
var oldPos = overlay.offsetPosition;
var newPos = {
x: Number(oldPos.x),
y: Number(oldPos.y),
z: Number(oldPos.z) + 0.1
};
overlay.offsetPosition = newPos;
} else {
overlay.offsetPosition = Vec3.sum(overlay.offsetPosition, { x: 0, y: 0, z: -0.1 });
}
}
}
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
if (event.isRightButton && mouseDown.maxDistance < 10) {
mainPanel.visible = !mainPanel.visible;
}
}
@ -165,5 +191,6 @@ function onScriptEnd() {
}
Controller.mousePressEvent.connect(onMouseDown);
Controller.mouseMoveEvent.connect(onMouseMove);
Controller.mouseReleaseEvent.connect(onMouseUp);
Script.scriptEnding.connect(onScriptEnd);
Script.scriptEnding.connect(onScriptEnd);

View file

@ -0,0 +1,439 @@
//
// widgets-example.js
// games
//
// 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 paddingX = 8;
var paddingY = 8;
var buttonWidth = 30;
var buttonHeight = 30;
var ICONS_URL = 'https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/images/';
var panelX = 1250;
var panelY = 500;
var panelWidth = 50;
var panelHeight = 210;
// var mainPanel = new UIPanel(panelX, panelY, panelWidth, panelHeight);
// var systemViewButton = mainPanel.addImage('solarsystems');
// var zoomButton = mainPanel.addImage('magnifier');
// var satelliteButton = mainPanel.addImage('satellite');
// var settingsButton = mainPanel.addImage('settings');
// var stopButton = mainPanel.addImage('close');
//
// mainPanel.show();
//
// var systemViewPanel = new UIPanel(panelX - 120, panelY, 120, 40);
// var reverseButton = systemViewPanel.addImage('reverse');
// var pauseButton = systemViewPanel.addImage('playpause');
// var forwardButton = systemViewPanel.addImage('forward');
//
// var zoomPanel = new UIPanel(panelX - 60, panelY + buttonHeight + paddingY, 650, 50);
// for (var i = 0; i < planets.length; ++i) {
// zoomPanel.addText(planets[i].name);
// }
Script.include('../libraries/uiwidgets.js');
UI.setDefaultVisibility(true);
UI.setErrorHandler(function(err) {
teardown();
// print(err);
// Script.stop();
});
// Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return settings.mouseMoveEvent(event); });
// Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return settings.mousePressEvent(event); });
// Controller.mouseDoublePressEvent.connect( function panelMouseDoublePressEvent(event) { return settings.mouseDoublePressEvent(event); });
// Controller.mouseReleaseEvent.connect(function(event) { return settings.mouseReleaseEvent(event); });
// Controller.keyPressEvent.connect(function(event) { return settings.keyPressEvent(event); });
// var ICON_WIDTH = 50.0;
// var ICON_HEIGHT = 50.0;
var ICON_WIDTH = 40.0;
var ICON_HEIGHT = 40.0;
var ICON_COLOR = UI.rgba(45, 45, 45, 0.7);
var FOCUSED_COLOR = UI.rgba(250, 250, 250, 1.0);
var PANEL_BACKGROUND_COLOR = UI.rgba(50, 50, 50, 0.7);
var PANEL_PADDING = 7.0;
var PANEL_BORDER = 12.0;
var SUBPANEL_GAP = 1.0;
var icons = [];
function addImage(panel, iconId) {
var icon = panel.add(new UI.Image({
'imageURL': ICONS_URL + iconId + '.svg',
'width': ICON_WIDTH,
'height': ICON_HEIGHT,
'color': ICON_COLOR,
'alpha': ICON_COLOR.a
}));
icons.push(icon);
return icon;
}
var panels = [];
function addPanel (properties) {
properties.background = properties.background || {};
properties.background.backgroundColor = properties.background.backgroundColor ||
PANEL_BACKGROUND_COLOR;
properties.background.backgroundAlpha = properties.background.backgroundAlpha ||
PANEL_BACKGROUND_COLOR.a;
properties.padding = properties.padding || { x: PANEL_PADDING, y: PANEL_PADDING };
properties.border = properties.border || { x: PANEL_BORDER, y: PANEL_BORDER };
var panel = new UI.WidgetStack(properties);
panels.push(panel);
return panel;
}
function makeDraggable (panel, target) {
if (!target)
target = panel;
var dragStart = null;
var initialPos = null;
panel.addAction('onDragBegin', function (event) {
dragStart = { x: event.x, y: event.y };
initialPos = { x: target.position.x, y: target.position.y };
});
panel.addAction('onDragUpdate', function (event) {
target.setPosition(
initialPos.x + event.x - dragStart.x,
initialPos.y + event.y - dragStart.y
);
UI.updateLayout();
});
panel.addAction('onDragEnd', function () {
dragStart = dragEnd = null;
});
}
// var panelContainer = new UI.WidgetContainer();
// panelContainer.setPosition(500, 250);
// panelContainer.setVisible(true);
var demoPane = addPanel({ dir: '+y' });
var demoLabel = demoPane.add(new UI.Label({
text: "< no events >",
width: 400, height: 20
}));
var demoButton = demoPane.add(new UI.Box({
width: 200, height: 80,
text: "Button"
}));
function setText(text) {
return function () {
demoLabel.setText(text);
UI.updateLayout();
};
}
function addDebugActions(widget, msg, actions) {
actions.forEach(function(action) {
widget.addAction(action, setText(action + " " + msg + widget));
});
}
var debugEvents = [
'onMouseOver',
'onMouseExit',
'onMouseDown',
'onMouseUp',
'onDragBegin',
'onDragEnd',
'onDragUpdate'
];
addDebugActions(demoPane, "(container) ", debugEvents);
addDebugActions(demoButton, "(button) ", debugEvents);
addDebugActions(demoLabel, "(label) ", debugEvents);
// demoPane.addAction('onMouseOver', setText("onMouseOver " + demoPane));
// demoPane.addAction('onMouseExit', setText("onMouseExit " + demoPane));
// demoPane.addAction('onMouseDown', setText("onMouseDown " + demoPane));
// demoPane.addAction('onMouseUp', setText("onMouseUp " + demoPane));
makeDraggable(demoPane, demoPane);
demoPane.setPosition(600, 200);
// demoButton.addAction('onMouseOver', setText("onMouseOver " + demoButton));
// demoButton.addAction('onMouseExit', setText("onMouseExit " + demoButton));
// demoButton.addAction()
// var resizablePanel = new UI.Label({
// text: "Resizable panel",
// width: 200, height: 200,
// backgroundAlpha: 0.5
// });
// resizablePanel.setPosition(1100, 200);
var debugToggle = new UI.Box({
text: "debug", width: 150, height: 20
});
debugToggle.setPosition(200, 0);
debugToggle.addAction('onClick', function () {
UI.debug.setVisible(!UI.debug.isVisible());
});
// debugEvents.forEach(function (action) {
// resizablePanel.addAction(action, function (event, widget) {
// widget.setText(action + " " + widget);
// });
// })
function join(obj) {
var s = "{";
var sep = "\n";
for (var k in obj) {
s += sep + k + ": " + (""+obj[k]).replace("\n", "\n");
sep = ",\n";
}
if (s.length > 1)
return s + " }";
return s + "}";
}
// resizablePanel.getOverlay().update({
// text: "" + join(resizablePanel.actions)
// });
setText = addDebugActions = undefined;
var tooltipWidget = new UI.Label({
text: "<tooltip>",
width: 500, height: 20,
visible: false
});
function addTooltip (widget, text) {
widget.addAction('onMouseOver', function (event, widget) {
tooltipWidget.setVisible(true);
tooltipWidget.setPosition(widget.position.x + widget.getWidth() + 20, widget.position.y);
tooltipWidget.setText(text);
UI.updateLayout();
});
widget.addAction('onMouseExit', function () {
tooltipWidget.setVisible(false);
UI.updateLayout();
});
}
var mainPanel = addPanel({ dir: '+y' });
mainPanel.setPosition(500, 250);
mainPanel.setVisible(true);
var systemViewButton = addImage(mainPanel, 'solarsystems');
var zoomButton = addImage(mainPanel, 'magnifier');
var satelliteButton = addImage(mainPanel, 'satellite');
var settingsButton = addImage(mainPanel, 'settings');
var stopButton = addImage(mainPanel, 'close');
addTooltip(systemViewButton, "system view");
addTooltip(zoomButton, "zoom");
addTooltip(satelliteButton, "satelite view");
addTooltip(settingsButton, "settings");
addTooltip(stopButton, "exit");
var systemViewPanel = addPanel({ dir: '+x', visible: false });
var reverseButton = addImage(systemViewPanel, 'reverse');
var pauseButton = addImage(systemViewPanel, 'playpause');
var forwardButton = addImage(systemViewPanel, 'forward');
var zoomPanel = addPanel({ dir: '+y', visible: true });
var label = new UI.Label({
text: "Foo",
width: 120,
height: 15,
color: UI.rgb(245, 290, 20),
alpha: 1.0,
backgroundColor: UI.rgb(10, 10, 10),
backgroundAlpha: 0.0
});
zoomPanel.add(label);
label.addAction('onMouseOver', function () {
label.setText("Bar");
UI.updateLayout();
});
label.addAction('onMouseExit', function () {
label.setText("Foo");
UI.updateLayout();
});
label.setText("Label id: " + label.id + ", parent id " + label.parent.id);
label.parent.addAction('onMouseOver', function () {
label.setText("on parent");
UI.updateLayout();
});
label.parent.addAction('onMouseExit', function () {
label.setText('exited parent');
UI.updateLayout();
});
var sliderLayout = zoomPanel.add(new UI.WidgetStack({
dir: '+x', visible: true, backgroundAlpha: 0.0
}));
var sliderLabel = sliderLayout.add(new UI.Label({
text: " ", width: 45, height: 20
}));
var slider = sliderLayout.add(new UI.Slider({
value: 10, maxValue: 100, minValue: 0,
width: 300, height: 20,
backgroundColor: UI.rgb(10, 10, 10),
backgroundAlpha: 1.0,
slider: { // slider knob
width: 30,
height: 18,
backgroundColor: UI.rgb(120, 120, 120),
backgroundAlpha: 1.0
}
}));
sliderLabel.setText("" + (+slider.getValue().toFixed(1)));
slider.onValueChanged = function (value) {
sliderLabel.setText("" + (+value.toFixed(1)));
UI.updateLayout();
}
var checkBoxLayout = zoomPanel.add(new UI.WidgetStack({
dir: '+x', visible: true, backgroundAlpha: 0.0
}));
// var padding = checkBoxLayout.add(new UI.Label({
// text: " ", width: 45, height: 20
// }));
var checkBoxLabel = checkBoxLayout.add(new UI.Label({
text: "set red", width: 60, height: 20,
backgroundAlpha: 0.0
}));
checkBoxLabel.setText("set red");
var defaultColor = UI.rgb(10, 10, 10);
var redColor = UI.rgb(210, 80, 80);
var checkbox = checkBoxLayout.add(new UI.Checkbox({
width: 20, height: 20, padding: { x: 3, y: 3 },
backgroundColor: defaultColor,
backgroundAlpha: 0.9,
checked: false,
onValueChanged: function (red) {
zoomPanel.getOverlay().update({
// backgroundAlpha: 0.1,
backgroundColor: red ? redColor : defaultColor
});
}
}));
addImage(zoomPanel, 'reverse');
UI.updateLayout();
var subpanels = [ systemViewPanel, zoomPanel ];
function hideSubpanelsExcept (panel) {
subpanels.forEach(function (x) {
if (x != panel) {
x.setVisible(false);
}
});
}
function attachPanel (panel, button) {
button.addAction('onClick', function () {
hideSubpanelsExcept(panel);
panel.setVisible(!panel.isVisible());
UI.updateLayout();
})
UI.addAttachment(panel, button, function (target, rel) {
target.setPosition(
rel.position.x - (target.getWidth() + target.border.x + SUBPANEL_GAP),
rel.position.y - target.border.y
);
});
}
attachPanel(systemViewPanel, systemViewButton);
attachPanel(zoomPanel, zoomButton);
var addColorToggle = function (widget) {
widget.addAction('onMouseOver', function () {
widget.setColor(FOCUSED_COLOR);
});
widget.addAction('onMouseExit', function () {
widget.setColor(ICON_COLOR);
});
}
reverseButton.addAction('onClick', function() {});
systemViewPanel.addAction('onMouseOver', function() {
hideSubpanels();
UI.updateLayout();
});
zoomButton.addAction('onClick', function() {
hideSubpanels();
UI.updateLayout();
});
UI.updateLayout();
stopButton.addAction('onClick', function() {
// Script.stop();
teardown();
});
// Panel drag behavior
// (click + drag on border to drag)
(function () {
var dragged = null;
this.startDrag = function (dragAction) {
dragged = dragAction;
}
this.updateDrag = function (event) {
if (dragged) {
print("Update drag");
dragged.updateDrag(event);
}
}
this.clearDrag = function (event) {
if (dragged)
print("End drag");
dragged = null;
}
})();
var buttons = icons;
buttons.map(addColorToggle);
panels.map(function (panel) { makeDraggable(panel, mainPanel); });
// Cleanup script resources
function teardown() {
UI.teardown();
// etc...
};
var inputHandler = {
onMouseMove: function (event) {
updateDrag(event);
UI.handleMouseMove(event);
},
onMousePress: function (event) {
UI.handleMousePress(event);
},
onMouseRelease: function (event) {
clearDrag(event);
UI.handleMouseRelease(event);
}
};
Controller.mousePressEvent.connect(inputHandler.onMousePress);
Controller.mouseMoveEvent.connect(inputHandler.onMouseMove);
Controller.mouseReleaseEvent.connect(inputHandler.onMouseRelease);
Script.scriptEnding.connect(teardown);

251
examples/fireworks.js Normal file
View file

@ -0,0 +1,251 @@
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");
var audioOptions = {
volume: 0.9,
position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()))
};
var DISTANCE_FROM_CAMERA = 7.0;
var bluePalette = [{
red: 0,
green: 206,
blue: 209
}, {
red: 173,
green: 216,
blue: 230
}, {
red: 0,
green: 191,
blue: 255
}];
var greenPalette = [{
red: 152,
green: 251,
blue: 152
}, {
red: 127,
green: 255,
blue: 0
}, {
red: 50,
green: 205,
blue: 50
}];
var redPalette = [{
red: 255,
green: 20,
blue: 147
}, {
red: 255,
green: 69,
blue: 0
}, {
red: 255,
green: 90,
blue: 120
}];
var COLOR_RED = {red: 255, green: 0, blue: 0 };
var COLOR_GREEN = {red: 0, green: 255, blue: 0};
var COLOR_BLUE = {red: 0, green: 0, blue: 255};
var iconsX = 700;
var iconsY = 660;
var ICON_SIZE = 30;
var redIcon = Overlays.addOverlay("text", {
backgroundColor: COLOR_RED,
x: iconsX,
y: iconsY,
width: ICON_SIZE,
height: ICON_SIZE,
alpha: 0.0,
backgroundAlpha: 1.0,
visible: true
});
var greenIcon = Overlays.addOverlay("text", {
backgroundColor: COLOR_GREEN,
x: iconsX + 50,
y: iconsY,
width: ICON_SIZE,
height: ICON_SIZE,
alpha: 0.0,
backgroundAlpha: 1.0,
visible: true
});
var blueIcon = Overlays.addOverlay("text", {
backgroundColor: COLOR_BLUE,
x: iconsX + 100,
y: iconsY,
width: ICON_SIZE,
height: ICON_SIZE,
alpha: 0.0,
backgroundAlpha: 1.0,
visible: true
});
var NUM_BURSTS = 11;
var SPEED = 6.0;
var rockets = [];
Rocket = function(point, colorPalette) {
//default to blue palette if no palette passed in
this.colors = colorPalette;
this.point = point;
this.bursts = [];
this.burst = false;
this.emitRate = randInt(80, 120);
this.emitStrength = randInt(5.0, 7.0);
this.rocket = Entities.addEntity({
type: "Sphere",
position: this.point,
dimensions: {
x: 0.07,
y: 0.07,
z: 0.07
},
color: {
red: 240,
green: 240,
blue: 240
}
});
this.animationSettings = JSON.stringify({
fps: 40,
frameIndex: 0,
running: true,
firstFrame: 0,
lastFrame: 20,
loop: false
});
this.direction = {
x: randFloat(-0.4, 0.4),
y: 1.0,
z: 0.0
}
this.time = 0.0;
this.timeout = randInt(15, 40);
};
Rocket.prototype.update = function(deltaTime) {
this.time++;
Entities.editEntity(this.rocket, {
velocity: Vec3.multiply(SPEED, this.direction)
});
var position = Entities.getEntityProperties(this.rocket).position;
if (this.time > this.timeout) {
this.explode(position);
return;
}
};
Rocket.prototype.explode = function(position) {
Audio.playSound(fireSound, audioOptions);
Entities.editEntity(this.rocket, {
velocity: {
x: 0,
y: 0,
z: 0
}
});
var colorIndex = 0;
for (var i = 0; i < NUM_BURSTS; ++i) {
var color = this.colors[colorIndex];
print(JSON.stringify(color));
this.bursts.push(Entities.addEntity({
type: "ParticleEffect",
animationSettings: this.animationSettings,
position: position,
textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png',
emitRate: this.emitRate,
emitStrength: this.emitStrength,
emitDirection: {
x: Math.pow(-1, i) * randFloat(0.0, 1.4),
y: 1.0,
z: 0.0
},
color: color,
lifespan: 1.0,
visible: true,
locked: false
}));
if (colorIndex < this.colors.length - 1) {
colorIndex++;
}
}
this.burst = true;
Entities.deleteEntity(this.rocket);
};
//var lastLoudness;
var LOUDNESS_RADIUS_RATIO = 10;
function update(deltaTime) {
for (var i = 0; i < rockets.length; i++) {
if (!rockets[i].burst) {
rockets[i].update();
}
}
}
function randFloat(min, max) {
return Math.random() * (max - min) + min;
}
function randInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function computeWorldPoint(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DISTANCE_FROM_CAMERA);
return Vec3.sum(Camera.getPosition(), addVector);
}
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if(clickedOverlay === redIcon) {
rockets.push(new Rocket(computeWorldPoint(event), redPalette));
} else if (clickedOverlay === greenIcon) {
rockets.push(new Rocket(computeWorldPoint(event), greenPalette));
} else if (clickedOverlay === blueIcon) {
rockets.push(new Rocket(computeWorldPoint(event), bluePalette));
}
}
function cleanup() {
Overlays.deleteOverlay(redIcon);
Overlays.deleteOverlay(greenIcon);
Overlays.deleteOverlay(blueIcon);
for (var i = 0; i < rockets.length; ++i) {
Entities.deleteEntity(rockets[i].rocket);
for (var j = 0; j < NUM_BURSTS; ++j) {
Entities.deleteEntity(rockets[i].bursts[j]);
}
}
}
Script.update.connect(update);
Script.scriptEnding.connect(cleanup);
Controller.mousePressEvent.connect(mousePressEvent);

78
examples/gridTest.js Normal file
View file

@ -0,0 +1,78 @@
//
// Created by Philip Rosedale on July 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
//
// Creates a rectangular grid of objects, starting at the origin and proceeding along the X/Z plane.
// Useful for testing the rendering, LOD, and octree storage aspects of the system.
//
// Note that when creating things quickly, the entity server will ignore data if we send updates too quickly.
// like Internet MTU, these rates are set by th domain operator, so in this script there is a RATE_PER_SECOND
// variable letting you set this speed. If entities are missing from the grid after a relog, this number
// being too high may be the reason.
var SIZE = 10.0;
var SEPARATION = 20.0;
var ROWS_X = 30;
var ROWS_Z = 30;
var TYPE = "Sphere"; // Right now this can be "Box" or "Model" or "Sphere"
var MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/props/LowPolyIsland/CypressTreeGroup.fbx";
var MODEL_DIMENSION = { x: 33, y: 16, z: 49 };
var RATE_PER_SECOND = 1000; // The entity server will drop data if we create things too fast.
var SCRIPT_INTERVAL = 100;
var LIFETIME = 600; // By default, these entities will live in the server for 10 minutes
var addRandom = false;
var x = 0;
var z = 0;
var totalCreated = 0;
Script.setInterval(function () {
if (!Entities.serversExist() || !Entities.canRez()) {
return;
}
var numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0);
for (var i = 0; i < numToCreate; i++) {
var position = { x: SIZE + (x * SEPARATION), y: SIZE, z: SIZE + (z * SEPARATION) };
if (TYPE == "Model") {
Entities.addEntity({
type: TYPE,
name: "gridTest",
modelURL: MODEL_URL,
position: position,
dimensions: MODEL_DIMENSION,
ignoreCollisions: true,
collisionsWillMove: false,
lifetime: LIFETIME
});
} else {
Entities.addEntity({
type: TYPE,
name: "gridTest",
position: position,
dimensions: { x: SIZE, y: SIZE, z: SIZE },
color: { red: x / ROWS_X * 255, green: 50, blue: z / ROWS_Z * 255 },
ignoreCollisions: true,
collisionsWillMove: false,
lifetime: LIFETIME
});
}
totalCreated++;
x++;
if (x == ROWS_X) {
x = 0;
z++;
print("Created: " + totalCreated);
}
if (z == ROWS_Z) {
Script.stop();
}
}
}, SCRIPT_INTERVAL);

View file

@ -55,8 +55,8 @@ var warpLine = Overlays.addOverlay("line3d", {
var velocity = { x: 0, y: 0, z: 0 };
var VERY_LONG_TIME = 1000000.0;
var active = Menu.isOptionChecked("Enable VR Mode");
var prevVRMode = Menu.isOptionChecked("Enable VR Mode");
var active = HMD.active;
var prevVRMode = HMD.active;
var hmdControls = (function () {
@ -121,28 +121,28 @@ var hmdControls = (function () {
velocity = Vec3.sum(velocity, direction);
break;
case findAction("YAW_LEFT"):
if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) {
if (yawTimer < 0.0 && HMD.active) {
yawChange = yawChange + (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
yawTimer = CAMERA_UPDATE_TIME;
} else if (!Menu.isOptionChecked("Enable VR Mode")) {
} else if (!HMD.active) {
yawChange = yawChange + (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
}
break;
case findAction("YAW_RIGHT"):
if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) {
if (yawTimer < 0.0 && HMD.active) {
yawChange = yawChange - (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
yawTimer = CAMERA_UPDATE_TIME;
} else if (!Menu.isOptionChecked("Enable VR Mode")) {
} else if (!HMD.active) {
yawChange = yawChange - (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
}
break;
case findAction("PITCH_DOWN"):
if (!Menu.isOptionChecked("Enable VR Mode")) {
if (!HMD.active) {
pitchChange = pitchChange - (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
}
break;
case findAction("PITCH_UP"):
if (!Menu.isOptionChecked("Enable VR Mode")) {
if (!HMD.active) {
pitchChange = pitchChange + (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
}
break;
@ -175,9 +175,9 @@ var hmdControls = (function () {
}
function update(dt) {
if (prevVRMode != Menu.isOptionChecked("Enable VR Mode")) {
active = Menu.isOptionChecked("Enable VR Mode");
prevVRMode = Menu.isOptionChecked("Enable VR Mode");
if (prevVRMode != HMD.active) {
active = HMD.active;
prevVRMode = HMD.active;
}
if (yawTimer >= 0.0) {

View file

@ -362,6 +362,9 @@
var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
var elXTextureURL = document.getElementById("property-x-texture-url");
var elYTextureURL = document.getElementById("property-y-texture-url");
var elZTextureURL = document.getElementById("property-z-texture-url");
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
@ -614,6 +617,9 @@
elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle;
elXTextureURL.value = properties.xTextureURL;
elYTextureURL.value = properties.yTextureURL;
elZTextureURL.value = properties.zTextureURL;
}
if (selected) {
@ -867,6 +873,9 @@
elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction);
elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL'));
elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL'));
elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL'));
elMoveSelectionToGrid.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({
@ -1063,7 +1072,22 @@
<option value='0'>marching cubes</option>
<option value='1'>cubic</option>
<option value='2'>edged cubic</option>
</select>
</select>
</div>
<div class="label">X-axis Texture URL</div>
<div class="value">
<input type="text" id="property-x-texture-url" class="url"></input>
</div>
<div class="label">Y-axis Texture URL</div>
<div class="value">
<input type="text" id="property-y-texture-url" class="url"></input>
</div>
<div class="label">Z-axis Texture URL</div>
<div class="value">
<input type="text" id="property-z-texture-url" class="url"></input>
</div>
</div>

View file

@ -98,8 +98,8 @@ EntityPropertyDialogBox = (function () {
index++;
}
if (properties.type == "PolyVox") {
array.push({ label: "Voxel Space Size:", type: "header" });
if (properties.type == "PolyVox") {
array.push({ label: "Voxel Space Size:", type: "header" });
index++;
array.push({ label: "X:", value: properties.voxelVolumeSize.x.toFixed(decimals) });
@ -109,9 +109,16 @@ EntityPropertyDialogBox = (function () {
array.push({ label: "Z:", value: properties.voxelVolumeSize.z.toFixed(decimals) });
index++;
array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle });
index++;
}
array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle });
index++;
array.push({ label: "X-axis Texture URL:", value: properties.xTextureURL });
index++;
array.push({ label: "Y-axis Texture URL:", value: properties.yTextureURL });
index++;
array.push({ label: "Z-axis Texture URL:", value: properties.zTextureURL });
index++;
}
array.push({ label: "Position:", type: "header" });
index++;
@ -348,14 +355,17 @@ EntityPropertyDialogBox = (function () {
properties.backgroundColor.blue = array[index++].value;
}
if (properties.type == "PolyVox") {
if (properties.type == "PolyVox") {
properties.shapeType = array[index++].value;
index++; // skip header
properties.voxelVolumeSize.x = array[index++].value;
properties.voxelVolumeSize.y = array[index++].value;
properties.voxelVolumeSize.z = array[index++].value;
properties.voxelSurfaceStyle = array[index++].value;
index++; // skip header
properties.voxelVolumeSize.x = array[index++].value;
properties.voxelVolumeSize.y = array[index++].value;
properties.voxelVolumeSize.z = array[index++].value;
properties.voxelSurfaceStyle = array[index++].value;
properties.xTextureURL = array[index++].value;
properties.yTextureURL = array[index++].value;
properties.zTextureURL = array[index++].value;
}
index++; // skip header

View file

@ -340,7 +340,7 @@ SelectionDisplay = (function () {
leftMargin: 0,
});
var grabberMoveUp = Overlays.addOverlay("billboard", {
var grabberMoveUp = Overlays.addOverlay("image3d", {
url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg",
position: { x:0, y: 0, z: 0},
color: handleColor,
@ -609,7 +609,7 @@ SelectionDisplay = (function () {
minorTickMarksColor: { red: 0, green: 0, blue: 0 },
});
var yawHandle = Overlays.addOverlay("billboard", {
var yawHandle = Overlays.addOverlay("image3d", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: handleColor,
@ -622,7 +622,7 @@ SelectionDisplay = (function () {
});
var pitchHandle = Overlays.addOverlay("billboard", {
var pitchHandle = Overlays.addOverlay("image3d", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: handleColor,
@ -635,7 +635,7 @@ SelectionDisplay = (function () {
});
var rollHandle = Overlays.addOverlay("billboard", {
var rollHandle = Overlays.addOverlay("image3d", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: handleColor,

View file

@ -61,7 +61,7 @@ LightOverlayManager = function() {
// Allocate or get an unused overlay
function getOverlay() {
if (unusedOverlays.length == 0) {
var overlay = Overlays.addOverlay("billboard", {
var overlay = Overlays.addOverlay("image3d", {
});
allOverlays.push(overlay);
} else {

View file

@ -5,10 +5,13 @@
// Created by Zander Otavka on 7/24/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
//
// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods.
// Instead of:
//
// var billboard = Overlays.addOverlay("billboard", { visible: false });
// var billboard = Overlays.addOverlay("image3d", { visible: false });
// ...
// Overlays.editOverlay(billboard, { visible: true });
// ...
@ -16,35 +19,66 @@
//
// You can now do:
//
// var billboard = new BillboardOverlay({ visible: false });
// var billboard = new Image3DOverlay({ visible: false });
// ...
// billboard.visible = true;
// ...
// billboard.destroy();
//
// See more on usage below.
// More on usage below. Examples in `examples/example/overlayPanelExample.js`.
//
// Note that including this file will delete Overlays from the global scope. All the
// functionality of Overlays is represented here, just better. If you try to use Overlays in
// tandem, there may be performance problems or nasty surprises.
// Note that including this file will delete `Overlays` from the global scope. All the
// functionality of `Overlays` is represented here, just better. If you try to use `Overlays`
// in tandem, there may be performance problems or nasty surprises.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
// Delete `Overlays` from the global scope.
var Overlays = this.Overlays;
delete this.Overlays;
var ABSTRACT = null;
var overlays = {};
var panels = {};
var overlayTypes;
var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay;
var overlayTypes = {};
function generateOverlayClass(superclass, type, properties) {
var that;
if (type == ABSTRACT) {
that = function(type, params) {
superclass.call(this, type, params);
};
} else {
that = function(params) {
superclass.call(this, type, params);
};
overlayTypes[type] = that;
}
that.prototype = new superclass();
that.prototype.constructor = that;
properties.forEach(function(prop) {
Object.defineProperty(that.prototype, prop, {
get: function() {
return Overlays.getProperty(this._id, prop);
},
set: function(newValue) {
var keyValuePair = {};
keyValuePair[prop] = newValue;
this.setProperties(keyValuePair);
},
configurable: false
});
});
return that;
}
//
// Create a new JavaScript object for an overlay of given ID.
//
@ -55,10 +89,6 @@
}
var overlay = new overlayTypes[type]();
overlay._id = id;
var panelID = Overlays.getAttachedPanel(id)
if (panelID && panelID in panels) {
panels[panelID].addChild(overlay);
}
overlays[id] = overlay;
return overlay;
}
@ -74,7 +104,7 @@
//
function findOverlay(id, knownOverlaysOnly, searchList) {
if (id > 0) {
knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList);
knownOverlaysOnly = Boolean(knownOverlaysOnly);
searchList = searchList || overlays;
var foundOverlay = searchList[id];
if (foundOverlay) {
@ -87,257 +117,187 @@
return null;
}
//
// Perform global scoped operations on overlays, such as finding by ray intersection.
// Create a new JavaScript object for a panel of given ID.
//
OverlayManager = {
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
var rayPickResult = Overlays.findRayIntersection(pickRay);
print("raypick " + rayPickResult.overlayID);
if (rayPickResult.intersects) {
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
}
function makePanelFromId(id) {
if (!Overlays.isAddedPanel(id)) {
return null;
},
findAtPoint: function(point, knownOverlaysOnly, searchList) {
var foundID = Overlays.getOverlayAtPoint(point);
print("at point " + foundID);
if (foundID) {
return findOverlay(foundID, knownOverlaysOnly, searchList);
} else {
var pickRay = Camera.computePickRay(point.x, point.y);
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
}
},
makeSearchList: function(overlayArray) {
var searchList = {};
overlayArray.forEach(function(overlay){
searchList[overlay._id] = overlay;
});
return searchList;
}
};
var panel = new OverlayPanel();
panel._id = id;
overlays[id] = overlay;
return overlay;
}
//
// Object oriented abstraction layer for overlays.
// Get or create a panel object from the id.
//
// Usage:
// // Create an overlay
// var billboard = new BillboardOverlay({
// visible: true,
// isFacingAvatar: true,
// ignoreRayIntersections: false
// });
// @param knownOverlaysOnly (Optional: Boolean)
// If true, a new object will not be created.
// @param searchList (Optional: Object)
// Map of overlay id's and overlay objects. Can be generated with
// `OverlayManager.makeSearchList`.
//
// // Get a property
// var isVisible = billboard.visible;
//
// // Set a single property
// billboard.position = { x: 1, y: 3, z: 2 };
//
// // Set multiple properties at the same time
// billboard.setProperties({
// url: "http://images.com/overlayImage.jpg",
// dimensions: { x: 2, y: 2 }
// });
//
// // Clone an overlay
// var clonedBillboard = billboard.clone();
//
// // Remove an overlay from the world
// billboard.destroy();
//
// // Remember, there is a poor orphaned JavaScript object left behind. You should
// // remove any references to it so you don't accidentally try to modify an overlay that
// // isn't there.
// billboard = undefined;
//
(function() {
var ABSTRACT = null;
overlayTypes = {};
function generateOverlayClass(superclass, type, properties) {
var that;
if (type == ABSTRACT) {
that = function(type, params) {
superclass.call(this, type, params);
};
} else {
that = function(params) {
superclass.call(this, type, params);
};
overlayTypes[type] = that;
function findPanel(id, knownPanelsOnly, searchList) {
if (id > 0) {
knownPanelsOnly = Boolean(knownPanelsOnly);
searchList = searchList || panels;
var foundPanel = searchList[id];
if (foundPanel) {
return foundPanel;
}
if (!knownPanelsOnly) {
return makePanelFromId(id);
}
that.prototype = new superclass();
that.prototype.constructor = that;
properties.forEach(function(prop) {
Object.defineProperty(that.prototype, prop, {
get: function() {
return Overlays.getProperty(this._id, prop);
},
set: function(newValue) {
var keyValuePair = {};
keyValuePair[prop] = newValue;
this.setProperties(keyValuePair);
},
configurable: false
});
});
return that;
}
return null;
}
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
// properties list.
var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"];
Overlay = (function() {
var that = function(type, params) {
if (type && params) {
this._id = Overlays.addOverlay(type, params);
overlays[this._id] = this;
} else {
this._id = 0;
}
this._attachedPanelPointer = null;
};
that.prototype.constructor = that;
Object.defineProperty(that.prototype, "isLoaded", {
get: function() {
return Overlays.isLoaded(this._id);
}
});
Object.defineProperty(that.prototype, "attachedPanel", {
get: function() {
return this._attachedPanelPointer;
}
});
that.prototype.getTextSize = function(text) {
return Overlays.textSize(this._id, text);
};
that.prototype.setProperties = function(properties) {
Overlays.editOverlay(this._id, properties);
};
that.prototype.clone = function() {
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
};
that.prototype.destroy = function() {
Overlays.deleteOverlay(this._id);
};
return generateOverlayClass(that, ABSTRACT, [
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
"alphaPulse", "colorPulse", "visible", "anchor"
]);
})();
Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
"bounds", "x", "y", "width", "height"
]);
Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
]);
Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
generateOverlayClass(Overlay2D, "image", [
"subImage", "imageURL"
]);
generateOverlayClass(Overlay2D, "text", [
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
]);
generateOverlayClass(Planar3DOverlay, "text3d", [
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
"rightMargin", "bottomMargin", "isFacingAvatar"
]);
generateOverlayClass(Volume3DOverlay, "cube", [
"borderSize"
]);
generateOverlayClass(Volume3DOverlay, "sphere", [
]);
generateOverlayClass(Planar3DOverlay, "circle3d", [
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
]);
generateOverlayClass(Planar3DOverlay, "rectangle3d", [
]);
generateOverlayClass(Base3DOverlay, "line3d", [
"start", "end"
]);
generateOverlayClass(Planar3DOverlay, "grid", [
"minorGridWidth", "majorGridEvery"
]);
generateOverlayClass(Volume3DOverlay, "localmodels", [
]);
generateOverlayClass(Volume3DOverlay, "model", [
"url", "dimensions", "textures"
]);
generateOverlayClass(Planar3DOverlay, "billboard", [
"url", "subImage", "isFacingAvatar"
].concat(PANEL_ATTACHABLE_FIELDS));
})();
ImageOverlay = overlayTypes["image"];
TextOverlay = overlayTypes["text"];
Text3DOverlay = overlayTypes["text3d"];
Cube3DOverlay = overlayTypes["cube"];
Sphere3DOverlay = overlayTypes["sphere"];
Circle3DOverlay = overlayTypes["circle3d"];
Rectangle3DOverlay = overlayTypes["rectangle3d"];
Line3DOverlay = overlayTypes["line3d"];
Grid3DOverlay = overlayTypes["grid"];
LocalModelsOverlay = overlayTypes["localmodels"];
ModelOverlay = overlayTypes["model"];
BillboardOverlay = overlayTypes["billboard"];
function findOverlayOrPanel(id, knownObjectsOnly, searchList) {
return findOverlay(id, knownObjectsOnly, searchList) ||
findPanel(id, knownObjectsOnly, searchList);
}
//
// Object oriented abstraction layer for panels.
//
FloatingUIPanel = (function() {
var that = function(params) {
this._id = Overlays.addPanel(params);
this._children = [];
this._visible = Boolean(params.visible);
panels[this._id] = this;
this._attachedPanelPointer = null;
var Overlay = (function() {
var that = function(type, params) {
if (type && params) {
this._id = Overlays.addOverlay(type, params);
overlays[this._id] = this;
} else {
this._id = 0;
}
};
that.prototype.constructor = that;
var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"];
FIELDS.forEach(function(prop) {
Object.defineProperty(that.prototype, "isLoaded", {
get: function() {
return Overlays.isLoaded(this._id);
}
});
Object.defineProperty(that.prototype, "parentPanel", {
get: function() {
return findPanel(Overlays.getParentPanel(this._id));
}
});
that.prototype.getTextSize = function(text) {
return Overlays.textSize(this._id, text);
};
that.prototype.setProperties = function(properties) {
Overlays.editOverlay(this._id, properties);
};
that.prototype.clone = function() {
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
};
that.prototype.destroy = function() {
Overlays.deleteOverlay(this._id);
};
that.prototype.isPanelAttachable = function() {
return false;
};
return generateOverlayClass(that, ABSTRACT, [
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
"alphaPulse", "colorPulse", "visible", "anchor"
]);
})();
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
// properties list.
var PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"];
var Billboardable = ["isFacingAvatar"];
var Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
"bounds", "x", "y", "width", "height"
]);
var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
]);
var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
var Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [
].concat(PanelAttachable).concat(Billboardable));
Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; };
var Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
ImageOverlay = generateOverlayClass(Overlay2D, "image", [
"subImage", "imageURL"
]);
Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [
"url", "subImage"
]);
TextOverlay = generateOverlayClass(Overlay2D, "text", [
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
]);
Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
"rightMargin", "bottomMargin"
]);
Cube3DOverlay = generateOverlayClass(Volume3DOverlay, "cube", [
"borderSize"
]);
Sphere3DOverlay = generateOverlayClass(Volume3DOverlay, "sphere", [
]);
Circle3DOverlay = generateOverlayClass(Planar3DOverlay, "circle3d", [
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
]);
Rectangle3DOverlay = generateOverlayClass(Planar3DOverlay, "rectangle3d", [
]);
Line3DOverlay = generateOverlayClass(Base3DOverlay, "line3d", [
"start", "end"
]);
Grid3DOverlay = generateOverlayClass(Planar3DOverlay, "grid", [
"minorGridWidth", "majorGridEvery"
]);
LocalModelsOverlay = generateOverlayClass(Volume3DOverlay, "localmodels", [
]);
ModelOverlay = generateOverlayClass(Volume3DOverlay, "model", [
"url", "dimensions", "textures"
]);
OverlayPanel = (function() {
var that = function(params) {
this._id = Overlays.addPanel(params);
panels[this._id] = this;
};
that.prototype.constructor = that;
var props = [
"anchorPosition", "anchorPositionBinding", "anchorRotation", "anchorRotationBinding", "anchorScale", "visible"
].concat(PanelAttachable).concat(Billboardable)
props.forEach(function(prop) {
Object.defineProperty(that.prototype, prop, {
get: function() {
return Overlays.getPanelProperty(this._id, prop);
@ -351,78 +311,47 @@
});
});
var PSEUDO_FIELDS = [];
PSEUDO_FIELDS.push("children");
Object.defineProperty(that.prototype, "children", {
Object.defineProperty(that.prototype, "parentPanel", {
get: function() {
return this._children.slice();
return findPanel(Overlays.getParentPanel(this._id));
}
});
PSEUDO_FIELDS.push("visible");
Object.defineProperty(that.prototype, "visible", {
Object.defineProperty(that.prototype, "children", {
get: function() {
return this._visible;
},
set: function(visible) {
this._visible = visible;
this._children.forEach(function(child) {
child.visible = visible;
});
var idArray = Overlays.getPanelProperty(this._id, "children");
var objArray = [];
for (var i = 0; i < idArray.length; i++) {
objArray[i] = findOverlayOrPanel(idArray[i]);
}
return objArray;
}
});
that.prototype.addChild = function(child) {
if (child instanceof Overlay) {
Overlays.setAttachedPanel(child._id, this._id);
} else if (child instanceof FloatingUIPanel) {
child.setProperties({
anchorPosition: {
bind: "panel",
value: this._id
},
offsetRotation: {
bind: "panel",
value: this._id
}
});
}
child._attachedPanelPointer = this;
child.visible = this.visible;
this._children.push(child);
Overlays.setParentPanel(child._id, this._id);
return child;
};
that.prototype.removeChild = function(child) {
var i = this._children.indexOf(child);
if (i >= 0) {
if (child instanceof Overlay) {
Overlays.setAttachedPanel(child._id, 0);
} else if (child instanceof FloatingUIPanel) {
child.setProperties({
anchorPosition: {
bind: "myAvatar"
},
offsetRotation: {
bind: "myAvatar"
}
});
}
child._attachedPanelPointer = null;
this._children.splice(i, 1);
if (child.parentPanel === this) {
Overlays.setParentPanel(child._id, 0);
}
};
that.prototype.setProperties = function(properties) {
for (var i in PSEUDO_FIELDS) {
if (properties[PSEUDO_FIELDS[i]] !== undefined) {
this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]];
}
}
Overlays.editPanel(this._id, properties);
};
that.prototype.setChildrenVisible = function() {
this.children.forEach(function(child) {
child.visible = true;
if (child.setChildrenVisible !== undefined) {
child.setChildrenVisible();
}
});
};
that.prototype.destroy = function() {
Overlays.deletePanel(this._id);
};
@ -431,10 +360,39 @@
})();
OverlayManager = {
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
var rayPickResult = Overlays.findRayIntersection(pickRay);
if (rayPickResult.intersects) {
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
}
return null;
},
findAtPoint: function(point, knownOverlaysOnly, searchList) {
var foundID = Overlays.getOverlayAtPoint(point);
if (foundID) {
return findOverlay(foundID, knownOverlaysOnly, searchList);
} else {
var pickRay = Camera.computePickRay(point.x, point.y);
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
}
},
makeSearchList: function(array) {
var searchList = {};
array.forEach(function(object) {
searchList[object._id] = object;
});
return searchList;
}
};
// Threadsafe cleanup of JavaScript objects.
function onOverlayDeleted(id) {
if (id in overlays) {
if (overlays[id]._attachedPanelPointer) {
overlays[id]._attachedPanelPointer.removeChild(overlays[id]);
if (overlays[id].parentPanel) {
overlays[id].parentPanel.removeChild(overlays[id]);
}
delete overlays[id];
}
@ -442,10 +400,9 @@
function onPanelDeleted(id) {
if (id in panels) {
panels[id]._children.forEach(function(child) {
print(JSON.stringify(child.destroy));
child.destroy();
});
if (panels[id].parentPanel) {
panels[id].parentPanel.removeChild(panels[id]);
}
delete panels[id];
}
}

File diff suppressed because it is too large Load diff

View file

@ -148,7 +148,7 @@ KeyboardKey = (function(keyboard, keyProperties) {
};
for (var i = 0; i < this.bounds.length; i++) {
if (THREE_D_MODE) {
this.overlays.push(Overlays.addOverlay("billboard", {
this.overlays.push(Overlays.addOverlay("image3d", {
scale: 1,
rotation: MyAvatar.rotation,
isFacingAvatar: false,
@ -202,7 +202,7 @@ Keyboard = (function(params) {
return windowDimensions.y - this.height();
};
if (THREE_D_MODE) {
this.background = Overlays.addOverlay("billboard", {
this.background = Overlays.addOverlay("image3d", {
scale: 1,
position: MyAvatar.position,
rotation: MyAvatar.rotation,

View file

@ -130,7 +130,6 @@ var heights = [];
var myAlpha = [];
var arrays = [];
var isOnHMD = false,
ENABLE_VR_MODE = "Enable VR Mode",
NOTIFICATIONS_3D_DIRECTION = 0.0, // Degrees from avatar orientation.
NOTIFICATIONS_3D_DISTANCE = 0.6, // Horizontal distance from avatar position.
NOTIFICATIONS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes.
@ -260,7 +259,7 @@ function notify(notice, button, height) {
positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y);
notifications.push((Overlays.addOverlay("text3d", notice)));
buttons.push((Overlays.addOverlay("billboard", button)));
buttons.push((Overlays.addOverlay("image3d", button)));
overlay3DDetails.push({
notificationOrientation: positions.notificationOrientation,
notificationPosition: positions.notificationPosition,
@ -414,7 +413,7 @@ function update() {
j,
k;
if (isOnHMD !== Menu.isOptionChecked(ENABLE_VR_MODE)) {
if (isOnHMD !== HMD.active) {
while (arrays.length > 0) {
deleteNotification(0);
}
@ -596,7 +595,7 @@ function menuItemEvent(menuItem) {
LODManager.LODDecreased.connect(function() {
var warningText = "\n"
+ "Due to the complexity of the content, the \n"
+ "level of detail has been decreased."
+ "level of detail has been decreased. "
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();

155
examples/particleDance.js Normal file
View file

@ -0,0 +1,155 @@
(function() {
var NUM_BURSTS = 3;
var NUM_EMITTERS_PER_BURST = 11;
var RANGE = 5.0;
var AUDIO_RANGE = 0.5 * RANGE;
var DIST_BETWEEN_BURSTS = 1.0;
var LOUDNESS_RADIUS_RATIO = 10;
var TEXTURE_PATH = 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png';
var cameraAxis = Quat.getFront(Camera.getOrientation());
var center = Vec3.sum(Camera.getPosition(), Vec3.multiply(RANGE, cameraAxis));
var audioPosition = Vec3.sum(Camera.getPosition(), Vec3.multiply(AUDIO_RANGE, cameraAxis));
var song = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/songs/Made%20In%20Heights%20-%20Forgiveness.wav");
var audioOptions = {
volume: 0.9, position: audioPosition
};
var DISTANCE_FROM_CAMERA = 7.0;
var colorPalette = [{
red: 0,
green: 206,
blue: 209
}, {
red: 173,
green: 216,
blue: 230
}, {
red: 0,
green: 191,
blue: 255
}];
var bursts = [];
var audioStats;
Burst = function(point) {
if (!audioStats) {
audioStats = Audio.playSound(song, audioOptions);
}
this.point = point;
this.emitters = [];
this.emitRate = randInt(80, 120);
this.emitStrength = randInt(4.0, 6.0);
this.animationSettings = JSON.stringify({
fps: 10,
frameIndex: 0,
running: true,
firstFrame: 0,
lastFrame: 50,
loop: true
});
this.direction = {
x: randFloat(-0.3, 0.3),
y: 1.0,
z: 0.0
}
this.base = Entities.addEntity({
type: "Sphere",
position: this.point,
dimensions: {
x: 0.05,
y: 0.05,
z: 0.05
},
color: {
red: 240,
green: 240,
blue: 240
}
});
for (var i = 0; i < NUM_EMITTERS_PER_BURST; ++i) {
var colorIndex = randInt(0, colorPalette.length - 1);
var color = colorPalette[colorIndex];
this.emitters.push(Entities.addEntity({
type: "ParticleEffect",
animationSettings: this.animationSettings,
position: this.point,
textures: TEXTURE_PATH,
emitRate: this.emitRate,
emitStrength: this.emitStrength,
emitDirection: {
x: Math.pow(-1, i) * randFloat(0.0, 0.4),
y: 1.0,
z: 0.0
},
color: color,
lifespan: 1.0,
visible: true,
locked: false
}));
}
};
var nextPosition = center;
var posOrNeg = -1;
for (var i = 0; i < NUM_BURSTS; ++i) {
posOrNeg *= -1;
bursts.push(new Burst(nextPosition));
var offset = {
x: RANGE/(i+2) * posOrNeg,
y: 0,
z: 0
};
var nextPosition = Vec3.sum(nextPosition, offset);
}
function update(deltaTime) {
for (var i = 0; i < NUM_BURSTS; i++) {
if (audioStats && audioStats.loudness > 0.0) {
for (var j = 0; j < NUM_EMITTERS_PER_BURST; ++j) {
Entities.editEntity(bursts[i].emitters[j], {
particleRadius: audioStats.loudness / LOUDNESS_RADIUS_RATIO
});
}
}
}
}
function randFloat(min, max) {
return Math.random() * (max - min) + min;
}
function randInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
this.cleanup = function() {
for (var i = 0; i < NUM_BURSTS; ++i) {
Entities.deleteEntity(bursts[i].base);
for (var j = 0; j < NUM_EMITTERS_PER_BURST; ++j) {
var emitter = bursts[i].emitters[j];
Entities.deleteEntity(emitter);
}
}
Audio.stop();
}
Script.update.connect(update);
})();
Script.scriptEnding.connect(cleanup);

View file

@ -41,7 +41,6 @@
SCALE_2D = 0.35, // Scale the SVGs for 2D display.
background3D = {},
bar3D = {},
ENABLE_VR_MODE_MENU_ITEM = "Enable VR Mode",
PROGRESS_3D_DIRECTION = 0.0, // Degrees from avatar orientation.
PROGRESS_3D_DISTANCE = 0.602, // Horizontal distance from avatar position.
PROGRESS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes.
@ -117,7 +116,7 @@
visible: false,
ignoreRayIntersection: true
});
bar3D.overlay = Overlays.addOverlay("billboard", {
bar3D.overlay = Overlays.addOverlay("image3d", {
url: BAR_URL,
subImage: { x: BAR_WIDTH, y: 0, width: BAR_WIDTH, height: BAR_HEIGHT },
scale: SCALE_3D * BAR_WIDTH,
@ -157,7 +156,7 @@
eyePosition,
avatarOrientation;
if (isOnHMD !== Menu.isOptionChecked(ENABLE_VR_MODE_MENU_ITEM)) {
if (isOnHMD !== HMD.active) {
deleteOverlays();
isOnHMD = !isOnHMD;
createOverlays();

View file

@ -175,7 +175,7 @@ function SeatIndicator(modelProperties, seatIndex) {
modelProperties.sittingPoints[seatIndex].rotation);
this.scale = MyAvatar.scale / 12;
this.sphere = Overlays.addOverlay("billboard", {
this.sphere = Overlays.addOverlay("image3d", {
subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight},
url: buttonImageUrl,
position: this.position,

View file

@ -1360,7 +1360,7 @@ var CHECK_MARK_COLOR = {
this.nextY = this.y + this.getHeight();
var item = new CollapsablePanelItem(name, this.x, this.nextY, textWidth, rawHeight, panel);
var item = new CollapsablePanelItem(name, this.x, this.nextY, textWidth, rawHeight);
item.isSubPanel = true;
this.nextY += 1.5 * item.height;

View file

@ -44,7 +44,7 @@ function mousePressEvent(event) {
}
}
// if the PolyVox entity is empty, we can't pick against its voxel. try picking against its
// if the PolyVox entity is empty, we can't pick against its "on" voxels. try picking against its
// bounding box, instead.
intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking
if (intersection.intersects) {

View file

@ -34,8 +34,12 @@ IceServer::IceServer(int argc, char* argv[]) :
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
// call our process datagrams slot when the UDP socket has packets ready
connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams);
// set processPacket as the verified packet callback for the udt::Socket
using std::placeholders::_1;
_serverSocket.setPacketHandler(std::bind(&IceServer::processPacket, this, _1));
// set packetVersionMatch as the verify packet operator for the udt::Socket
_serverSocket.setPacketFilterOperator(std::bind(&IceServer::packetVersionMatch, this, _1));
// setup our timer to clear inactive peers
QTimer* inactivePeerTimer = new QTimer(this);
@ -44,63 +48,65 @@ IceServer::IceServer(int argc, char* argv[]) :
}
void IceServer::processDatagrams() {
HifiSockAddr sendingSockAddr;
while (_serverSocket.hasPendingDatagrams()) {
// setup a buffer to read the packet into
int packetSizeWithHeader = _serverSocket.pendingDatagramSize();
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
PacketType headerType = NLPacket::typeInHeader(packet);
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
if (headerVersion == versionForPacketType(headerType)) {
return true;
} else {
qDebug() << "Packet version mismatch for packet" << headerType
<< "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr();
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
return false;
}
}
PacketType::Value packetType = packet->getType();
if (packetType == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
} else if (packetType == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(packet.get());
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
auto nlPacket = NLPacket::fromBase(std::move(packet));
if (nlPacket->getType() == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket);
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
} else if (nlPacket->getType() == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(nlPacket.get());
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*matchingPeer, &nlPacket->getSenderSockAddr());
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
}
}
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) {
// pull the UUID, public and private sock addrs for this peer
QUuid senderUUID;
@ -133,14 +139,13 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
}
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
auto peerPacket = Packet::create(PacketType::ICEServerPeerInformation);
auto peerPacket = NLPacket::create(PacketType::ICEServerPeerInformation);
// get the byte array for this peer
peerPacket->write(peer.toByteArray());
// write the current packet
_serverSocket.writeDatagram(peerPacket->getData(), peerPacket->getDataSize(),
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
_serverSocket.writePacket(*peerPacket, *destinationSockAddr);
}
void IceServer::clearInactivePeers() {

View file

@ -19,7 +19,8 @@
#include <NetworkPeer.h>
#include <HTTPConnection.h>
#include <HTTPManager.h>
#include <udt/Packet.h>
#include <NLPacket.h>
#include <udt/Socket.h>
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
@ -29,15 +30,16 @@ public:
IceServer(int argc, char* argv[]);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
private slots:
void processDatagrams();
void clearInactivePeers();
private:
SharedNetworkPeer addOrUpdateHeartbeatingPeer(Packet& incomingPacket);
bool packetVersionMatch(const udt::Packet& packet);
void processPacket(std::unique_ptr<udt::Packet> packet);
SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket);
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
QUuid _id;
QUdpSocket _serverSocket;
udt::Socket _serverSocket;
NetworkPeerHash _activePeers;
HTTPManager _httpManager;
};

View file

@ -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 "Faceshift" "Sixense" "LeapMotion" "RtMidi" "SDL2" "RSSDK" "connexionClient")
set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD")
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
@ -29,18 +29,6 @@ endif()
configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVersion.h")
macro(GroupSources curdir)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
GroupSources(${curdir}/${child})
else()
string(REPLACE "/" "\\" groupname ${curdir})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
endforeach()
endmacro()
# grab the implementation and header files from src dirs
file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h")
GroupSources("src")
@ -115,16 +103,12 @@ else()
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
endif()
# set up the external glm library
add_dependency_external_projects(glm bullet)
# set up the external glm library
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${GLM_INCLUDE_DIRS})
add_dependency_external_projects(LibOVR)
find_package(LibOVR REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
find_package(Bullet REQUIRED)
# perform the system include hack for OS X to ignore warnings
@ -137,9 +121,10 @@ endif()
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater)
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
plugins display-plugins input-plugins)
add_dependency_external_projects(sdl2)

View file

@ -0,0 +1,3 @@
The Mac version does not require any files. The 3D Connexion driver should be installed from http://www.3dconnexion.eu/service/drivers.html
For Windows the provided header file is required: include/I3dMouseParams.h

View file

@ -1,4 +0,0 @@
The mac version does not require any files here. 3D connexion should be installed from
http://www.3dconnexion.eu/service/drivers.html
For windows a header file is required Inc/I3dMouseParams.h

14
interface/external/iViewHMD/readme.txt vendored Normal file
View file

@ -0,0 +1,14 @@
Instructions for adding SMI HMD Eye Tracking to Interface on Windows
David Rowe, 27 Jul 2015.
1. Download and install the SMI HMD Eye Tracking software from http://update.smivision.com/iViewNG-HMD.exe.
2. Copy the SDK folders (3rdParty, include, libs) from the SDK installation folder C:\Program Files (x86)\SMI\iViewNG-HMD\SDK
into the interface/externals/iViewHMD folder. This readme.txt should be there as well.
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different
checkouts and different projects). If so, set the ENV variable "HIFI_LIB_DIR" to a directory containing a subfolder
"iViewHMD" that contains the folders mentioned above.
3. Clear your build directory, run cmake and build, and you should be all set.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,64 @@
name = defaultAvatar_full
type = body+head
scale = 1
filename = defaultAvatar_full/defaultAvatar_full.fbx
texdir = defaultAvatar_full/textures
joint = jointNeck = Head
joint = jointLeftHand = LeftHand
joint = jointRoot = Hips
joint = jointHead = HeadTop_End
joint = jointRightHand = RightHand
joint = jointLean = Spine
freeJoint = LeftArm
freeJoint = LeftForeArm
freeJoint = RightArm
freeJoint = RightForeArm
jointIndex = LeftHand = 35
jointIndex = Reye = 3
jointIndex = Hips = 10
jointIndex = LeftHandIndex1 = 36
jointIndex = LeftHandIndex2 = 37
jointIndex = LeftHandIndex3 = 38
jointIndex = LeftHandIndex4 = 39
jointIndex = LeftShoulder = 32
jointIndex = RightLeg = 12
jointIndex = Grp_blendshapes = 0
jointIndex = Leye = 4
jointIndex = headphone = 8
jointIndex = RightForeArm = 26
jointIndex = Spine = 21
jointIndex = LeftFoot = 18
jointIndex = RightToeBase = 14
jointIndex = face = 1
jointIndex = LeftToe_End = 20
jointIndex = Spine1 = 22
jointIndex = body = 9
jointIndex = Spine2 = 23
jointIndex = RightUpLeg = 11
jointIndex = top1 = 7
jointIndex = Neck = 40
jointIndex = HeadTop_End = 42
jointIndex = RightShoulder = 24
jointIndex = RightArm = 25
jointIndex = Head = 41
jointIndex = LeftLeg = 17
jointIndex = LeftForeArm = 34
jointIndex = hair = 6
jointIndex = RightHand = 27
jointIndex = LeftToeBase = 19
jointIndex = LeftUpLeg = 16
jointIndex = mouth = 2
jointIndex = RightFoot = 13
jointIndex = LeftArm = 33
jointIndex = shield = 5
jointIndex = RightHandIndex1 = 28
jointIndex = RightHandIndex2 = 29
jointIndex = RightToe_End = 15
jointIndex = RightHandIndex3 = 30
jointIndex = RightHandIndex4 = 31
ry = 0
rz = 0
tx = 0
ty = 0
tz = 0
rx = 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -47,6 +47,11 @@ Item {
font.pixelSize: root.fontSize
text: "Framerate: " + root.framerate
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Simrate: " + root.simrate
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,9 @@
#include <StDev.h>
#include <udt/PacketHeaders.h>
#include <ViewFrustum.h>
#include <plugins/PluginContainer.h>
#include <plugins/PluginManager.h>
#include <SimpleMovingAverage.h>
#include "AudioClient.h"
#include "Bookmarks.h"
@ -47,13 +50,12 @@
#include "Stars.h"
#include "avatar/Avatar.h"
#include "avatar/MyAvatar.h"
#include "devices/SixenseManager.h"
#include <input-plugins/KeyboardMouseDevice.h>
#include "scripting/ControllerScriptingInterface.h"
#include "scripting/DialogsManagerScriptingInterface.h"
#include "scripting/WebWindowClass.h"
#include "ui/AudioStatsDialog.h"
#include "ui/BandwidthDialog.h"
#include "ui/HMDToolsDialog.h"
#include "ui/ModelsBrowser.h"
#include "ui/OctreeStatsDialog.h"
#include "ui/SnapshotShareDialog.h"
@ -62,10 +64,9 @@
#include "ui/overlays/Overlays.h"
#include "ui/ApplicationOverlay.h"
#include "ui/ApplicationCompositor.h"
#include "ui/OverlayConductor.h"
#include "ui/RunningScriptsWidget.h"
#include "ui/ToolWindow.h"
#include "ui/UserInputMapper.h"
#include "devices/KeyboardMouseDevice.h"
#include "octree/OctreePacketProcessor.h"
#include "UndoStackScriptingInterface.h"
@ -79,6 +80,7 @@ class QMouseEvent;
class QSystemTrayIcon;
class QTouchEvent;
class QWheelEvent;
class OffscreenGlCanvas;
class GLCanvas;
class FaceTracker;
@ -86,6 +88,12 @@ class MainWindow;
class Node;
class ScriptEngine;
namespace gpu {
class Context;
typedef std::shared_ptr<Context> ContextPointer;
}
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SVO_EXTENSION = ".svo";
static const QString SVO_JSON_EXTENSION = ".svo.json";
@ -124,7 +132,7 @@ class Application;
typedef bool (Application::* AcceptURLMethod)(const QString &);
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface {
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, PluginContainer {
Q_OBJECT
friend class OctreePacketProcessor;
@ -136,7 +144,6 @@ public:
static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); }
static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); }
static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; }
static void initPlugins();
static void shutdownPlugins();
@ -179,6 +186,7 @@ public:
bool eventFilter(QObject* object, QEvent* event);
glm::uvec2 getCanvasSize() const;
glm::uvec2 getUiSize() const;
QSize getDeviceSize() const;
bool hasFocus() const;
PickRay computePickRay() const;
@ -262,14 +270,7 @@ public:
void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false);
virtual const glm::vec3& getShadowDistances() const { return _shadowDistances; }
/// Computes the off-axis frustum parameters for the view frustum, taking mirroring into account.
virtual void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
virtual ViewFrustum* getCurrentViewFrustum() { return getDisplayViewFrustum(); }
virtual bool getShadowsEnabled();
virtual bool getCascadeShadowsEnabled();
virtual QThread* getMainThread() { return thread(); }
virtual float getSizeScale() const;
virtual int getBoundaryLevelAdjust() const;
@ -279,6 +280,25 @@ public:
virtual void endOverrideEnvironmentData() { _environment.endOverride(); }
virtual qreal getDevicePixelRatio();
// Plugin container support
virtual void addMenu(const QString& menuName);
virtual void removeMenu(const QString& menuName);
virtual void addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName);
virtual void removeMenuItem(const QString& menuName, const QString& menuItem);
virtual bool isOptionChecked(const QString& name);
virtual void setIsOptionChecked(const QString& path, bool checked);
virtual void setFullscreen(const QScreen* target) override;
virtual void unsetFullscreen(const QScreen* avoid) override;
virtual void showDisplayPluginsTools() override;
virtual QGLWidget* getPrimarySurface() override;
void setActiveDisplayPlugin(const QString& pluginName);
DisplayPlugin * getActiveDisplayPlugin();
const DisplayPlugin * getActiveDisplayPlugin() const;
public:
FileLogger* getLogger() { return _logger; }
glm::vec2 getViewportDimensions() const;
@ -302,10 +322,9 @@ public:
// rendering of several elements depend on that
// TODO: carry that information on the Camera as a setting
bool isHMDMode() const;
glm::quat getHeadOrientation() const;
glm::vec3 getHeadPosition() const;
glm::mat4 getHeadPose() const;
glm::mat4 getHMDSensorPose() const;
glm::mat4 getEyePose(int eye) const;
glm::mat4 getEyeOffset(int eye) const;
glm::mat4 getEyeProjection(int eye) const;
QRect getDesirableApplicationGeometry();
@ -332,6 +351,8 @@ public:
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
float getAverageSimsPerSecond();
signals:
/// Fired when we're simulating; allows external parties to hook in.
@ -355,6 +376,7 @@ signals:
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void beforeAboutToQuit();
void activeDisplayPluginChanged();
public slots:
void setSessionUUID(const QUuid& sessionUUID);
@ -363,6 +385,8 @@ public slots:
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
void packetSent(quint64 length);
void updateDisplayMode();
void updateInputModes();
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
@ -408,6 +432,11 @@ public slots:
void resetSensors();
void setActiveFaceTracker();
void setActiveEyeTracker();
void calibrateEyeTracker1Point();
void calibrateEyeTracker3Points();
void calibrateEyeTracker5Points();
void aboutApp();
void showEditEntitiesHelp();
@ -433,15 +462,8 @@ private slots:
void connectedToDomain(const QString& hostname);
friend class HMDToolsDialog;
void setFullscreen(bool fullscreen);
void setEnable3DTVMode(bool enable3DTVMode);
void setEnableVRMode(bool enableVRMode);
void rotationModeChanged();
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
void closeMirrorView();
void restoreMirrorView();
void shrinkMirrorView();
@ -469,6 +491,9 @@ private:
void update(float deltaTime);
void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index);
void emulateMouse(Hand* hand, float click, float shift, int index);
// Various helper functions called during update()
void updateLOD();
void updateMouseRay();
@ -482,7 +507,7 @@ private:
void renderLookatIndicator(glm::vec3 pointOfInterest);
void queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions);
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
glm::vec3 getSunDirection();
@ -497,6 +522,11 @@ private:
int sendNackPackets();
bool _dependencyManagerIsSetup;
OffscreenGlCanvas* _offscreenContext;
DisplayPluginPointer _displayPlugin;
InputPluginList _activeInputPlugins;
MainWindow* _window;
ToolWindow* _toolWindow;
@ -534,11 +564,10 @@ private:
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
Camera _myCamera; // My view onto the world
Camera _mirrorCamera; // Cammera for mirror view
KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
Camera _myCamera; // My view onto the world
Camera _mirrorCamera; // Cammera for mirror view
QRect _mirrorViewRect;
Setting::Handle<bool> _firstRun;
@ -625,9 +654,6 @@ private:
void checkSkeleton();
QWidget* _fullscreenMenuWidget = new QWidget();
int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals;
@ -644,9 +670,24 @@ private:
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
ApplicationCompositor _compositor;
OverlayConductor _overlayConductor;
int _oldHandMouseX[2];
int _oldHandMouseY[2];
bool _oldHandLeftClick[2];
bool _oldHandRightClick[2];
int _numFramesSinceLastResize = 0;
bool _overlayEnabled = true;
QRect _savedGeometry;
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
EntityItemID _keyboardFocusedItem;
quint64 _lastAcceptedKeyPress = 0;
SimpleMovingAverage _simsPerSecond{10};
int _simsPerSecondReport = 0;
quint64 _lastSimsPerSecondUpdate = 0;
};
#endif // hifi_Application_h

View file

@ -46,11 +46,7 @@ QString modeToString(CameraMode mode) {
}
Camera::Camera() :
_mode(CAMERA_MODE_THIRD_PERSON),
_position(0.0f, 0.0f, 0.0f),
_projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)),
_isKeepLookingAt(false),
_lookingAt(0.0f, 0.0f, 0.0f)
_projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP))
{
}
@ -61,12 +57,33 @@ void Camera::update(float deltaTime) {
return;
}
void Camera::recompose() {
mat4 orientation = glm::mat4_cast(_rotation);
mat4 translation = glm::translate(mat4(), _position);
_transform = translation * orientation;
}
void Camera::decompose() {
_position = vec3(_transform[3]);
_rotation = glm::quat_cast(_transform);
}
void Camera::setTransform(const glm::mat4& transform) {
_transform = transform;
decompose();
}
void Camera::setPosition(const glm::vec3& position) {
_position = position;
_position = position;
recompose();
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
}
void Camera::setRotation(const glm::quat& rotation) {
_rotation = rotation;
recompose();
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
@ -129,3 +146,21 @@ void Camera::keepLookingAt(const glm::vec3& point) {
_isKeepLookingAt = true;
_lookingAt = point;
}
void Camera::loadViewFrustum(ViewFrustum& frustum) const {
// We will use these below, from either the camera or head vectors calculated above
frustum.setProjection(getProjection());
// Set the viewFrustum up with the correct position and orientation of the camera
frustum.setPosition(getPosition());
frustum.setOrientation(getRotation());
// Ask the ViewFrustum class to calculate our corners
frustum.calculate();
}
ViewFrustum Camera::toViewFrustum() const {
ViewFrustum result;
loadViewFrustum(result);
return result;
}

View file

@ -43,24 +43,31 @@ public:
void update( float deltaTime );
void setRotation(const glm::quat& rotation);
void setProjection(const glm::mat4 & projection);
CameraMode getMode() const { return _mode; }
void setMode(CameraMode m);
glm::quat getRotation() const { return _rotation; }
const glm::mat4& getProjection() const { return _projection; }
CameraMode getMode() const { return _mode; }
void loadViewFrustum(ViewFrustum& frustum) const;
ViewFrustum toViewFrustum() const;
public slots:
QString getModeString() const;
void setModeString(const QString& mode);
glm::quat getRotation() const { return _rotation; }
void setRotation(const glm::quat& rotation);
glm::vec3 getPosition() const { return _position; }
void setPosition(const glm::vec3& position);
glm::quat getOrientation() const { return getRotation(); }
void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
const glm::mat4& getTransform() const { return _transform; }
void setTransform(const glm::mat4& transform);
const glm::mat4& getProjection() const { return _projection; }
void setProjection(const glm::mat4& projection);
PickRay computePickRay(float x, float y);
// These only work on independent cameras
@ -78,11 +85,17 @@ signals:
void modeUpdated(const QString& newMode);
private:
CameraMode _mode;
void recompose();
void decompose();
CameraMode _mode{ CAMERA_MODE_THIRD_PERSON };
glm::mat4 _transform;
glm::mat4 _projection;
// derived
glm::vec3 _position;
glm::quat _rotation;
glm::mat4 _projection;
bool _isKeepLookingAt;
bool _isKeepLookingAt{ false };
glm::vec3 _lookingAt;
};

View file

@ -9,17 +9,33 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "GLCanvas.h"
#include <QMimeData>
#include <QUrl>
#include <QWindow>
#include "Application.h"
#include "GLCanvas.h"
#include "MainWindow.h"
const int MSECS_PER_FRAME_WHEN_THROTTLED = 66;
GLCanvas::GLCanvas() : QGLWidget(QGL::NoDepthBuffer | QGL::NoStencilBuffer),
static QGLFormat& getDesiredGLFormat() {
// Specify an OpenGL 3.3 format using the Core profile.
// That is, no old-school fixed pipeline functionality
static QGLFormat glFormat;
static std::once_flag once;
std::call_once(once, [] {
glFormat.setVersion(4, 1);
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
glFormat.setSampleBuffers(false);
glFormat.setDepth(false);
glFormat.setStencil(false);
});
return glFormat;
}
GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()),
_throttleRendering(false),
_idleRenderInterval(MSECS_PER_FRAME_WHEN_THROTTLED)
{
@ -48,7 +64,6 @@ int GLCanvas::getDeviceHeight() const {
}
void GLCanvas::initializeGL() {
Application::getInstance()->initializeGL();
setAttribute(Qt::WA_AcceptTouchEvents);
setAcceptDrops(true);
connect(Application::getInstance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(activeChanged(Qt::ApplicationState)));

View file

@ -22,7 +22,7 @@ class GLCanvas : public QGLWidget {
public:
GLCanvas();
void stopFrameTimer();
bool isThrottleRendering() const;

View file

@ -97,10 +97,6 @@ void MainWindow::changeEvent(QEvent* event) {
} else {
emit windowShown(true);
}
if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) {
Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen());
}
} else if (event->type() == QEvent::ActivationChange) {
if (isActiveWindow()) {
emit windowShown(true);

View file

@ -28,8 +28,7 @@
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "devices/RealSense.h"
#include "devices/SixenseManager.h"
#include "devices/3Dconnexion.h"
#include "devices/3DConnexionClient.h"
#include "MainWindow.h"
#include "scripting/MenuScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
@ -221,9 +220,20 @@ Menu::Menu() {
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
qApp, SLOT(packageModel()));
MenuWrapper* displayMenu = addMenu("Display");
{
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
displayModeGroup->setExclusive(true);
}
MenuWrapper* avatarMenu = addMenu("Avatar");
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu);
QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu);
inputModeGroup->setExclusive(false);
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize,
@ -242,26 +252,16 @@ Menu::Menu() {
SLOT(resetSize()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl,
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehavior()));
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true,
avatar, SLOT(updateMotionBehavior()));
avatar, SLOT(updateMotionBehaviorFromMenu()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
avatar, SLOT(updateMotionBehavior()));
MenuWrapper* viewMenu = addMenu("View");
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::Fullscreen,
#ifdef Q_OS_MAC
Qt::CTRL | Qt::META | Qt::Key_F,
#else
Qt::CTRL | Qt::Key_F,
#endif
false,
qApp,
SLOT(setFullscreen(bool)));
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
MenuWrapper* cameraModeMenu = viewMenu->addMenu("Camera Mode");
QActionGroup* cameraModeGroup = new QActionGroup(cameraModeMenu);
@ -289,28 +289,11 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
0, false, qApp, SLOT(rotationModeChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
#ifdef Q_OS_MAC
Qt::META | Qt::Key_H,
#else
Qt::CTRL | Qt::Key_H,
#endif
false,
dialogsManager.data(),
SLOT(hmdTools(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0,
false,
qApp,
SLOT(setEnableVRMode(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
false,
qApp,
SLOT(setEnable3DTVMode(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::StandingHMDSensorMode, 0, false,
avatar, SLOT(updateStandingHMDModeFromMenu()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
@ -346,12 +329,6 @@ Menu::Menu() {
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows");
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
{
MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
@ -421,6 +398,8 @@ Menu::Menu() {
faceTrackingMenu->addSeparator();
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
binaryEyelidControl->setVisible(true); // DDE face tracking is on by default
QAction* coupleEyelids = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::CoupleEyelids, 0, true);
coupleEyelids->setVisible(true); // DDE face tracking is on by default
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
useAudioForMouth->setVisible(true); // DDE face tracking is on by default
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
@ -436,6 +415,23 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false);
#endif
#ifdef HAVE_IVIEWHMD
MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking");
addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false,
qApp, SLOT(setActiveEyeTracker()));
{
MenuWrapper* calibrateEyeTrackingMenu = eyeTrackingMenu->addMenu("Calibrate");
addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::OnePointCalibration, 0,
qApp, SLOT(calibrateEyeTracker1Point()));
addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::ThreePointCalibration, 0,
qApp, SLOT(calibrateEyeTracker3Points()));
addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::FivePointCalibration, 0,
qApp, SLOT(calibrateEyeTracker5Points()));
}
addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SimulateEyeTracking, 0, false,
qApp, SLOT(setActiveEyeTracker()));
#endif
auto avatarManager = DependencyManager::get<AvatarManager>();
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
@ -444,8 +440,10 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtTargets, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
MenuOption::Connexion,
0, false,
@ -457,30 +455,11 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandMouseInput, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
qApp, SLOT(setLowVelocityFilter(bool)));
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
#ifdef __APPLE__
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
MenuOption::SixenseEnabled,
0, false,
&SixenseManager::getInstance(),
SLOT(toggleSixense(bool)));
#endif
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
MenuOption::FilterSixense,
0,
true,
&SixenseManager::getInstance(),
SLOT(setFilter(bool)));
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
MenuOption::LowVelocityFilter,
0,
true,
qApp,
SLOT(setLowVelocityFilter(bool)));
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);

View file

@ -80,6 +80,13 @@ public:
const QKeySequence& shortcut = 0,
QAction::MenuRole role = QAction::NoRole,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const bool checked = false,
const QObject* receiver = NULL,
const char* member = NULL,
int menuItemLocation = UNSPECIFIED_POSITION);
void removeAction(MenuWrapper* menu, const QString& actionName);
@ -109,14 +116,6 @@ private:
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const bool checked = false,
const QObject* receiver = NULL,
const char* member = NULL,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
@ -154,7 +153,6 @@ namespace MenuOption {
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";
const QString CascadedShadows = "Cascaded";
const QString CachesSize = "RAM Caches Size";
const QString CalibrateCamera = "Calibrate Camera";
const QString CenterPlayerInView = "Center Player In View";
@ -165,6 +163,7 @@ namespace MenuOption {
const QString ControlWithSpeech = "Control With Speech";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString CoupleEyelids = "Couple Eyelids";
const QString DebugAmbientOcclusion = "Debug Ambient Occlusion";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DeleteBookmark = "Delete Bookmark...";
@ -186,22 +185,23 @@ namespace MenuOption {
const QString EditEntitiesHelp = "Edit Entities Help...";
const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString EnableCharacterController = "Enable avatar collisions";
const QString EnableVRMode = "Enable VR Mode";
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
const QString ExpandPaintGLTiming = "Expand /paintGL";
const QString ExpandUpdateTiming = "Expand /update";
const QString Faceshift = "Faceshift";
const QString FilterSixense = "Smooth Sixense Movement";
const QString FirstPerson = "First Person";
const QString FivePointCalibration = "5 Point Calibration";
const QString FixGaze = "Fix Gaze (no saccade)";
const QString Forward = "Forward";
const QString FrameTimer = "Show Timer";
const QString Fullscreen = "Fullscreen";
const QString FullscreenMirror = "Fullscreen Mirror";
const QString HMDTools = "HMD Tools";
const QString GlowWhenSpeaking = "Glow When Speaking";
const QString HandMouseInput = "Enable Hand Mouse Input";
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IndependentMode = "Independent Mode";
const QString InputMenu = "Avatar>Input Devices";
const QString KeyboardMotorControl = "Enable Keyboard Motor Control";
const QString LeapMotionOnHMD = "Leap Motion on HMD";
const QString LoadScript = "Open and Run Script File...";
@ -219,7 +219,9 @@ namespace MenuOption {
const QString NamesAboveHeads = "Names Above Heads";
const QString NoFaceTracking = "None";
const QString OctreeStats = "Entity Statistics";
const QString OnePointCalibration = "1 Point Calibration";
const QString OnlyDisplayTopTen = "Only Display Top Ten";
const QString OutputMenu = "Display>Mode";
const QString PackageModel = "Package Model...";
const QString Pair = "Pair";
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
@ -232,6 +234,7 @@ namespace MenuOption {
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
const QString RenderLookAtTargets = "Show Look-at Targets";
const QString RenderLookAtVectors = "Show Look-at Vectors";
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
const QString RenderTargetFramerate = "Framerate";
@ -270,15 +273,16 @@ namespace MenuOption {
const QString ShowIKConstraints = "Show IK Constraints";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
const QString ShowWhosLookingAtMe = "Show Who's Looking at Me";
const QString SimpleShadows = "Simple";
const QString SixenseEnabled = "Enable Hydra Support";
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
const QString SimulateEyeTracking = "Simulate";
const QString SMIEyeTracking = "SMI Eye Tracking";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString TestPing = "Test Ping";
const QString ThirdPerson = "Third Person";
const QString ThreePointCalibration = "3 Point Calibration";
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus";
const QString ToolWindow = "Tool Window";
const QString TransmitterDrive = "Transmitter Drive";

View file

@ -12,8 +12,6 @@
#include <QStyle>
#include <QStyleOptionTitleBar>
#include "GLCanvas.h"
#include "UIUtil.h"
int UIUtil::getWindowTitleBarHeight(const QWidget* window) {

View file

@ -137,7 +137,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch._glLineWidth(1.0f); // default
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
geometryCache->renderGrid(batch, x, y, w, h, gridRows, gridCols, gridColor, _audioScopeGrid);
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);

View file

@ -69,6 +69,8 @@ namespace render {
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
avatarPtr->setDisplayingLookatVectors(renderLookAtVectors);
bool renderLookAtTarget = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtTargets);
avatarPtr->setDisplayingLookatTarget(renderLookAtTarget);
if (avatarPtr->isInitialized() && args) {
avatarPtr->render(args, Application::getInstance()->getCamera()->getPosition());
@ -245,7 +247,7 @@ void Avatar::simulate(float deltaTime) {
}
void Avatar::slamPosition(const glm::vec3& newPosition) {
AvatarData::setPosition(newPosition);
setPosition(newPosition);
_positionDeltaAccumulator = glm::vec3(0.0f);
_velocity = glm::vec3(0.0f);
_lastVelocity = glm::vec3(0.0f);
@ -601,7 +603,9 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa
getHand()->render(renderArgs, false);
}
getHead()->render(renderArgs, 1.0f, renderFrustum);
getHead()->renderLookAts(renderArgs);
}
bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
@ -717,6 +721,29 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
// Compute correct scale to apply
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
#ifdef DEBUG
// TODO: Temporary logging to track cause of invalid scale vale; remove once cause has been fixed.
if (scale == 0.0f || glm::isnan(scale) || glm::isinf(scale)) {
if (scale == 0.0f) {
qDebug() << "ASSERT because scale == 0.0f";
}
if (glm::isnan(scale)) {
qDebug() << "ASSERT because isnan(scale)";
}
if (glm::isinf(scale)) {
qDebug() << "ASSERT because isinf(scale)";
}
qDebug() << "windowSizeY =" << windowSizeY;
qDebug() << "p1.y =" << p1.y;
qDebug() << "p1.w =" << p1.w;
qDebug() << "p0.y =" << p0.y;
qDebug() << "p0.w =" << p0.w;
qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio();
qDebug() << "fontSize =" << fontSize;
qDebug() << "pixelHeight =" << pixelHeight;
qDebug() << "devicePixelRatio =" << devicePixelRatio;
}
#endif
// Compute pixel alignment offset
float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates
@ -936,8 +963,14 @@ void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
const QUrl DEFAULT_FULL_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst");
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
if (isMyAvatar()) {
_skeletonModel.setURL(_skeletonModelURL,
getUseFullAvatar() ? DEFAULT_FULL_MODEL_URL : DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
} else {
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
}
}
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {

View file

@ -91,6 +91,7 @@ public:
//setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
void setDisplayingLookatTarget(bool displayingLookatTarget) { getHead()->setRenderLookatTarget(displayingLookatTarget); }
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
bool getIsLookAtTarget() const { return _isLookAtTarget; }
//getters
@ -150,6 +151,7 @@ public:
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
virtual bool getUseFullAvatar() const { return false; }
/// Scales a world space position vector relative to the avatar position and scale
/// \param vector position to be scaled. Will store the result
@ -169,9 +171,6 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
protected:
SkeletonModel _skeletonModel;
glm::vec3 _skeletonOffset;

View file

@ -279,7 +279,7 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
const QString& collisionSoundURL = myAvatar->getCollisionSoundURL();
if (!collisionSoundURL.isEmpty()) {
const float velocityChange = glm::length(collision.velocityChange);
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01;
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01f;
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
if (!isSound) {

View file

@ -56,12 +56,12 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
}
}
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
_owningHead->getTorsoTwist(),
@ -71,15 +71,15 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
* joint.rotation, DEFAULT_PRIORITY);
* state.getDefaultRotation(), DEFAULT_PRIORITY);
}
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) {
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index) {
// likewise with the eye joints
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation()));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
@ -87,22 +87,22 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
_rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
-MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation, DEFAULT_PRIORITY);
state.getDefaultRotation(), DEFAULT_PRIORITY);
}
void FaceModel::maybeUpdateNeckAndEyeRotation(int index) {
const JointState& state = _rig->getJointState(index);
const FBXJoint& joint = state.getFBXJoint();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const int parentIndex = state.getParentIndex();
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) {
const JointState& parentState = _rig->getJointState(joint.parentIndex);
if (parentIndex != -1 && parentIndex >= 0 && parentIndex < _rig->getJointStateCount()) {
const JointState& parentState = _rig->getJointState(parentIndex);
if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, index);
maybeUpdateNeckRotation(parentState, state, index);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(this, parentState, joint, index);
maybeUpdateEyeRotation(this, parentState, state, index);
}
}
}

View file

@ -26,8 +26,8 @@ public:
virtual void simulate(float deltaTime, bool fullUpdate = true);
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index);
void maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index);
void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index);
void maybeUpdateNeckAndEyeRotation(int index);
/// Retrieve the positions of up to two eye meshes.

View file

@ -17,11 +17,13 @@
#include "Application.h"
#include "Avatar.h"
#include "DependencyManager.h"
#include "GeometryUtil.h"
#include "Head.h"
#include "Menu.h"
#include "Util.h"
#include "devices/DdeFaceTracker.h"
#include "devices/EyeTracker.h"
#include "devices/Faceshift.h"
#include "AvatarRig.h"
@ -44,6 +46,7 @@ Head::Head(Avatar* owningAvatar) :
_mouth3(0.0f),
_mouth4(0.0f),
_renderLookatVectors(false),
_renderLookatTarget(false),
_saccade(0.0f, 0.0f, 0.0f),
_saccadeTarget(0.0f, 0.0f, 0.0f),
_leftEyeBlinkVelocity(0.0f),
@ -116,29 +119,40 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
applyEyelidOffset(getFinalOrientationInWorldFrame());
}
}
auto eyeTracker = DependencyManager::get<EyeTracker>();
_isEyeTrackerConnected = eyeTracker->isTracking();
}
if (!myAvatar->getStandingHMDSensorMode()) {
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
// since everyone else will see the full joint rotations for other people.
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
float currentTwist = getTorsoTwist();
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
}
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
// since everyone else will see the full joint rotations for other people.
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
float currentTwist = getTorsoTwist();
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
}
if (!(_isFaceTrackerConnected || billboard)) {
// Update eye saccades
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
const float AVERAGE_SACCADE_INTERVAL = 6.0f;
const float MICROSACCADE_MAGNITUDE = 0.002f;
const float SACCADE_MAGNITUDE = 0.04f;
const float NOMINAL_FRAME_RATE = 60.0f;
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
if (!_isEyeTrackerConnected) {
// Update eye saccades
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
const float AVERAGE_SACCADE_INTERVAL = 6.0f;
const float MICROSACCADE_MAGNITUDE = 0.002f;
const float SACCADE_MAGNITUDE = 0.04f;
const float NOMINAL_FRAME_RATE = 60.0f;
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
}
_saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime);
} else {
_saccade = glm::vec3();
}
_saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime);
// Detect transition from talking to not; force blink after that and a delay
bool forceBlink = false;
@ -215,6 +229,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
} else {
_saccade = glm::vec3();
}
if (Menu::getInstance()->isOptionChecked(MenuOption::FixGaze)) { // if debug menu turns off, use no saccade
_saccade = glm::vec3();
}
if (!isMine) {
_faceModel.setLODDistance(static_cast<Avatar*>(_owningAvatar)->getLODDistance());
@ -260,7 +277,7 @@ void Head::calculateMouthShapes() {
void Head::applyEyelidOffset(glm::quat headOrientation) {
// Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches.
glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getCorrectedLookAtPosition() - _eyePosition);
glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition);
eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head
float eyePitch = safeEulerAngles(eyeRotation).x;
@ -297,8 +314,18 @@ void Head::relaxLean(float deltaTime) {
}
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) {
}
void Head::renderLookAts(RenderArgs* renderArgs) {
renderLookAts(renderArgs, _leftEyePosition, _rightEyePosition);
}
void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition) {
if (_renderLookatVectors) {
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
renderLookatVectors(renderArgs, leftEyePosition, rightEyePosition, getCorrectedLookAtPosition());
}
if (_renderLookatTarget) {
renderLookatTarget(renderArgs, getCorrectedLookAtPosition());
}
}
@ -317,6 +344,20 @@ glm::quat Head::getFinalOrientationInLocalFrame() const {
return glm::quat(glm::radians(glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() )));
}
// Everyone else's head keeps track of a lookAtPosition that everybody sees the same, and refers to where that head
// is looking in model space -- e.g., at someone's eyeball, or between their eyes, or mouth, etc. Everyon's Interface
// will have the same value for the lookAtPosition of any given head.
//
// Everyone else's head also keeps track of a correctedLookAtPosition that may be different for the same head within
// different Interfaces. If that head is not looking at me, the correctedLookAtPosition is the same as the lookAtPosition.
// However, if that head is looking at me, then I will attempt to adjust the lookAtPosition by the difference between
// my (singular) eye position and my actual camera position. This adjustment is used on their eyeballs during rendering
// (and also on any lookAt vector display for that head, during rendering). Note that:
// 1. this adjustment can be made directly to the other head's eyeball joints, because we won't be send their joint information to others.
// 2. the corrected position is a separate ivar, so the common/uncorrected value is still available
//
// There is a pun here: The two lookAtPositions will always be the same for my own avatar in my own Interface, because I
// will not be looking at myself. (Even in a mirror, I will be looking at the camera.)
glm::vec3 Head::getCorrectedLookAtPosition() {
if (isLookingAtMe()) {
return _correctedLookAtPosition;
@ -337,21 +378,27 @@ void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
bool Head::isLookingAtMe() {
// Allow for outages such as may be encountered during avatar movement
quint64 now = usecTimestampNow();
const quint64 LOOKING_AT_ME_GAP_ALLOWED = 1000000; // microseconds
const quint64 LOOKING_AT_ME_GAP_ALLOWED = (5 * 1000 * 1000) / 60; // n frames, in microseconds
return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED;
}
glm::quat Head::getCameraOrientation() const {
// NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so
// you may wonder why this code is here. This method will be called while in Oculus mode to determine how
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
// always the same.
if (qApp->isHMDMode()) {
return getOrientation();
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
if (myAvatar && myAvatar->getStandingHMDSensorMode()) {
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
} else {
return getOrientation();
}
} else {
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
}
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
}
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
@ -397,7 +444,7 @@ void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition
auto& batch = *renderArgs->_batch;
auto transform = Transform{};
batch.setModelTransform(transform);
batch._glLineWidth(2.0f);
// FIXME: THe line width of 2.0f is not supported anymore, we ll need a workaround
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
deferredLighting->bindSimpleProgram(batch);
@ -409,4 +456,17 @@ void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition
geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
}
void Head::renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition) {
auto& batch = *renderArgs->_batch;
auto transform = Transform{};
transform.setTranslation(lookatPosition);
batch.setModelTransform(transform);
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
deferredLighting->bindSimpleProgram(batch);
auto geometryCache = DependencyManager::get<GeometryCache>();
const float LOOK_AT_TARGET_RADIUS = 0.075f;
const glm::vec4 LOOK_AT_TARGET_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
geometryCache->renderSphere(batch, LOOK_AT_TARGET_RADIUS, 15, 15, LOOK_AT_TARGET_COLOR, true);
}

View file

@ -39,6 +39,9 @@ public:
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; }
void renderLookAts(RenderArgs* renderArgs);
void renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition);
/// \return orientationBase+Delta
glm::quat getFinalOrientationInLocalFrame() const;
@ -123,6 +126,7 @@ private:
float _mouth3;
float _mouth4;
bool _renderLookatVectors;
bool _renderLookatTarget;
glm::vec3 _saccade;
glm::vec3 _saccadeTarget;
float _leftEyeBlinkVelocity;
@ -151,6 +155,7 @@ private:
// private methods
void renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition);
void calculateMouthShapes();
void applyEyelidOffset(glm::quat headOrientation);

View file

@ -107,7 +107,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
EntityItemPointer item = _tree->findEntityByID(_entityID);
const Model* model = getModel(item);
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
if (isValid() && model != NULL && _jointIndex < (uint32_t)(model->getJointStateCount())) {
_lastRefDimension = item->getDimensions();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);

Some files were not shown because too many files have changed in this diff Show more