mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 10:43:56 +02:00
resolve conflicts on merge with upstream/master
This commit is contained in:
commit
abea65fa84
258 changed files with 11151 additions and 3419 deletions
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 2.8.12.2)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
if (USE_ANDROID_TOOLCHAIN)
|
||||
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake")
|
||||
|
@ -37,11 +37,11 @@ if (WIN32)
|
|||
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
elseif (MSVC12)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(WINDOW_SDK_FOLDER "x64")
|
||||
set(WINDOW_SDK_FOLDER "x64")
|
||||
else()
|
||||
set(WINDOW_SDK_FOLDER "x86")
|
||||
set(WINDOW_SDK_FOLDER "x86")
|
||||
endif()
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}")
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}")
|
||||
endif ()
|
||||
message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
|
@ -67,9 +67,9 @@ if ((NOT MSVC12) AND (NOT MSVC14))
|
|||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
else()
|
||||
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
|
@ -197,6 +197,10 @@ if (WIN32)
|
|||
add_paths_to_fixup_libs("${QT_DIR}/bin")
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
set(SERVER_ONLY 0)
|
||||
endif()
|
||||
|
||||
# add subdirectories for all targets
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(assignment-client)
|
||||
|
@ -205,13 +209,15 @@ if (NOT ANDROID)
|
|||
set_target_properties(domain-server PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(ice-server)
|
||||
set_target_properties(ice-server PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(interface)
|
||||
set_target_properties(interface PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(console)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(plugins)
|
||||
if (NOT SERVER_ONLY)
|
||||
add_subdirectory(interface)
|
||||
set_target_properties(interface PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(plugins)
|
||||
endif()
|
||||
add_subdirectory(tools)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (ANDROID OR DESKTOP_GVR)
|
||||
add_subdirectory(gvr-interface)
|
||||
|
@ -227,34 +233,4 @@ if (HIFI_MEMORY_DEBUGGING)
|
|||
endif ()
|
||||
|
||||
include_application_version()
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
message(STATUS "+++++ Package for deployment will be generated on this build +++++")
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment)
|
||||
|
||||
set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico")
|
||||
set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico")
|
||||
string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE})
|
||||
string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER})
|
||||
|
||||
set(CPACK_PACKAGE_NAME "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack")
|
||||
set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}")
|
||||
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||
set(CPACK_PACKAGE_EXECUTABLES
|
||||
stack-manager "Stack Manager"
|
||||
interface "Interface"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./")
|
||||
endif (WIN32)
|
||||
|
||||
include(CPack)
|
||||
endif ()
|
||||
generate_installers()
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "AssignmentActionFactory.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
#include "AssignmentClientLogging.h"
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
|
||||
|
@ -84,7 +85,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
// check for a wallet UUID on the command line or in the config
|
||||
// this would represent where the user running AC wants funds sent to
|
||||
if (!walletUUID.isNull()) {
|
||||
qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID);
|
||||
qCDebug(assigmnentclient) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID);
|
||||
_requestAssignment.setWalletUUID(walletUUID);
|
||||
}
|
||||
|
||||
|
@ -98,13 +99,13 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
_assignmentServerSocket.setObjectName("AssigmentServer");
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
|
||||
qDebug() << "Assignment server socket is" << _assignmentServerSocket;
|
||||
qCDebug(assigmnentclient) << "Assignment server socket is" << _assignmentServerSocket;
|
||||
|
||||
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
|
||||
qDebug() << "Waiting for assignment -" << _requestAssignment;
|
||||
qCDebug(assigmnentclient) << "Waiting for assignment -" << _requestAssignment;
|
||||
|
||||
if (_assignmentServerHostname != "localhost") {
|
||||
qDebug () << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort();
|
||||
qCDebug(assigmnentclient) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort();
|
||||
}
|
||||
|
||||
connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
|
||||
|
@ -122,7 +123,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
|
||||
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
|
||||
|
||||
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
|
||||
qCDebug(assigmnentclient) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
|
||||
|
||||
// Hook up a timer to send this child's status to the Monitor once per second
|
||||
setUpStatusToMonitor();
|
||||
|
@ -133,7 +134,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
}
|
||||
|
||||
void AssignmentClient::stopAssignmentClient() {
|
||||
qDebug() << "Forced stop of assignment-client.";
|
||||
qCDebug(assigmnentclient) << "Forced stop of assignment-client.";
|
||||
|
||||
_requestTimer.stop();
|
||||
_statsTimerACM.stop();
|
||||
|
@ -209,14 +210,14 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
quint16 localAssignmentServerPort;
|
||||
if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) {
|
||||
if (localAssignmentServerPort != _assignmentServerSocket.getPort()) {
|
||||
qDebug() << "Port for local assignment server read from shared memory is"
|
||||
qCDebug(assigmnentclient) << "Port for local assignment server read from shared memory is"
|
||||
<< localAssignmentServerPort;
|
||||
|
||||
_assignmentServerSocket.setPort(localAssignmentServerPort);
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Failed to read local assignment server port from shared memory"
|
||||
qCWarning(assigmnentclient) << "Failed to read local assignment server port from shared memory"
|
||||
<< "- will send assignment request to previous assignment server socket.";
|
||||
}
|
||||
}
|
||||
|
@ -226,13 +227,13 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
}
|
||||
|
||||
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
qDebug() << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
||||
qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
||||
|
||||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
||||
|
||||
if (_currentAssignment && !_isAssigned) {
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||
qDebug(assigmnentclient) << "Received an assignment -" << *_currentAssignment;
|
||||
_isAssigned = true;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -242,7 +243,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
nodeList->getDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname);
|
||||
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
qCDebug(assigmnentclient) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
QThread* workerThread = new QThread;
|
||||
|
@ -270,7 +271,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
// Starts an event loop, and emits workerThread->started()
|
||||
workerThread->start();
|
||||
} else {
|
||||
qDebug() << "Received an assignment that could not be unpacked. Re-requesting.";
|
||||
qCWarning(assigmnentclient) << "Received an assignment that could not be unpacked. Re-requesting.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,12 +279,12 @@ void AssignmentClient::handleStopNodePacket(QSharedPointer<ReceivedMessage> mess
|
|||
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
|
||||
|
||||
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
|
||||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
|
||||
qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode.";
|
||||
|
||||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
|
||||
|
||||
qCDebug(assigmnentclient) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode.";
|
||||
QCoreApplication::quit();
|
||||
} else {
|
||||
qDebug() << "Got a stop packet from other than localhost.";
|
||||
qCWarning(assigmnentclient) << "Got a stop packet from other than localhost.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,7 +304,7 @@ void AssignmentClient::handleAuthenticationRequest() {
|
|||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString())
|
||||
qCWarning(assigmnentclient) << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
|
||||
|
||||
|
@ -321,7 +322,7 @@ void AssignmentClient::assignmentCompleted() {
|
|||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
qDebug() << "Assignment finished or never started - waiting for new assignment.";
|
||||
qCDebug(assigmnentclient) << "Assignment finished or never started - waiting for new assignment.";
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
|
14
assignment-client/src/AssignmentClientLogging.cpp
Normal file
14
assignment-client/src/AssignmentClientLogging.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// AssignmentClientLogging.cpp
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Clement on 12/14/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
|
||||
//
|
||||
|
||||
#include "AssignmentClientLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(assigmnentclient, "hifi.assignment-client")
|
19
assignment-client/src/AssignmentClientLogging.h
Normal file
19
assignment-client/src/AssignmentClientLogging.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// AssignmentClientLogging.h
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Clement on 12/14/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
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentClientLogging_h
|
||||
#define hifi_AssignmentClientLogging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(assigmnentclient)
|
||||
|
||||
#endif // hifi_AssignmentClientLogging_h
|
|
@ -20,80 +20,8 @@
|
|||
|
||||
#include "OctreeSendThread.h"
|
||||
|
||||
OctreeQueryNode::OctreeQueryNode() :
|
||||
_viewSent(false),
|
||||
_octreePacket(),
|
||||
_octreePacketWaiting(false),
|
||||
_lastOctreePayload(new char[udt::MAX_PACKET_SIZE]),
|
||||
_lastOctreePacketLength(0),
|
||||
_duplicatePacketCount(0),
|
||||
_firstSuppressedPacket(usecTimestampNow()),
|
||||
_maxSearchLevel(1),
|
||||
_maxLevelReachedInLastSearch(1),
|
||||
_lastTimeBagEmpty(0),
|
||||
_viewFrustumChanging(false),
|
||||
_viewFrustumJustStoppedChanging(true),
|
||||
_octreeSendThread(NULL),
|
||||
_lastClientBoundaryLevelAdjust(0),
|
||||
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_lodChanged(false),
|
||||
_lodInitialized(false),
|
||||
_sequenceNumber(0),
|
||||
_lastRootTimestamp(0),
|
||||
_myPacketType(PacketType::Unknown),
|
||||
_isShuttingDown(false),
|
||||
_sentPacketHistory()
|
||||
{
|
||||
}
|
||||
|
||||
OctreeQueryNode::~OctreeQueryNode() {
|
||||
_isShuttingDown = true;
|
||||
if (_octreeSendThread) {
|
||||
forceNodeShutdown();
|
||||
}
|
||||
|
||||
delete[] _lastOctreePayload;
|
||||
}
|
||||
|
||||
void OctreeQueryNode::nodeKilled() {
|
||||
_isShuttingDown = true;
|
||||
if (_octreeSendThread) {
|
||||
// just tell our thread we want to shutdown, this is asynchronous, and fast, we don't need or want it to block
|
||||
// while the thread actually shuts down
|
||||
_octreeSendThread->setIsShuttingDown();
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::forceNodeShutdown() {
|
||||
_isShuttingDown = true;
|
||||
if (_octreeSendThread) {
|
||||
// we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually
|
||||
// shuts down because we really need it to shutdown, and it's ok if we wait for it to complete
|
||||
OctreeSendThread* sendThread = _octreeSendThread;
|
||||
_octreeSendThread = NULL;
|
||||
sendThread->setIsShuttingDown();
|
||||
sendThread->terminate();
|
||||
delete sendThread;
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::sendThreadFinished() {
|
||||
// We've been notified by our thread that it is shutting down. So we can clean up our reference to it, and
|
||||
// delete the actual thread object. Cleaning up our thread will correctly unroll all refereces to shared
|
||||
// pointers to our node as well as the octree server assignment
|
||||
if (_octreeSendThread) {
|
||||
OctreeSendThread* sendThread = _octreeSendThread;
|
||||
_octreeSendThread = NULL;
|
||||
delete sendThread;
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) {
|
||||
_octreeSendThread = new OctreeSendThread(myServer, node);
|
||||
|
||||
// we want to be notified when the thread finishes
|
||||
connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished);
|
||||
_octreeSendThread->initialize(true);
|
||||
}
|
||||
|
||||
bool OctreeQueryNode::packetIsDuplicate() const {
|
||||
|
@ -105,7 +33,7 @@ bool OctreeQueryNode::packetIsDuplicate() const {
|
|||
// of the entire packet, we need to compare only the packet content...
|
||||
|
||||
if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) {
|
||||
if (memcmp(_lastOctreePayload + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
||||
if (memcmp(_lastOctreePayload.data() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
||||
_octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
||||
_octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) {
|
||||
return true;
|
||||
|
@ -173,7 +101,7 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
|
||||
// packet send rate.
|
||||
_lastOctreePacketLength = _octreePacket->getPayloadSize();
|
||||
memcpy(_lastOctreePayload, _octreePacket->getPayload(), _lastOctreePacketLength);
|
||||
memcpy(_lastOctreePayload.data(), _octreePacket->getPayload(), _lastOctreePacketLength);
|
||||
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
|
|
|
@ -29,8 +29,8 @@ class OctreeServer;
|
|||
class OctreeQueryNode : public OctreeQuery {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeQueryNode();
|
||||
virtual ~OctreeQueryNode();
|
||||
OctreeQueryNode() = default;
|
||||
virtual ~OctreeQueryNode() = default;
|
||||
|
||||
void init(); // called after creation to set up some virtual items
|
||||
virtual PacketType getMyPacketType() const = 0;
|
||||
|
@ -79,9 +79,6 @@ public:
|
|||
|
||||
OctreeSceneStats stats;
|
||||
|
||||
void initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
|
||||
|
||||
void dumpOutOfView();
|
||||
|
||||
quint64 getLastRootTimestamp() const { return _lastRootTimestamp; }
|
||||
|
@ -92,7 +89,6 @@ public:
|
|||
void sceneStart(quint64 sceneSendStartTime) { _sceneSendStartTime = sceneSendStartTime; }
|
||||
|
||||
void nodeKilled();
|
||||
void forceNodeShutdown();
|
||||
bool isShuttingDown() const { return _isShuttingDown; }
|
||||
|
||||
void octreePacketSent() { packetSent(*_octreePacket); }
|
||||
|
@ -104,49 +100,47 @@ public:
|
|||
bool hasNextNackedPacket() const;
|
||||
const NLPacket* getNextNackedPacket();
|
||||
|
||||
private slots:
|
||||
void sendThreadFinished();
|
||||
|
||||
private:
|
||||
OctreeQueryNode(const OctreeQueryNode &);
|
||||
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
||||
|
||||
bool _viewSent;
|
||||
bool _viewSent { false };
|
||||
std::unique_ptr<NLPacket> _octreePacket;
|
||||
bool _octreePacketWaiting;
|
||||
|
||||
char* _lastOctreePayload = nullptr;
|
||||
unsigned int _lastOctreePacketLength;
|
||||
int _duplicatePacketCount;
|
||||
quint64 _firstSuppressedPacket;
|
||||
unsigned int _lastOctreePacketLength { 0 };
|
||||
int _duplicatePacketCount { 0 };
|
||||
quint64 _firstSuppressedPacket { usecTimestampNow() };
|
||||
|
||||
int _maxSearchLevel;
|
||||
int _maxLevelReachedInLastSearch;
|
||||
int _maxSearchLevel { 1 };
|
||||
int _maxLevelReachedInLastSearch { 1 };
|
||||
ViewFrustum _currentViewFrustum;
|
||||
ViewFrustum _lastKnownViewFrustum;
|
||||
quint64 _lastTimeBagEmpty;
|
||||
bool _viewFrustumChanging;
|
||||
bool _viewFrustumJustStoppedChanging;
|
||||
quint64 _lastTimeBagEmpty { 0 };
|
||||
bool _viewFrustumChanging { false };
|
||||
bool _viewFrustumJustStoppedChanging { true };
|
||||
|
||||
OctreeSendThread* _octreeSendThread;
|
||||
OctreeSendThread* _octreeSendThread { nullptr };
|
||||
|
||||
// watch for LOD changes
|
||||
int _lastClientBoundaryLevelAdjust;
|
||||
float _lastClientOctreeSizeScale;
|
||||
bool _lodChanged;
|
||||
bool _lodInitialized;
|
||||
int _lastClientBoundaryLevelAdjust { 0 };
|
||||
float _lastClientOctreeSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
|
||||
bool _lodChanged { false };
|
||||
bool _lodInitialized { false };
|
||||
|
||||
OCTREE_PACKET_SEQUENCE _sequenceNumber;
|
||||
OCTREE_PACKET_SEQUENCE _sequenceNumber { 0 };
|
||||
|
||||
quint64 _lastRootTimestamp;
|
||||
quint64 _lastRootTimestamp { 0 };
|
||||
|
||||
PacketType _myPacketType;
|
||||
bool _isShuttingDown;
|
||||
PacketType _myPacketType { PacketType::Unknown };
|
||||
bool _isShuttingDown { false };
|
||||
|
||||
SentPacketHistory _sentPacketHistory;
|
||||
QQueue<OCTREE_PACKET_SEQUENCE> _nackedSequenceNumbers;
|
||||
|
||||
quint64 _sceneSendStartTime = 0;
|
||||
|
||||
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeQueryNode_h
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <udt/PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeSendThread.h"
|
||||
#include "OctreeServer.h"
|
||||
#include "OctreeServerConsts.h"
|
||||
|
@ -25,15 +26,12 @@ quint64 endSceneSleepTime = 0;
|
|||
OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
|
||||
_myServer(myServer),
|
||||
_node(node),
|
||||
_nodeUUID(node->getUUID()),
|
||||
_packetData(),
|
||||
_nodeMissingCount(0),
|
||||
_isShuttingDown(false)
|
||||
_nodeUuid(node->getUUID())
|
||||
{
|
||||
QString safeServerName("Octree");
|
||||
|
||||
// set our QThread object name so we can identify this thread while debugging
|
||||
setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(node->getUUID())));
|
||||
setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(_nodeUuid)));
|
||||
|
||||
if (_myServer) {
|
||||
safeServerName = _myServer->getMyServerName();
|
||||
|
@ -46,6 +44,8 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePoint
|
|||
}
|
||||
|
||||
OctreeSendThread::~OctreeSendThread() {
|
||||
setIsShuttingDown();
|
||||
|
||||
QString safeServerName("Octree");
|
||||
if (_myServer) {
|
||||
safeServerName = _myServer->getMyServerName();
|
||||
|
@ -56,8 +56,6 @@ OctreeSendThread::~OctreeSendThread() {
|
|||
|
||||
OctreeServer::clientDisconnected();
|
||||
OctreeServer::stopTrackingThread(this);
|
||||
|
||||
_node.clear();
|
||||
}
|
||||
|
||||
void OctreeSendThread::setIsShuttingDown() {
|
||||
|
@ -79,15 +77,17 @@ bool OctreeSendThread::process() {
|
|||
|
||||
// don't do any send processing until the initial load of the octree is complete...
|
||||
if (_myServer->isInitialLoadComplete()) {
|
||||
if (_node) {
|
||||
if (auto node = _node.lock()) {
|
||||
_nodeMissingCount = 0;
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(_node->getLinkedData());
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData && !nodeData->isShuttingDown()) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
packetDistributor(nodeData, viewFrustumChanged);
|
||||
packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
} else {
|
||||
return false; // exit early if we're shutting down
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,8 @@ AtomicUIntStat OctreeSendThread::_totalSpecialBytes { 0 };
|
|||
AtomicUIntStat OctreeSendThread::_totalSpecialPackets { 0 };
|
||||
|
||||
|
||||
int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent,
|
||||
int& truePacketsSent) {
|
||||
OctreeServer::didHandlePacketSend(this);
|
||||
|
||||
// if we're shutting down, then exit early
|
||||
|
@ -183,12 +184,12 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
|
||||
// actually send it
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *node);
|
||||
packetSent = true;
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *node);
|
||||
|
||||
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
||||
// there was nothing else to send.
|
||||
|
@ -219,7 +220,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
packetsSent++;
|
||||
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
packetSent = true;
|
||||
|
||||
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
|
||||
|
@ -251,7 +252,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||
// just send the octree packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
packetSent = true;
|
||||
|
||||
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
|
||||
|
@ -293,7 +294,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
}
|
||||
|
||||
/// Version of octree element distributor that sends the deepest LOD level at once
|
||||
int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
||||
int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
||||
|
||||
OctreeServer::didPacketDistributor(this);
|
||||
|
||||
|
@ -322,7 +323,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
} else {
|
||||
nodeData->resetOctreePacket();
|
||||
}
|
||||
|
@ -355,7 +356,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
//unsigned long encodeTime = nodeData->stats.getTotalEncodeTime();
|
||||
//unsigned long elapsedTime = nodeData->stats.getElapsedTime();
|
||||
|
||||
int packetsJustSent = handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval += packetsJustSent;
|
||||
|
||||
// If we're starting a full scene, then definitely we want to empty the elementBag
|
||||
|
@ -431,8 +432,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back
|
||||
// during the encodeTreeBitstream() as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUUID);
|
||||
params.trackSend = [this, node](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, node->getUUID());
|
||||
};
|
||||
|
||||
// TODO: should this include the lock time or not? This stat is sent down to the client,
|
||||
|
@ -481,7 +482,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
unsigned int writtenSize = _packetData.getFinalizedSize() + sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
|
||||
if (writtenSize > nodeData->getAvailable()) {
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
}
|
||||
|
||||
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
|
||||
|
@ -501,7 +502,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
|
||||
if (sendNow) {
|
||||
quint64 packetSendingStart = usecTimestampNow();
|
||||
packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
quint64 packetSendingEnd = usecTimestampNow();
|
||||
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
|
||||
|
||||
|
@ -538,9 +539,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) {
|
||||
if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) {
|
||||
int specialPacketsSent = 0;
|
||||
trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent);
|
||||
trueBytesSent += _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent);
|
||||
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
||||
truePacketsSent += specialPacketsSent;
|
||||
packetsSentThisInterval += specialPacketsSent;
|
||||
|
@ -556,7 +557,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) {
|
||||
const NLPacket* packet = nodeData->getNextNackedPacket();
|
||||
if (packet) {
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
#include <GenericThread.h>
|
||||
|
||||
#include "OctreeQueryNode.h"
|
||||
|
||||
class OctreeQueryNode;
|
||||
class OctreeServer;
|
||||
|
||||
using AtomicUIntStat = std::atomic<uintmax_t>;
|
||||
|
@ -32,6 +31,9 @@ public:
|
|||
virtual ~OctreeSendThread();
|
||||
|
||||
void setIsShuttingDown();
|
||||
bool isShuttingDown() { return _isShuttingDown; }
|
||||
|
||||
QUuid getNodeUuid() const { return _nodeUuid; }
|
||||
|
||||
static AtomicUIntStat _totalBytes;
|
||||
static AtomicUIntStat _totalWastedBytes;
|
||||
|
@ -48,17 +50,18 @@ protected:
|
|||
virtual bool process();
|
||||
|
||||
private:
|
||||
OctreeServer* _myServer;
|
||||
SharedNodePointer _node;
|
||||
QUuid _nodeUUID;
|
||||
|
||||
int handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
|
||||
int packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged);
|
||||
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
|
||||
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
|
||||
|
||||
|
||||
OctreeServer* _myServer { nullptr };
|
||||
QWeakPointer<Node> _node;
|
||||
QUuid _nodeUuid;
|
||||
|
||||
OctreePacketData _packetData;
|
||||
|
||||
int _nodeMissingCount;
|
||||
bool _isShuttingDown;
|
||||
int _nodeMissingCount { 0 };
|
||||
bool _isShuttingDown { false };
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeSendThread_h
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
|
||||
#include "../AssignmentClient.h"
|
||||
|
||||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeServerConsts.h"
|
||||
|
||||
OctreeServer* OctreeServer::_instance = NULL;
|
||||
int OctreeServer::_clientCount = 0;
|
||||
const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000000;
|
||||
|
||||
|
@ -231,13 +231,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
|
|||
_started(time(0)),
|
||||
_startedUSecs(usecTimestampNow())
|
||||
{
|
||||
if (_instance) {
|
||||
qDebug() << "Octree Server starting... while old instance still running _instance=["<<_instance<<"] this=[" << this << "]";
|
||||
}
|
||||
|
||||
qDebug() << "Octree Server starting... setting _instance to=[" << this << "]";
|
||||
_instance = this;
|
||||
|
||||
_averageLoopTime.updateAverage(0);
|
||||
qDebug() << "Octree server starting... [" << this << "]";
|
||||
|
||||
|
@ -281,9 +274,6 @@ OctreeServer::~OctreeServer() {
|
|||
_tree.reset();
|
||||
qDebug() << qPrintable(_safeServerName) << "server DONE cleaning up octree... [" << this << "]";
|
||||
|
||||
if (_instance == this) {
|
||||
_instance = NULL; // we are gone
|
||||
}
|
||||
qDebug() << qPrintable(_safeServerName) << "server DONE shutting down... [" << this << "]";
|
||||
}
|
||||
|
||||
|
@ -878,16 +868,38 @@ void OctreeServer::parsePayload() {
|
|||
}
|
||||
}
|
||||
|
||||
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
|
||||
auto sendThread = std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
|
||||
|
||||
// we want to be notified when the thread finishes
|
||||
connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread);
|
||||
sendThread->initialize(true);
|
||||
|
||||
return sendThread;
|
||||
}
|
||||
|
||||
void OctreeServer::removeSendThread() {
|
||||
// If the object has been deleted since the event was queued, sender() will return nullptr
|
||||
if (auto sendThread = qobject_cast<OctreeSendThread*>(sender())) {
|
||||
// This deletes the unique_ptr, so sendThread is destructed after that line
|
||||
_sendThreads.erase(sendThread->getNodeUuid());
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
if (!_isFinished) {
|
||||
if (!_isFinished && !_isShuttingDown) {
|
||||
// If we got a query packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->updateNodeWithDataFromPacket(message, senderNode);
|
||||
|
||||
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
nodeData->initializeOctreeSendThread(this, senderNode);
|
||||
auto it = _sendThreads.find(senderNode->getUUID());
|
||||
if (it == _sendThreads.end()) {
|
||||
_sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode));
|
||||
} else if (it->second->isShuttingDown()) {
|
||||
_sendThreads.erase(it); // Remove right away and wait on thread to be
|
||||
|
||||
_sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1117,8 +1129,8 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
nodeList->linkedDataCreateCallback = [] (Node* node) {
|
||||
auto queryNodeData = _instance->createOctreeQueryNode();
|
||||
nodeList->linkedDataCreateCallback = [this](Node* node) {
|
||||
auto queryNodeData = createOctreeQueryNode();
|
||||
queryNodeData->init();
|
||||
node->setLinkedData(std::move(queryNodeData));
|
||||
};
|
||||
|
@ -1167,6 +1179,13 @@ void OctreeServer::nodeAdded(SharedNodePointer node) {
|
|||
|
||||
void OctreeServer::nodeKilled(SharedNodePointer node) {
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
// Shutdown send thread
|
||||
auto it = _sendThreads.find(node->getUUID());
|
||||
if (it != _sendThreads.end()) {
|
||||
auto& sendThread = *it->second;
|
||||
sendThread.setIsShuttingDown();
|
||||
}
|
||||
|
||||
// calling this here since nodeKilled slot in ReceivedPacketProcessor can't be triggered by signals yet!!
|
||||
_octreeInboundPacketProcessor->nodeKilled(node);
|
||||
|
@ -1188,24 +1207,6 @@ void OctreeServer::nodeKilled(SharedNodePointer node) {
|
|||
trackViewerGone(node->getUUID());
|
||||
}
|
||||
|
||||
void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
|
||||
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down
|
||||
} else {
|
||||
qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node;
|
||||
}
|
||||
|
||||
quint64 end = usecTimestampNow();
|
||||
quint64 usecsElapsed = (end - start);
|
||||
qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: "
|
||||
<< usecsElapsed << " usecs for node:" << *node;
|
||||
}
|
||||
|
||||
|
||||
void OctreeServer::aboutToFinish() {
|
||||
qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish...";
|
||||
|
||||
|
@ -1214,9 +1215,8 @@ void OctreeServer::aboutToFinish() {
|
|||
qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down...";
|
||||
|
||||
// we're going down - set the NodeList linkedDataCallback to NULL so we do not create any more OctreeQueryNode objects.
|
||||
// This ensures that when we forceNodeShutdown below for each node we don't get any more newly connecting nodes
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->linkedDataCreateCallback = NULL;
|
||||
// This ensures that we don't get any more newly connecting nodes
|
||||
DependencyManager::get<NodeList>()->linkedDataCreateCallback = nullptr;
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
|
@ -1226,21 +1226,15 @@ void OctreeServer::aboutToFinish() {
|
|||
_jurisdictionSender->terminating();
|
||||
}
|
||||
|
||||
QSet<SharedNodePointer> nodesToShutdown;
|
||||
|
||||
// Force a shutdown of all of our OctreeSendThreads.
|
||||
// At this point it has to be impossible for a linkedDataCreateCallback to be called for a new node
|
||||
nodeList->eachNode([&nodesToShutdown](const SharedNodePointer& node) {
|
||||
nodesToShutdown << node;
|
||||
});
|
||||
|
||||
// What follows is a hack to force OctreeSendThreads to cleanup before the OctreeServer is gone.
|
||||
// I would prefer to allow the SharedNodePointer ref count drop to zero to do this automatically
|
||||
// but that isn't possible as long as the OctreeSendThread has an OctreeServer* that it uses.
|
||||
for (auto& node : nodesToShutdown) {
|
||||
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
|
||||
forceNodeShutdown(node);
|
||||
// Shut down all the send threads
|
||||
for (auto& it : _sendThreads) {
|
||||
auto& sendThread = *it.second;
|
||||
sendThread.setIsShuttingDown();
|
||||
}
|
||||
|
||||
// Clear will destruct all the unique_ptr to OctreeSendThreads which will call the GenericThread's dtor
|
||||
// which waits on the thread to be done before returning
|
||||
_sendThreads.clear(); // Cleans up all the send threads.
|
||||
|
||||
if (_persistThread) {
|
||||
_persistThread->aboutToFinish();
|
||||
|
@ -1459,15 +1453,22 @@ void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) {
|
|||
|
||||
|
||||
void OctreeServer::stopTrackingThread(OctreeSendThread* thread) {
|
||||
QMutexLocker lockerA(&_threadsDidProcessMutex);
|
||||
QMutexLocker lockerB(&_threadsDidPacketDistributorMutex);
|
||||
QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex);
|
||||
QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex);
|
||||
|
||||
_threadsDidProcess.remove(thread);
|
||||
_threadsDidPacketDistributor.remove(thread);
|
||||
_threadsDidHandlePacketSend.remove(thread);
|
||||
_threadsDidCallWriteDatagram.remove(thread);
|
||||
{
|
||||
QMutexLocker locker(&_threadsDidProcessMutex);
|
||||
_threadsDidProcess.remove(thread);
|
||||
}
|
||||
{
|
||||
QMutexLocker locker(&_threadsDidPacketDistributorMutex);
|
||||
_threadsDidPacketDistributor.remove(thread);
|
||||
}
|
||||
{
|
||||
QMutexLocker locker(&_threadsDidHandlePacketSendMutex);
|
||||
_threadsDidHandlePacketSend.remove(thread);
|
||||
}
|
||||
{
|
||||
QMutexLocker locker(&_threadsDidCallWriteDatagramMutex);
|
||||
_threadsDidCallWriteDatagram.remove(thread);
|
||||
}
|
||||
}
|
||||
|
||||
int howManyThreadsDidSomething(QMutex& mutex, QMap<OctreeSendThread*, quint64>& something, quint64 since) {
|
||||
|
|
|
@ -124,7 +124,6 @@ public:
|
|||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler);
|
||||
|
||||
virtual void aboutToFinish();
|
||||
void forceNodeShutdown(SharedNodePointer node);
|
||||
|
||||
public slots:
|
||||
/// runs the octree server assignment
|
||||
|
@ -138,8 +137,12 @@ private slots:
|
|||
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void removeSendThread();
|
||||
|
||||
protected:
|
||||
using UniqueSendThread = std::unique_ptr<OctreeSendThread>;
|
||||
using SendThreads = std::unordered_map<QUuid, UniqueSendThread>;
|
||||
|
||||
virtual OctreePointer createTree() = 0;
|
||||
bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result);
|
||||
bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result);
|
||||
|
@ -153,6 +156,8 @@ protected:
|
|||
QString getFileLoadTime();
|
||||
QString getConfiguration();
|
||||
QString getStatusLink();
|
||||
|
||||
UniqueSendThread createSendThread(const SharedNodePointer& node);
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
|
@ -187,11 +192,11 @@ protected:
|
|||
int _backupInterval;
|
||||
int _maxBackupVersions;
|
||||
|
||||
static OctreeServer* _instance;
|
||||
|
||||
time_t _started;
|
||||
quint64 _startedUSecs;
|
||||
QString _safeServerName;
|
||||
|
||||
SendThreads _sendThreads;
|
||||
|
||||
static int _clientCount;
|
||||
static SimpleMovingAverage _averageLoopTime;
|
||||
|
|
77
cmake/externals/qxmpp/CMakeLists.txt
vendored
77
cmake/externals/qxmpp/CMakeLists.txt
vendored
|
@ -1,77 +0,0 @@
|
|||
set(EXTERNAL_NAME qxmpp)
|
||||
|
||||
# we need to find qmake inside QT_DIR
|
||||
find_program(QMAKE_COMMAND NAME qmake PATHS ${QT_DIR}/bin $ENV{QTTOOLDIR} NO_DEFAULT_PATH)
|
||||
|
||||
if (NOT QMAKE_COMMAND)
|
||||
message(FATAL_ERROR "Could not find qmake. Qxmpp cannot be compiled without qmake.")
|
||||
endif ()
|
||||
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
find_program(PLATFORM_BUILD_COMMAND nmake PATHS "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin")
|
||||
|
||||
if (NOT PLATFORM_BUILD_COMMAND)
|
||||
message(FATAL_ERROR "You asked CMake to grap QXmpp and build it, but nmake was not found. Please make sure the folder containing nmake.exe is in your PATH.")
|
||||
endif ()
|
||||
else ()
|
||||
find_program(PLATFORM_BUILD_COMMAND make)
|
||||
endif ()
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://qxmpp.googlecode.com/files/qxmpp-0.7.6.tar.gz
|
||||
URL_MD5 ee45a97313306ded2ff0f6618a3ed1e1
|
||||
BUILD_IN_SOURCE 1
|
||||
PATCH_COMMAND patch -p2 -t -N --verbose < ${CMAKE_CURRENT_SOURCE_DIR}/qxmpp.patch
|
||||
CONFIGURE_COMMAND ${QMAKE_COMMAND} PREFIX=<INSTALL_DIR>
|
||||
BUILD_COMMAND ${PLATFORM_BUILD_COMMAND}
|
||||
INSTALL_COMMAND ${PLATFORM_BUILD_COMMAND} install
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL Xcode)
|
||||
find_program(DITTO_COMMAND ditto)
|
||||
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
copy-from-xcode-install
|
||||
COMMENT "Copying from /tmp/hifi.dst${INSTALL_DIR} to move install to proper location"
|
||||
COMMAND ${DITTO_COMMAND} /tmp/hifi.dst${INSTALL_DIR} ${INSTALL_DIR}
|
||||
DEPENDEES install
|
||||
LOG 1
|
||||
)
|
||||
endif ()
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Qxmpp include directory")
|
||||
|
||||
set(_LIB_DIR ${INSTALL_DIR}/lib)
|
||||
|
||||
if (WIN32)
|
||||
set(_LIB_EXT "0.lib")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of QXmpp DLL")
|
||||
else ()
|
||||
if (APPLE)
|
||||
set(_LIB_EXT ".dylib")
|
||||
else ()
|
||||
set(_LIB_EXT ".so")
|
||||
endif ()
|
||||
|
||||
set(_LIB_PREFIX "lib")
|
||||
endif ()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/${_LIB_PREFIX}qxmpp${_LIB_EXT} CACHE FILEPATH "Path to QXmpp release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to QXmpp debug library")
|
|
@ -1,26 +1,27 @@
|
|||
macro(CONSOLIDATE_STACK_COMPONENTS)
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
|
||||
# Copy all the output for this target into the common deployment location
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
)
|
||||
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
elseif (TARGET_NAME STREQUAL "stack-manager")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
if (WIN32)
|
||||
# Copy all the output for this target into the common deployment location
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
)
|
||||
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
elseif (TARGET_NAME STREQUAL "stack-manager")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME}
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
|
30
cmake/macros/GenerateInstallers.cmake
Normal file
30
cmake/macros/GenerateInstallers.cmake
Normal file
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# GenerateInstallers.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Leonardo Murillo on 12/16/2015.
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(GENERATE_INSTALLERS)
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/full-stack-deployment")
|
||||
find_program(MAKENSIS_COMMAND makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS])
|
||||
if (NOT MAKENSIS_COMMAND)
|
||||
message(FATAL_ERROR "The Nullsoft Scriptable Install Systems is required for generating packaged installers on Windows (http://nsis.sourceforge.net/)")
|
||||
endif ()
|
||||
add_custom_target(
|
||||
build-package ALL
|
||||
DEPENDS interface assignment-client domain-server stack-manager
|
||||
COMMAND set INSTALLER_SOURCE_DIR=${CMAKE_BINARY_DIR}/full-stack-deployment
|
||||
COMMAND set INSTALLER_NAME=${CMAKE_BINARY_DIR}/${INSTALLER_NAME}
|
||||
COMMAND set INSTALLER_SCRIPTS_DIR=${CMAKE_SOURCE_DIR}/examples
|
||||
COMMAND set INSTALLER_COMPANY=${INSTALLER_COMPANY}
|
||||
COMMAND set INSTALLER_DIRECTORY=${INSTALLER_DIRECTORY}
|
||||
COMMAND CMD /C "\"${MAKENSIS_COMMAND}\" ${CMAKE_SOURCE_DIR}/tools/nsis/release.nsi"
|
||||
)
|
||||
endif ()
|
||||
endmacro()
|
|
@ -14,13 +14,22 @@ macro(INCLUDE_APPLICATION_VERSION)
|
|||
# We are relying on Jenkins defined environment variables to determine the origin of this build
|
||||
# and will only package if this is a PR or Release build
|
||||
if (DEFINED ENV{JOB_ID})
|
||||
set (DEPLOY_PACKAGE 1)
|
||||
set (BUILD_SEQ $ENV{JOB_ID})
|
||||
set(DEPLOY_PACKAGE 1)
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
set(INSTALLER_COMPANY "High Fidelity")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "interface-win64-${BUILD_SEQ}.exe")
|
||||
elseif (DEFINED ENV{ghprbPullId})
|
||||
set (DEPLOY_PACKAGE 1)
|
||||
set (BUILD_SEQ "PR-$ENV{ghprbPullId}")
|
||||
set(DEPLOY_PACKAGE 1)
|
||||
set(BUILD_SEQ "PR-$ENV{ghprbPullId}")
|
||||
set(INSTALLER_COMPANY "High Fidelity - PR")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}\\${BUILD_SEQ}")
|
||||
set(INSTALLER_NAME "pr-interface-win64-${BUILD_SEQ}.exe")
|
||||
else ()
|
||||
set(BUILD_SEQ "dev")
|
||||
set(INSTALLER_COMPANY "High Fidelity - Dev")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "dev-interface-win64.exe")
|
||||
endif ()
|
||||
configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${PROJECT_BINARY_DIR}/includes/ApplicationVersion.h")
|
||||
include_directories("${PROJECT_BINARY_DIR}/includes")
|
||||
|
|
|
@ -40,7 +40,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
|
|||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||
)
|
||||
elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE)
|
||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||
|
|
72
examples/acScripts/triggeredRecordingOnAC.js
Normal file
72
examples/acScripts/triggeredRecordingOnAC.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// triggeredRecordingOnAC.js
|
||||
// examples/acScripts
|
||||
//
|
||||
// Created by Thijs Wenker on 12/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is the triggered rocording script used in the winterSmashUp target practice game.
|
||||
// Change the CLIP_URL to your asset,
|
||||
// the RECORDING_CHANNEL and RECORDING_CHANNEL_MESSAGE are used to trigger it i.e.:
|
||||
// Messages.sendMessage("PlayBackOnAssignment", "BowShootingGameWelcome");
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
const CLIP_URL = "atp:3fbe82f2153c443f12f9a2b14ce2d7fa2fff81977263746d9e0885ea5aabed62.hfr";
|
||||
|
||||
const RECORDING_CHANNEL = 'PlayBackOnAssignment';
|
||||
const RECORDING_CHANNEL_MESSAGE = 'BowShootingGameWelcome'; // For each different assignment add a different message here.
|
||||
const PLAY_FROM_CURRENT_LOCATION = true;
|
||||
const USE_DISPLAY_NAME = true;
|
||||
const USE_ATTACHMENTS = true;
|
||||
const USE_AVATAR_MODEL = true;
|
||||
const AUDIO_OFFSET = 0.0;
|
||||
const STARTING_TIME = 0.0;
|
||||
const COOLDOWN_PERIOD = 0; // The period in ms that no animations can be played after one has been played already
|
||||
|
||||
var isPlaying = false;
|
||||
var isPlayable = true;
|
||||
|
||||
var playRecording = function() {
|
||||
if (!isPlayable || isPlaying) {
|
||||
return;
|
||||
}
|
||||
Agent.isAvatar = true;
|
||||
Recording.setPlayFromCurrentLocation(PLAY_FROM_CURRENT_LOCATION);
|
||||
Recording.setPlayerUseDisplayName(USE_DISPLAY_NAME);
|
||||
Recording.setPlayerUseAttachments(USE_ATTACHMENTS);
|
||||
Recording.setPlayerUseHeadModel(false);
|
||||
Recording.setPlayerUseSkeletonModel(USE_AVATAR_MODEL);
|
||||
Recording.setPlayerLoop(false);
|
||||
Recording.setPlayerTime(STARTING_TIME);
|
||||
Recording.setPlayerAudioOffset(AUDIO_OFFSET);
|
||||
Recording.loadRecording(CLIP_URL);
|
||||
Recording.startPlaying();
|
||||
isPlaying = true;
|
||||
isPlayable = false; // Set this true again after the cooldown period
|
||||
};
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
if (isPlaying && !Recording.isPlaying()) {
|
||||
print('Reached the end of the recording. Resetting.');
|
||||
isPlaying = false;
|
||||
Agent.isAvatar = false;
|
||||
if (COOLDOWN_PERIOD === 0) {
|
||||
isPlayable = true;
|
||||
return;
|
||||
}
|
||||
Script.setTimeout(function () {
|
||||
isPlayable;
|
||||
}, COOLDOWN_PERIOD);
|
||||
}
|
||||
});
|
||||
|
||||
Messages.subscribe(RECORDING_CHANNEL);
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel === RECORDING_CHANNEL && message === RECORDING_CHANNEL_MESSAGE) {
|
||||
playRecording();
|
||||
}
|
||||
});
|
54
examples/controllers/getHUDLookAtPositionTest.js
Normal file
54
examples/controllers/getHUDLookAtPositionTest.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// getHUDLookAtPositionTest.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
// This script demonstrates the testing of the HMD.getHUDLookAtPosition--() functions.
|
||||
// If these functions are working correctly, we'd expect to see a 3D cube and a 2D square
|
||||
// follow around the center of the HMD view.
|
||||
|
||||
var cubePosition = { x: 0, y: 0, z: 0 };
|
||||
var cubeSize = 0.03;
|
||||
var cube = Overlays.addOverlay("cube", {
|
||||
position: cubePosition,
|
||||
size: cubeSize,
|
||||
color: { red: 255, green: 0, blue: 0},
|
||||
alpha: 1,
|
||||
solid: false
|
||||
});
|
||||
|
||||
var square = Overlays.addOverlay("text", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: { red: 255, green: 255, blue: 0},
|
||||
backgroundColor: { red: 255, green: 255, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
if (!HMD.active) {
|
||||
return;
|
||||
}
|
||||
var lookAt3D = HMD.getHUDLookAtPosition3D();
|
||||
Overlays.editOverlay(cube, { position: lookAt3D });
|
||||
|
||||
var lookAt2D = HMD.getHUDLookAtPosition2D();
|
||||
Overlays.editOverlay(square, { x: lookAt2D.x, y: lookAt2D.y });
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
Overlays.deleteOverlay(cube);
|
||||
Overlays.deleteOverlay(square);
|
||||
});
|
||||
|
||||
|
File diff suppressed because it is too large
Load diff
87
examples/controllers/philipsVersion.js
Normal file
87
examples/controllers/philipsVersion.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// reticleTest.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2015/12/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
|
||||
//
|
||||
|
||||
function length(posA, posB) {
|
||||
var dx = posA.x - posB.x;
|
||||
var dy = posA.y - posB.y;
|
||||
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||
return length;
|
||||
}
|
||||
|
||||
var PITCH_DEADZONE = 1.0;
|
||||
var PITCH_MAX = 20.0;
|
||||
var YAW_DEADZONE = 1.0;
|
||||
var YAW_MAX = 20.0;
|
||||
var PITCH_SCALING = 10.0;
|
||||
var YAW_SCALING = 10.0;
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
function moveReticle(dY, dX) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||
print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||
}
|
||||
|
||||
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||
print("UNEXPECTED dX:" + dX + "----------------------------");
|
||||
dX = 0;
|
||||
}
|
||||
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||
print("UNEXPECTED dY:" + dY + "----------------------------");
|
||||
dY = 0;
|
||||
}
|
||||
|
||||
globalPos.x += dX;
|
||||
globalPos.y += dY;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
|
||||
var lastHandPitch = 0;
|
||||
var lastHandYaw = 0;
|
||||
|
||||
mapping.from(Controller.Standard.LeftHand).peek().to(function(pose) {
|
||||
var handEulers = Quat.safeEulerAngles(pose.rotation);
|
||||
//Vec3.print("handEulers:", handEulers);
|
||||
|
||||
var handPitch = handEulers.y;
|
||||
var handYaw = handEulers.x;
|
||||
var changePitch = (handPitch - lastHandPitch) * PITCH_SCALING;
|
||||
var changeYaw = (handYaw - lastHandYaw) * YAW_SCALING;
|
||||
if (Math.abs(changePitch) > PITCH_MAX) {
|
||||
print("Pitch: " + changePitch);
|
||||
changePitch = 0;
|
||||
}
|
||||
if (Math.abs(changeYaw) > YAW_MAX) {
|
||||
print("Yaw: " + changeYaw);
|
||||
changeYaw = 0;
|
||||
}
|
||||
changePitch = Math.abs(changePitch) < PITCH_DEADZONE ? 0 : changePitch;
|
||||
changeYaw = Math.abs(changeYaw) < YAW_DEADZONE ? 0 : changeYaw;
|
||||
moveReticle(changePitch, changeYaw);
|
||||
lastHandPitch = handPitch;
|
||||
lastHandYaw = handYaw;
|
||||
|
||||
});
|
||||
mapping.enable();
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
76
examples/controllers/proceduralHandPoseExample.js
Normal file
76
examples/controllers/proceduralHandPoseExample.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// proceduralHandPoseExample.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.examples.proceduralHandPose";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
var translation = { x: 0, y: 0.1, z: 0 };
|
||||
var translationDx = 0.01;
|
||||
var translationDy = 0.01;
|
||||
var translationDz = -0.01;
|
||||
var TRANSLATION_LIMIT = 0.5;
|
||||
|
||||
var pitch = 45;
|
||||
var yaw = 0;
|
||||
var roll = 45;
|
||||
var pitchDelta = 1;
|
||||
var yawDelta = -1;
|
||||
var rollDelta = 1;
|
||||
var ROTATION_MIN = -90;
|
||||
var ROTATION_MAX = 90;
|
||||
|
||||
mapping.from(function() {
|
||||
|
||||
// adjust the hand translation in a periodic back and forth motion for each of the 3 axes
|
||||
translation.x = translation.x + translationDx;
|
||||
translation.y = translation.y + translationDy;
|
||||
translation.z = translation.z + translationDz;
|
||||
if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 * TRANSLATION_LIMIT))) {
|
||||
translationDx = translationDx * -1;
|
||||
}
|
||||
if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 * TRANSLATION_LIMIT))) {
|
||||
translationDy = translationDy * -1;
|
||||
}
|
||||
if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 * TRANSLATION_LIMIT))) {
|
||||
translationDz = translationDz * -1;
|
||||
}
|
||||
|
||||
// adjust the hand rotation in a periodic back and forth motion for each of pitch/yaw/roll
|
||||
pitch = pitch + pitchDelta;
|
||||
yaw = yaw + yawDelta;
|
||||
roll = roll + rollDelta;
|
||||
if ((pitch > ROTATION_MAX) || (pitch < ROTATION_MIN)) {
|
||||
pitchDelta = pitchDelta * -1;
|
||||
}
|
||||
if ((yaw > ROTATION_MAX) || (yaw < ROTATION_MIN)) {
|
||||
yawDelta = yawDelta * -1;
|
||||
}
|
||||
if ((roll > ROTATION_MAX) || (roll < ROTATION_MIN)) {
|
||||
rollDelta = rollDelta * -1;
|
||||
}
|
||||
|
||||
var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);
|
||||
|
||||
var pose = {
|
||||
translation: translation,
|
||||
rotation: rotation,
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 }
|
||||
};
|
||||
return pose;
|
||||
}).debug(true).to(Controller.Standard.LeftHand);
|
||||
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// reticleHandAngularVelocityTest.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2015/12/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
|
||||
//
|
||||
|
||||
// If you set this to true, you will get the raw instantaneous angular velocity.
|
||||
// note: there is a LOT of noise in the hydra rotation, you will probably be very
|
||||
// frustrated with the level of jitter.
|
||||
var USE_INSTANTANEOUS_ANGULAR_VELOCITY = false;
|
||||
var whichHand = Controller.Standard.RightHand;
|
||||
var whichTrigger = Controller.Standard.RT;
|
||||
|
||||
|
||||
|
||||
function msecTimestampNow() {
|
||||
var d = new Date();
|
||||
return d.getTime();
|
||||
}
|
||||
|
||||
function length(posA, posB) {
|
||||
var dx = posA.x - posB.x;
|
||||
var dy = posA.y - posB.y;
|
||||
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||
return length;
|
||||
}
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
function moveReticle(dX, dY) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||
print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||
}
|
||||
|
||||
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||
print("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||
dX = 0;
|
||||
}
|
||||
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||
print("surpressing unexpectedly large change dY:" + dY + "----------------------------");
|
||||
dY = 0;
|
||||
}
|
||||
|
||||
globalPos.x += dX;
|
||||
globalPos.y += dY;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
var firstTime = true;
|
||||
var lastTime = msecTimestampNow();
|
||||
var previousRotation;
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||
mapping.from(whichHand).peek().to(function(pose) {
|
||||
|
||||
var MSECS_PER_SECOND = 1000;
|
||||
var now = msecTimestampNow();
|
||||
var deltaMsecs = (now - lastTime);
|
||||
var deltaTime = deltaMsecs / MSECS_PER_SECOND;
|
||||
|
||||
if (firstTime) {
|
||||
previousRotation = pose.rotation;
|
||||
lastTime = msecTimestampNow();
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
// pose.angularVelocity - is the angularVelocity in a "physics" sense, that
|
||||
// means the direction of the vector is the axis of symetry of rotation
|
||||
// and the scale of the vector is the speed in radians/second of rotation
|
||||
// around that axis.
|
||||
//
|
||||
// we want to deconstruct that in the portion of the rotation on the Y axis
|
||||
// and make that portion move our reticle in the horizontal/X direction
|
||||
// and the portion of the rotation on the X axis and make that portion
|
||||
// move our reticle in the veritcle/Y direction
|
||||
var xPart = -pose.angularVelocity.y;
|
||||
var yPart = -pose.angularVelocity.x;
|
||||
|
||||
// pose.angularVelocity is "smoothed", we can calculate our own instantaneous
|
||||
// angular velocity as such:
|
||||
if (USE_INSTANTANEOUS_ANGULAR_VELOCITY) {
|
||||
var previousConjugate = Quat.conjugate(previousRotation);
|
||||
var deltaRotation = Quat.multiply(pose.rotation, previousConjugate);
|
||||
var normalizedDeltaRotation = Quat.normalize(deltaRotation);
|
||||
var axis = Quat.axis(normalizedDeltaRotation);
|
||||
var speed = Quat.angle(normalizedDeltaRotation) / deltaTime;
|
||||
var instantaneousAngularVelocity = Vec3.multiply(speed, axis);
|
||||
|
||||
xPart = -instantaneousAngularVelocity.y;
|
||||
yPart = -instantaneousAngularVelocity.x;
|
||||
|
||||
previousRotation = pose.rotation;
|
||||
}
|
||||
|
||||
var MOVE_SCALE = 1;
|
||||
lastTime = now;
|
||||
|
||||
var dX = (xPart * MOVE_SCALE) / deltaTime;
|
||||
var dY = (yPart * MOVE_SCALE) / deltaTime;
|
||||
|
||||
moveReticle(dX, dY);
|
||||
});
|
||||
mapping.enable();
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
||||
|
||||
|
254
examples/controllers/reticleHandRotationTest.js
Normal file
254
examples/controllers/reticleHandRotationTest.js
Normal file
|
@ -0,0 +1,254 @@
|
|||
//
|
||||
// reticleHandRotationTest.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var DEBUGGING = false;
|
||||
|
||||
Math.clamp=function(a,b,c) {
|
||||
return Math.max(b,Math.min(c,a));
|
||||
}
|
||||
|
||||
function length(posA, posB) {
|
||||
var dx = posA.x - posB.x;
|
||||
var dy = posA.y - posB.y;
|
||||
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||
return length;
|
||||
}
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
function moveReticleAbsolute(x, y) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
var dX = x - globalPos.x;
|
||||
var dY = y - globalPos.y;
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||
debugPrint("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||
}
|
||||
|
||||
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||
debugPrint("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||
}
|
||||
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||
debugPrint("surpressing unexpectedly large change dY:" + dY + "----------------------------");
|
||||
}
|
||||
|
||||
globalPos.x = x;
|
||||
globalPos.y = y;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||
mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||
mapping.enable();
|
||||
|
||||
|
||||
var lastRotatedLeft = Vec3.UNIT_NEG_Y;
|
||||
var lastRotatedRight = Vec3.UNIT_NEG_Y;
|
||||
|
||||
function debugPrint(message) {
|
||||
if (DEBUGGING) {
|
||||
print(message);
|
||||
}
|
||||
}
|
||||
|
||||
var MAX_WAKE_UP_DISTANCE = 0.005;
|
||||
var MIN_WAKE_UP_DISTANCE = 0.001;
|
||||
var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE;
|
||||
var INCREMENTAL_WAKE_UP_DISTANCE = 0.001;
|
||||
|
||||
var MAX_SLEEP_DISTANCE = 0.0004;
|
||||
var MIN_SLEEP_DISTANCE = 0.00001; //0.00002;
|
||||
var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE;
|
||||
var INCREMENTAL_SLEEP_DISTANCE = 0.000002; // 0.00002;
|
||||
|
||||
var leftAsleep = true;
|
||||
var rightAsleep = true;
|
||||
|
||||
var leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
var rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
|
||||
var leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
var rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
var poseRight = Controller.getPoseValue(Controller.Standard.RightHand);
|
||||
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
|
||||
|
||||
// NOTE: hack for now
|
||||
var screenSizeX = 1920;
|
||||
var screenSizeY = 1080;
|
||||
|
||||
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
||||
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
||||
|
||||
var suppressRight = false;
|
||||
var suppressLeft = false;
|
||||
|
||||
// What I really want to do is to slowly increase the epsilon you have to move it
|
||||
// to wake up, the longer you go without moving it
|
||||
var leftDistance = Vec3.distance(rotatedLeft, lastRotatedLeft);
|
||||
var rightDistance = Vec3.distance(rotatedRight, lastRotatedRight);
|
||||
|
||||
// check to see if hand should wakeup or sleep
|
||||
if (leftAsleep) {
|
||||
if (leftDistance > leftWakeUpDistance) {
|
||||
leftAsleep = false;
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// grow the wake up distance to make it harder to wake up
|
||||
leftWakeUpDistance = Math.min(leftWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE);
|
||||
}
|
||||
} else {
|
||||
// we are awake, determine if we should fall asleep, if we haven't moved
|
||||
// at least as much as our sleep distance then we sleep
|
||||
if (leftDistance < leftSleepDistance) {
|
||||
leftAsleep = true;
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// if we moved more than the sleep amount, but we moved less than the max sleep
|
||||
// amount, then increase our liklihood of sleep.
|
||||
if (leftDistance < MAX_SLEEP_DISTANCE) {
|
||||
print("growing sleep....");
|
||||
leftSleepDistance = Math.max(leftSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE);
|
||||
} else {
|
||||
// otherwise reset it to initial
|
||||
leftSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (leftAsleep) {
|
||||
suppressLeft = true;
|
||||
debugPrint("suppressing left not moving enough");
|
||||
}
|
||||
|
||||
// check to see if hand should wakeup or sleep
|
||||
if (rightAsleep) {
|
||||
if (rightDistance > rightWakeUpDistance) {
|
||||
rightAsleep = false;
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// grow the wake up distance to make it harder to wake up
|
||||
rightWakeUpDistance = Math.min(rightWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE);
|
||||
}
|
||||
} else {
|
||||
// we are awake, determine if we should fall asleep, if we haven't moved
|
||||
// at least as much as our sleep distance then we sleep
|
||||
if (rightDistance < rightSleepDistance) {
|
||||
rightAsleep = true;
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE;
|
||||
} else {
|
||||
// if we moved more than the sleep amount, but we moved less than the max sleep
|
||||
// amount, then increase our liklihood of sleep.
|
||||
if (rightDistance < MAX_SLEEP_DISTANCE) {
|
||||
print("growing sleep....");
|
||||
rightSleepDistance = Math.max(rightSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE);
|
||||
} else {
|
||||
// otherwise reset it to initial
|
||||
rightSleepDistance = INITIAL_SLEEP_DISTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rightAsleep) {
|
||||
suppressRight = true;
|
||||
debugPrint("suppressing right not moving enough");
|
||||
}
|
||||
|
||||
// check to see if hand is on base station
|
||||
if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) {
|
||||
suppressLeft = true;
|
||||
debugPrint("suppressing left on base station");
|
||||
}
|
||||
if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) {
|
||||
suppressRight = true;
|
||||
debugPrint("suppressing right on base station");
|
||||
}
|
||||
|
||||
// Keep track of last rotations, to detect resting (but not on base station hands) in the future
|
||||
lastRotatedLeft = rotatedLeft;
|
||||
lastRotatedRight = rotatedRight;
|
||||
|
||||
if (suppressLeft && suppressRight) {
|
||||
debugPrint("both hands suppressed bail out early");
|
||||
return;
|
||||
}
|
||||
|
||||
if (suppressLeft) {
|
||||
debugPrint("right only");
|
||||
rotatedLeft = rotatedRight;
|
||||
}
|
||||
if (suppressRight) {
|
||||
debugPrint("left only");
|
||||
rotatedRight = rotatedLeft;
|
||||
}
|
||||
|
||||
// Average the two hand positions, if either hand is on base station, the
|
||||
// other hand becomes the only used hand and the average is the hand in use
|
||||
var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5);
|
||||
|
||||
if (DEBUGGING) {
|
||||
Vec3.print("rotatedRight:", rotatedRight);
|
||||
Vec3.print("rotatedLeft:", rotatedLeft);
|
||||
Vec3.print("rotated:", rotated);
|
||||
}
|
||||
|
||||
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
|
||||
var absoluteYaw = -rotated.x; // from -1 left to 1 right
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("absolutePitch:" + absolutePitch);
|
||||
print("absoluteYaw:" + absoluteYaw);
|
||||
Vec3.print("rotated:", rotated);
|
||||
}
|
||||
|
||||
var ROTATION_BOUND = 0.6;
|
||||
var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND);
|
||||
if (DEBUGGING) {
|
||||
print("clampYaw:" + clampYaw);
|
||||
print("clampPitch:" + clampPitch);
|
||||
}
|
||||
|
||||
// using only from -ROTATION_BOUND to ROTATION_BOUND
|
||||
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("xRatio:" + xRatio);
|
||||
print("yRatio:" + yRatio);
|
||||
}
|
||||
|
||||
var x = screenSizeX * xRatio;
|
||||
var y = screenSizeY * yRatio;
|
||||
|
||||
if (DEBUGGING) {
|
||||
print("position x:" + x + " y:" + y);
|
||||
}
|
||||
if (!(xRatio == 0.5 && yRatio == 0)) {
|
||||
moveReticleAbsolute(x, y);
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
||||
|
||||
|
|
@ -33,7 +33,6 @@ var mappingJSON = {
|
|||
|
||||
mapping = Controller.parseMapping(JSON.stringify(mappingJSON));
|
||||
mapping.enable();
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
||||
|
|
|
@ -62,7 +62,7 @@ var directory = (function () {
|
|||
function setUp() {
|
||||
viewport = Controller.getViewportDimensions();
|
||||
|
||||
directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false);
|
||||
directoryWindow = new OverlayWebWindow('Directory', DIRECTORY_URL, 900, 700, false);
|
||||
directoryWindow.setVisible(false);
|
||||
|
||||
directoryButton = Overlays.addOverlay("image", {
|
||||
|
|
|
@ -140,9 +140,33 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", {
|
|||
});
|
||||
|
||||
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
||||
var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false);
|
||||
var marketplaceWindow = new OverlayWebWindow('Marketplace', "about:blank", 900, 700, false);
|
||||
marketplaceWindow.setVisible(false);
|
||||
|
||||
function showMarketplace(marketplaceID) {
|
||||
var url = MARKETPLACE_URL;
|
||||
if (marketplaceID) {
|
||||
url = url + "/items/" + marketplaceID;
|
||||
}
|
||||
print("setting marketplace URL to " + url);
|
||||
marketplaceWindow.setURL(url);
|
||||
marketplaceWindow.setVisible(true);
|
||||
marketplaceWindow.raise();
|
||||
}
|
||||
|
||||
function hideMarketplace() {
|
||||
marketplaceWindow.setVisible(false);
|
||||
marketplaceWindow.setURL("about:blank");
|
||||
}
|
||||
|
||||
function toggleMarketplace() {
|
||||
if (marketplaceWindow.visible) {
|
||||
hideMarketplace();
|
||||
} else {
|
||||
showMarketplace();
|
||||
}
|
||||
}
|
||||
|
||||
var toolBar = (function() {
|
||||
var that = {},
|
||||
toolBar,
|
||||
|
@ -413,12 +437,9 @@ var toolBar = (function() {
|
|||
newModelButtonDown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) {
|
||||
if (marketplaceWindow.url != MARKETPLACE_URL) {
|
||||
marketplaceWindow.setURL(MARKETPLACE_URL);
|
||||
}
|
||||
marketplaceWindow.setVisible(true);
|
||||
marketplaceWindow.raise();
|
||||
toggleMarketplace();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1336,6 +1357,7 @@ function getPositionToCreateEntity() {
|
|||
}
|
||||
|
||||
function importSVO(importURL) {
|
||||
print("Import URL requested: " + importURL)
|
||||
if (!Entities.canAdjustLocks()) {
|
||||
Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
|
||||
return;
|
||||
|
@ -1574,11 +1596,7 @@ PropertiesTool = function(opts) {
|
|||
pushCommandForSelections();
|
||||
selectionManager._update();
|
||||
} else if (data.type == "showMarketplace") {
|
||||
if (marketplaceWindow.url != data.url) {
|
||||
marketplaceWindow.setURL(data.url);
|
||||
}
|
||||
marketplaceWindow.setVisible(true);
|
||||
marketplaceWindow.raise();
|
||||
showMarketplace();
|
||||
} else if (data.type == "action") {
|
||||
if (data.action == "moveSelectionToGrid") {
|
||||
if (selectionManager.hasSelection()) {
|
||||
|
@ -1859,12 +1877,7 @@ var propertyMenu = PopupMenu();
|
|||
|
||||
propertyMenu.onSelectMenuItem = function(name) {
|
||||
if (propertyMenu.marketplaceID) {
|
||||
var url = MARKETPLACE_URL + "/items/" + propertyMenu.marketplaceID;
|
||||
if (marketplaceWindow.url != url) {
|
||||
marketplaceWindow.setURL(url);
|
||||
}
|
||||
marketplaceWindow.setVisible(true);
|
||||
marketplaceWindow.raise();
|
||||
showMarketplace(propertyMenu.marketplaceID);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
});
|
||||
break;
|
||||
case 3:
|
||||
print("Radius spread");
|
||||
print("Radius spread - temporarily not working");
|
||||
Entities.editEntity(particles, {
|
||||
accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
radiusSpread: 0.035
|
||||
|
@ -71,6 +71,7 @@
|
|||
Entities.editEntity(particles, {
|
||||
radiusSpread: 0.0,
|
||||
radiusStart: 0.0,
|
||||
particleRadius: 2 * PARTICLE_RADIUS, // Bezier interpolation used means that middle value isn't intersected
|
||||
radiusFinish: 0.0
|
||||
});
|
||||
break;
|
||||
|
@ -78,12 +79,13 @@
|
|||
print("Alpha 0.5");
|
||||
Entities.editEntity(particles, {
|
||||
radiusStart: PARTICLE_RADIUS,
|
||||
particleRadius: PARTICLE_RADIUS,
|
||||
radiusFinish: PARTICLE_RADIUS,
|
||||
alpha: 0.5
|
||||
});
|
||||
break;
|
||||
case 6:
|
||||
print("Alpha spread");
|
||||
print("Alpha spread - temporarily not working");
|
||||
Entities.editEntity(particles, {
|
||||
alpha: 0.5,
|
||||
alphaSpread: 0.5
|
||||
|
@ -99,7 +101,7 @@
|
|||
});
|
||||
break;
|
||||
case 8:
|
||||
print("Color spread");
|
||||
print("Color spread - temporarily not working");
|
||||
Entities.editEntity(particles, {
|
||||
alpha: 1.0,
|
||||
alphaStart: 1.0,
|
||||
|
@ -255,7 +257,6 @@
|
|||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
lifespan: 5.0,
|
||||
visible: false,
|
||||
locked: false,
|
||||
isEmitting: false,
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
|
|
145
examples/flowArts/arcBall/arcBall.js
Normal file
145
examples/flowArts/arcBall/arcBall.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
//
|
||||
// arcBall.js
|
||||
// examples/arcBall
|
||||
//
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creats a particle light ball which makes particle trails as you move it.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
|
||||
var scriptURL = Script.resolvePath("arcBallEntityScript.js?v1" + Math.random());
|
||||
ArcBall = function(spawnPosition) {
|
||||
|
||||
var colorPalette = [{
|
||||
red: 25,
|
||||
green: 20,
|
||||
blue: 162
|
||||
}];
|
||||
|
||||
|
||||
var containerBall = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
name: "Arc Ball",
|
||||
script: scriptURL,
|
||||
position: Vec3.sum(spawnPosition, {
|
||||
x: 0,
|
||||
y: .7,
|
||||
z: 0
|
||||
}),
|
||||
dimensions: {
|
||||
x: .05,
|
||||
y: .05,
|
||||
z: .05
|
||||
},
|
||||
color: {
|
||||
red: 100,
|
||||
green: 10,
|
||||
blue: 150
|
||||
},
|
||||
ignoreForCollisions: true,
|
||||
damping: 0.8,
|
||||
collisionsWillMove: true,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
// relativePosition: {
|
||||
// x: 0,
|
||||
// y: -0.5,
|
||||
// z: 0.0
|
||||
// },
|
||||
},
|
||||
// invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
name: "ballLight",
|
||||
parentID: containerBall,
|
||||
dimensions: {
|
||||
x: 30,
|
||||
y: 30,
|
||||
z: 30
|
||||
},
|
||||
color: colorPalette[randInt(0, colorPalette.length)],
|
||||
intensity: 5
|
||||
});
|
||||
|
||||
|
||||
var arcBall = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
parentID: containerBall,
|
||||
isEmitting: true,
|
||||
name: "Arc Ball Particle Effect",
|
||||
colorStart: {
|
||||
red: 200,
|
||||
green: 20,
|
||||
blue: 40
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
colorFinish: {
|
||||
red: 25,
|
||||
green: 20,
|
||||
blue: 255
|
||||
},
|
||||
maxParticles: 100000,
|
||||
lifespan: 2,
|
||||
emitRate: 400,
|
||||
emitSpeed: .1,
|
||||
lifetime: -1,
|
||||
speedSpread: 0.0,
|
||||
emitDimensions: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
polarStart: 0,
|
||||
polarFinish: Math.PI,
|
||||
azimuthStart: -Math.PI,
|
||||
azimuthFinish: Math.PI,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: .00,
|
||||
y: .00,
|
||||
z: .00
|
||||
},
|
||||
particleRadius: 0.02,
|
||||
radiusSpread: 0,
|
||||
radiusStart: 0.03,
|
||||
radiusFinish: 0.0003,
|
||||
alpha: 0,
|
||||
alphaSpread: .5,
|
||||
alphaStart: 0,
|
||||
alphaFinish: 0.5,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
emitterShouldTrail: true
|
||||
})
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(arcBall);
|
||||
Entities.deleteEntity(containerBall);
|
||||
Entities.deleteEntity(light);
|
||||
}
|
||||
|
||||
this.cleanup = cleanup;
|
||||
}
|
155
examples/flowArts/arcBall/arcBallEntityScript.js
Normal file
155
examples/flowArts/arcBall/arcBallEntityScript.js
Normal file
|
@ -0,0 +1,155 @@
|
|||
// arcBallEntityScript.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script handles the logic for the arcBall rave toy
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
var ArcBall = function() {
|
||||
_this = this;
|
||||
this.colorPalette = [{
|
||||
red: 25,
|
||||
green: 20,
|
||||
blue: 162
|
||||
}, {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 10
|
||||
}];
|
||||
|
||||
this.searchRadius = 10;
|
||||
};
|
||||
|
||||
ArcBall.prototype = {
|
||||
isGrabbed: false,
|
||||
startDistantGrab: function() {
|
||||
this.searchForNearbyArcBalls();
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
this.searchForNearbyArcBalls();
|
||||
},
|
||||
|
||||
searchForNearbyArcBalls: function() {
|
||||
//Search for nearby balls and create an arc to it if one is found
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position
|
||||
var entities = Entities.findEntities(position, this.searchRadius);
|
||||
entities.forEach(function(entity) {
|
||||
var props = Entities.getEntityProperties(entity, ["position", "name"]);
|
||||
if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) {
|
||||
_this.target = entity;
|
||||
_this.createBeam(position, props.position);
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createBeam: function(startPosition, endPosition) {
|
||||
|
||||
// Creates particle arc from start position to end position
|
||||
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
|
||||
var sourceToTargetVec = Vec3.subtract(endPosition, startPosition);
|
||||
var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec);
|
||||
emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation);
|
||||
|
||||
var color = this.colorPalette[randInt(0, this.colorPalette.length)];
|
||||
var props = {
|
||||
type: "ParticleEffect",
|
||||
name: "Particle Arc",
|
||||
parentID: this.entityID,
|
||||
parentJointIndex: -1,
|
||||
// position: startPosition,
|
||||
isEmitting: true,
|
||||
colorStart: color,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
colorFinish: color,
|
||||
maxParticles: 100000,
|
||||
lifespan: 1,
|
||||
emitRate: 1000,
|
||||
emitOrientation: emitOrientation,
|
||||
emitSpeed: 1,
|
||||
speedSpread: 0.02,
|
||||
emitDimensions: {
|
||||
x: .01,
|
||||
y: .01,
|
||||
z: .01
|
||||
},
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthStart: 0.02,
|
||||
azimuthFinish: .01,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radiusStart: 0.01,
|
||||
radiusFinish: 0.005,
|
||||
radiusSpread: 0.005,
|
||||
alpha: 0.5,
|
||||
alphaSpread: 0.1,
|
||||
alphaStart: 0.5,
|
||||
alphaFinish: 0.5,
|
||||
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
|
||||
emitterShouldTrail: true
|
||||
}
|
||||
this.particleArc = Entities.addEntity(props);
|
||||
},
|
||||
|
||||
updateBeam: function() {
|
||||
if(!this.target) {
|
||||
return;
|
||||
}
|
||||
var startPosition = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
var targetPosition = Entities.getEntityProperties(this.target, "position").position;
|
||||
var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
|
||||
var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition);
|
||||
var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec);
|
||||
Entities.editEntity(this.particleArc, {
|
||||
emitOrientation: emitOrientation
|
||||
});
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
this.updateBeam();
|
||||
},
|
||||
|
||||
continueDistantGrab: function() {
|
||||
this.updateBeam();
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
Entities.editEntity(this.particleArc, {
|
||||
isEmitting: false
|
||||
});
|
||||
this.target = null;
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
if (this.particleArc) {
|
||||
Entities.deleteEntity(this.particleArc);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
return new ArcBall();
|
||||
});
|
91
examples/flowArts/flowArtsHutSpawner.js
Normal file
91
examples/flowArts/flowArtsHutSpawner.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// flowArtsHutSpawner.js
|
||||
// examples/flowArts
|
||||
//
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
Script.include("lightBall/lightBall.js");
|
||||
Script.include("raveStick/raveStick.js");
|
||||
Script.include("lightSaber/lightSaber.js");
|
||||
Script.include("arcBall/arcBall.js");
|
||||
|
||||
|
||||
|
||||
var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
|
||||
basePosition.y = MyAvatar.position.y + 1;
|
||||
|
||||
// RAVE ITEMS
|
||||
// var lightBall = new LightBall(basePosition);
|
||||
|
||||
var arcBall = new ArcBall(basePosition);
|
||||
var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0}));
|
||||
var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1}));
|
||||
var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1}));
|
||||
|
||||
|
||||
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/RaveRoom.fbx";
|
||||
|
||||
var roomDimensions = {x: 30.58, y: 15.29, z: 30.58};
|
||||
|
||||
var raveRoom = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "Rave Hut Room",
|
||||
modelURL: modelURL,
|
||||
position: basePosition,
|
||||
dimensions:roomDimensions,
|
||||
visible: true
|
||||
});
|
||||
|
||||
var floor = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "Rave Floor",
|
||||
position: Vec3.sum(basePosition, {x: 0, y: -1.2, z: 0}),
|
||||
dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z},
|
||||
color: {red: 50, green: 10, blue: 100},
|
||||
shapeType: 'box'
|
||||
});
|
||||
|
||||
|
||||
|
||||
var lightZone = Entities.addEntity({
|
||||
type: "Zone",
|
||||
name: "Rave Hut Zone",
|
||||
shapeType: 'box',
|
||||
keyLightIntensity: 0.4,
|
||||
keyLightColor: {
|
||||
red: 50,
|
||||
green: 0,
|
||||
blue: 50
|
||||
},
|
||||
keyLightAmbientIntensity: .2,
|
||||
position: MyAvatar.position,
|
||||
dimensions: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
z: 100
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
|
||||
Entities.deleteEntity(raveRoom);
|
||||
Entities.deleteEntity(lightZone);
|
||||
Entities.deleteEntity(floor);
|
||||
// lightBall.cleanup();
|
||||
arcBall.cleanup();
|
||||
arcBall2.cleanup();
|
||||
raveStick.cleanup();
|
||||
lightSaber.cleanup();
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
145
examples/flowArts/lightBall/lightBall.js
Normal file
145
examples/flowArts/lightBall/lightBall.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
//
|
||||
// LightBall.js
|
||||
// examples/lightBall
|
||||
//
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This script creats a particle light ball which makes particle trails as you move it.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
LightBall = function(spawnPosition) {
|
||||
|
||||
var colorPalette = [{
|
||||
red: 25,
|
||||
green: 20,
|
||||
blue: 162
|
||||
}];
|
||||
|
||||
|
||||
var containerBall = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
name: "containerBall",
|
||||
position: Vec3.sum(spawnPosition, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}),
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1
|
||||
},
|
||||
color: {
|
||||
red: 15,
|
||||
green: 10,
|
||||
blue: 150
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
// gravity: {
|
||||
// x: 0,
|
||||
// y: -0.5,
|
||||
// z: 0
|
||||
// },
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0.1,
|
||||
z: 0
|
||||
}
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
name: "ballLight",
|
||||
parentID: containerBall,
|
||||
dimensions: {
|
||||
x: 30,
|
||||
y: 30,
|
||||
z: 30
|
||||
},
|
||||
color: colorPalette[randInt(0, colorPalette.length)],
|
||||
intensity: 5
|
||||
});
|
||||
|
||||
|
||||
var lightBall = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
parentID: containerBall,
|
||||
isEmitting: true,
|
||||
name: "particleBall",
|
||||
colorStart: {
|
||||
red: 200,
|
||||
green: 20,
|
||||
blue: 40
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
colorFinish: {
|
||||
red: 25,
|
||||
green: 20,
|
||||
blue: 255
|
||||
},
|
||||
maxParticles: 100000,
|
||||
lifespan: 2,
|
||||
emitRate: 10000,
|
||||
emitSpeed: 0.1,
|
||||
lifetime: -1,
|
||||
speedSpread: 0.0,
|
||||
emitDimensions: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
polarStart: 0,
|
||||
polarFinish: Math.PI,
|
||||
azimuthStart: -Math.PI,
|
||||
azimuthFinish: Math.PI,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0.00,
|
||||
y: 0.00,
|
||||
z: 0.00
|
||||
},
|
||||
particleRadius: 0.02,
|
||||
radiusSpread: 0,
|
||||
radiusStart: 0.03,
|
||||
radiusFinish: 0.0003,
|
||||
alpha: 0,
|
||||
alphaSpread: 0.5,
|
||||
alphaStart: 0,
|
||||
alphaFinish: 0.5,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
emitterShouldTrail: true
|
||||
})
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(lightBall);
|
||||
Entities.deleteEntity(containerBall);
|
||||
Entities.deleteEntity(light);
|
||||
}
|
||||
|
||||
this.cleanup = cleanup;
|
||||
}
|
67
examples/flowArts/lightSaber/lightSaber.js
Normal file
67
examples/flowArts/lightSaber/lightSaber.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// LightSaber.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates a lightsaber which activates on grab
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/lightSaber.fbx";
|
||||
var scriptURL = Script.resolvePath("lightSaberEntityScript.js");
|
||||
LightSaber = function(spawnPosition) {
|
||||
|
||||
var saberHandle = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "LightSaber Handle",
|
||||
modelURL: modelURL,
|
||||
position: spawnPosition,
|
||||
shapeType: 'box',
|
||||
collisionsWillMove: true,
|
||||
script: scriptURL,
|
||||
dimensions: {
|
||||
x: 0.06,
|
||||
y: 0.06,
|
||||
z: 0.31
|
||||
},
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: -0.1
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(180, 90, 0)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
name: "raveLight",
|
||||
parentID: saberHandle,
|
||||
dimensions: {
|
||||
x: 30,
|
||||
y: 30,
|
||||
z: 30
|
||||
},
|
||||
color: {red: 200, green: 10, blue: 200},
|
||||
intensity: 5
|
||||
});
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(saberHandle);
|
||||
}
|
||||
|
||||
this.cleanup = cleanup;
|
||||
}
|
116
examples/flowArts/lightSaber/lightSaberEntityScript.js
Normal file
116
examples/flowArts/lightSaber/lightSaberEntityScript.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
// lightSaberEntityScript.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script creates the logic for displaying the lightsaber beam.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here
|
||||
var LightSaber = function() {
|
||||
_this = this;
|
||||
this.colorPalette = [{
|
||||
red: 0,
|
||||
green: 200,
|
||||
blue: 40
|
||||
}, {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 40
|
||||
}];
|
||||
};
|
||||
|
||||
LightSaber.prototype = {
|
||||
isGrabbed: false,
|
||||
|
||||
startNearGrab: function() {
|
||||
Entities.editEntity(this.beam, {
|
||||
isEmitting: true,
|
||||
visible: true
|
||||
});
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
Entities.editEntity(this.beam, {
|
||||
visible: false,
|
||||
isEmitting: false
|
||||
});
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.createBeam();
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
Entities.deleteEntity(this.beam);
|
||||
},
|
||||
|
||||
createBeam: function() {
|
||||
|
||||
this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
|
||||
var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
|
||||
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
|
||||
var position = this.props.position;
|
||||
|
||||
var color = this.colorPalette[randInt(0, this.colorPalette.length)];
|
||||
var props = {
|
||||
type: "ParticleEffect",
|
||||
name: "LightSaber Beam",
|
||||
position: position,
|
||||
parentID: this.entityID,
|
||||
isEmitting: false,
|
||||
colorStart: color,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
colorFinish: color,
|
||||
maxParticles: 100000,
|
||||
lifespan: 2,
|
||||
emitRate: 1000,
|
||||
emitOrientation: forwardQuat,
|
||||
emitSpeed: 0.7,
|
||||
speedSpread: 0.0,
|
||||
emitDimensions: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthStart: 0.1,
|
||||
azimuthFinish: 0.01,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: .00,
|
||||
y: .00,
|
||||
z: .00
|
||||
},
|
||||
radiusStart: 0.03,
|
||||
adiusFinish: 0.025,
|
||||
alpha: 0.7,
|
||||
alphaSpread: 0.1,
|
||||
alphaStart: 0.5,
|
||||
alphaFinish: 0.5,
|
||||
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
|
||||
emitterShouldTrail: false
|
||||
}
|
||||
this.beam = Entities.addEntity(props);
|
||||
|
||||
}
|
||||
};
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new LightSaber();
|
||||
});
|
191
examples/flowArts/lightTrails.js
Normal file
191
examples/flowArts/lightTrails.js
Normal file
|
@ -0,0 +1,191 @@
|
|||
//
|
||||
// lightTrails.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on 5/14/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates light trails as you move your hydra hands
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var eraseTrail = true;
|
||||
var ugLSD = 25;
|
||||
// var eraseTrail = false;
|
||||
var LEFT = 0;
|
||||
var RIGHT = 1;
|
||||
var MAX_POINTS_PER_LINE = 50;
|
||||
|
||||
var LIFETIME = 6000;
|
||||
var DRAWING_DEPTH = 0.8;
|
||||
var LINE_DIMENSIONS = 100;
|
||||
|
||||
|
||||
var MIN_POINT_DISTANCE = 0.02;
|
||||
|
||||
|
||||
var colorPalette = [{
|
||||
red: 250,
|
||||
green: 137,
|
||||
blue: 162
|
||||
}, {
|
||||
red: 204,
|
||||
green: 244,
|
||||
blue: 249
|
||||
}, {
|
||||
red: 146,
|
||||
green: 206,
|
||||
blue: 116
|
||||
}, {
|
||||
red: 240,
|
||||
green: 87,
|
||||
blue: 129
|
||||
}];
|
||||
|
||||
var STROKE_WIDTH = 0.04;
|
||||
|
||||
function controller(side, triggerAction) {
|
||||
this.triggerHeld = false;
|
||||
this.triggerThreshold = 0.9;
|
||||
this.side = side;
|
||||
this.triggerAction = triggerAction;
|
||||
var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png";
|
||||
|
||||
this.light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
position: MyAvatar.position,
|
||||
dimensions: {
|
||||
x: 30,
|
||||
y: 30,
|
||||
z: 30
|
||||
},
|
||||
color: colorPalette[randInt(0, colorPalette.length)],
|
||||
intensity: 5
|
||||
});
|
||||
|
||||
this.trail = Entities.addEntity({
|
||||
type: "PolyLine",
|
||||
dimensions: {
|
||||
x: LINE_DIMENSIONS,
|
||||
y: LINE_DIMENSIONS,
|
||||
z: LINE_DIMENSIONS
|
||||
},
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
textures: texture,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
this.points = [];
|
||||
this.normals = [];
|
||||
this.strokeWidths = [];
|
||||
var self = this;
|
||||
|
||||
this.trailEraseInterval = Script.setInterval(function() {
|
||||
if (self.points.length > 0 && eraseTrail) {
|
||||
self.points.shift();
|
||||
self.normals.shift();
|
||||
self.strokeWidths.shift();
|
||||
Entities.editEntity(self.trail, {
|
||||
linePoints: self.points,
|
||||
strokeWidths: self.strokeWidths,
|
||||
normals: self.normals
|
||||
});
|
||||
}
|
||||
}, ugLSD);
|
||||
|
||||
|
||||
this.setTrailPosition = function(position) {
|
||||
this.trailPosition = position;
|
||||
Entities.editEntity(this.trail, {
|
||||
position: this.trailPosition
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
this.updateControllerState();
|
||||
var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH);
|
||||
var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset);
|
||||
Entities.editEntity(this.light, {
|
||||
position: newTrailPos
|
||||
});
|
||||
|
||||
|
||||
if (!this.drawing) {
|
||||
this.setTrailPosition(newTrailPos);
|
||||
this.drawing = true;
|
||||
}
|
||||
|
||||
if (this.drawing) {
|
||||
var localPoint = Vec3.subtract(newTrailPos, this.trailPosition);
|
||||
if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) {
|
||||
//Need a minimum distance to avoid binormal NANs
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.points.length === MAX_POINTS_PER_LINE) {
|
||||
this.points.shift();
|
||||
this.normals.shift();
|
||||
this.strokeWidths.shift();
|
||||
}
|
||||
|
||||
this.points.push(localPoint);
|
||||
var normal = computeNormal(newTrailPos, Camera.getPosition());
|
||||
|
||||
this.normals.push(normal);
|
||||
this.strokeWidths.push(STROKE_WIDTH + Math.random() * 0.01);
|
||||
Entities.editEntity(this.trail, {
|
||||
linePoints: this.points,
|
||||
normals: this.normals,
|
||||
strokeWidths: this.strokeWidths,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.updateControllerState = function() {
|
||||
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
|
||||
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
|
||||
this.triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
Entities.deleteEntity(this.trail);
|
||||
Entities.deleteEntity(this.light);
|
||||
Script.clearInterval(this.trailEraseInterval);
|
||||
}
|
||||
}
|
||||
|
||||
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, Controller.findAction("RIGHT_HAND_CLICK"));
|
||||
var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK"));
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
95
examples/flowArts/raveStick/raveStick.js
Normal file
95
examples/flowArts/raveStick/raveStick.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// RaveStick.js
|
||||
// examples/flowArats/raveStick
|
||||
//
|
||||
// Created by Eric Levin on 12/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script creates a rave stick which makes pretty light trails as you paint
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/raveStick.fbx";
|
||||
var scriptURL = Script.resolvePath("raveStickEntityScript.js");
|
||||
RaveStick = function(spawnPosition) {
|
||||
var colorPalette = [{
|
||||
red: 0,
|
||||
green: 200,
|
||||
blue: 40
|
||||
}, {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 40
|
||||
}];
|
||||
var stick = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "raveStick",
|
||||
modelURL: modelURL,
|
||||
position: spawnPosition,
|
||||
shapeType: 'box',
|
||||
collisionsWillMove: true,
|
||||
script: scriptURL,
|
||||
dimensions: {
|
||||
x: 0.06,
|
||||
y: 0.06,
|
||||
z: 0.31
|
||||
},
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
rightRelativePosition: {
|
||||
x: 0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
leftRelativePosition: {
|
||||
x: -0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
name: "raveLight",
|
||||
parentID: stick,
|
||||
dimensions: {
|
||||
x: 30,
|
||||
y: 30,
|
||||
z: 30
|
||||
},
|
||||
color: colorPalette[randInt(0, colorPalette.length)],
|
||||
intensity: 5
|
||||
});
|
||||
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0)
|
||||
var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
var forwardQuat = orientationOf(forwardVec);
|
||||
var position = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(rotation), 0.1));
|
||||
position.z += 0.1;
|
||||
position.x += -0.035;
|
||||
var color = {
|
||||
red: 0,
|
||||
green: 200,
|
||||
blue: 40
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(stick);
|
||||
Entities.deleteEntity(light);
|
||||
}
|
||||
|
||||
this.cleanup = cleanup;
|
||||
}
|
141
examples/flowArts/raveStick/raveStickEntityScript.js
Normal file
141
examples/flowArts/raveStick/raveStickEntityScript.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
// raveStickEntityScript.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by Eric Levin on 12/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script create light trails on a given object as it moves.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
var LIFETIME = 6000;
|
||||
var DRAWING_DEPTH = 0.8;
|
||||
var LINE_DIMENSIONS = 100;
|
||||
var MAX_POINTS_PER_LINE = 50;
|
||||
var MIN_POINT_DISTANCE = 0.02;
|
||||
var STROKE_WIDTH = 0.05
|
||||
var ugLSD = 35;
|
||||
var RaveStick = function() {
|
||||
_this = this;
|
||||
this.colorPalette = [{
|
||||
red: 0,
|
||||
green: 200,
|
||||
blue: 40
|
||||
}, {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 40
|
||||
}];
|
||||
var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png";
|
||||
this.trail = Entities.addEntity({
|
||||
type: "PolyLine",
|
||||
dimensions: {
|
||||
x: LINE_DIMENSIONS,
|
||||
y: LINE_DIMENSIONS,
|
||||
z: LINE_DIMENSIONS
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
textures: texture,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
this.points = [];
|
||||
this.normals = [];
|
||||
this.strokeWidths = [];
|
||||
};
|
||||
|
||||
RaveStick.prototype = {
|
||||
isGrabbed: false,
|
||||
|
||||
startNearGrab: function() {
|
||||
this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
Entities.editEntity(this.trail, {
|
||||
position: this.trailBasePosition
|
||||
});
|
||||
this.points = [];
|
||||
this.normals = [];
|
||||
this.strokeWidths = [];
|
||||
this.setupEraseInterval();
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
|
||||
var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
var forwardQuat = orientationOf(forwardVec);
|
||||
var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.04));
|
||||
var localPoint = Vec3.subtract(position, this.trailBasePosition);
|
||||
if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) {
|
||||
//Need a minimum distance to avoid binormal NANs
|
||||
return;
|
||||
}
|
||||
if (this.points.length === MAX_POINTS_PER_LINE) {
|
||||
this.points.shift();
|
||||
this.normals.shift();
|
||||
this.strokeWidths.shift();
|
||||
}
|
||||
|
||||
this.points.push(localPoint);
|
||||
var normal = Quat.getUp(props.rotation);
|
||||
this.normals.push(normal);
|
||||
this.strokeWidths.push(STROKE_WIDTH);
|
||||
Entities.editEntity(this.trail, {
|
||||
linePoints: this.points,
|
||||
normals: this.normals,
|
||||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
|
||||
setupEraseInterval: function() {
|
||||
this.trailEraseInterval = Script.setInterval(function() {
|
||||
if (_this.points.length > 0) {
|
||||
_this.points.shift();
|
||||
_this.normals.shift();
|
||||
_this.strokeWidths.shift();
|
||||
Entities.editEntity(_this.trail, {
|
||||
linePoints: _this.points,
|
||||
strokeWidths: _this.strokeWidths,
|
||||
normals: _this.normals
|
||||
});
|
||||
}
|
||||
}, ugLSD);
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
if(!this.trailEraseInterval) {
|
||||
return;
|
||||
}
|
||||
Script.setTimeout(function() {
|
||||
Script.clearInterval(_this.trailEraseInterval);
|
||||
_this.trailEraseInterval = null;
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
Entities.deleteEntity(this.beam);
|
||||
Entities.deleteEntity(this.trail);
|
||||
if (this.trailEraseInterval) {
|
||||
Script.clearInterval(this.trailEraseInterval);
|
||||
}
|
||||
}
|
||||
};
|
||||
return new RaveStick();
|
||||
|
||||
function computeNormal(p1, p2) {
|
||||
return Vec3.normalize(Vec3.subtract(p2, p1));
|
||||
}
|
||||
});
|
59
examples/html/eventBridgeLoader.js
Normal file
59
examples/html/eventBridgeLoader.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
//public slots:
|
||||
// void emitWebEvent(const QString& data);
|
||||
// void emitScriptEvent(const QString& data);
|
||||
//
|
||||
//signals:
|
||||
// void webEventReceived(const QString& data);
|
||||
// void scriptEventReceived(const QString& data);
|
||||
//
|
||||
|
||||
EventBridgeConnectionProxy = function(parent) {
|
||||
this.parent = parent;
|
||||
this.realSignal = this.parent.realBridge.scriptEventReceived
|
||||
this.webWindowId = this.parent.webWindow.windowId;
|
||||
}
|
||||
|
||||
EventBridgeConnectionProxy.prototype.connect = function(callback) {
|
||||
var that = this;
|
||||
this.realSignal.connect(function(id, message) {
|
||||
if (id === that.webWindowId) { callback(message); }
|
||||
});
|
||||
}
|
||||
|
||||
EventBridgeProxy = function(webWindow) {
|
||||
this.webWindow = webWindow;
|
||||
this.realBridge = this.webWindow.eventBridge;
|
||||
this.scriptEventReceived = new EventBridgeConnectionProxy(this);
|
||||
}
|
||||
|
||||
EventBridgeProxy.prototype.emitWebEvent = function(data) {
|
||||
this.realBridge.emitWebEvent(data);
|
||||
}
|
||||
|
||||
openEventBridge = function(callback) {
|
||||
EVENT_BRIDGE_URI = "ws://localhost:51016";
|
||||
socket = new WebSocket(this.EVENT_BRIDGE_URI);
|
||||
|
||||
socket.onclose = function() {
|
||||
console.error("web channel closed");
|
||||
};
|
||||
|
||||
socket.onerror = function(error) {
|
||||
console.error("web channel error: " + error);
|
||||
};
|
||||
|
||||
socket.onopen = function() {
|
||||
channel = new QWebChannel(socket, function(channel) {
|
||||
console.log("Document url is " + document.URL);
|
||||
for(var key in channel.objects){
|
||||
console.log("registered object: " + key);
|
||||
}
|
||||
var webWindow = channel.objects[document.URL.toLowerCase()];
|
||||
console.log("WebWindow is " + webWindow)
|
||||
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
||||
if (callback) { callback(eventBridgeProxy); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
31
examples/html/qmlWebTest.html
Normal file
31
examples/html/qmlWebTest.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Properties</title>
|
||||
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
|
||||
<script>
|
||||
var myBridge;
|
||||
|
||||
window.onload = function() {
|
||||
openEventBridge(function(eventBridge) {
|
||||
myBridge = eventBridge;
|
||||
myBridge.scriptEventReceived.connect(function(message) {
|
||||
console.log("HTML side received message: " + message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testClick = function() {
|
||||
myBridge.emitWebEvent("HTML side sending message - button click");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="properties">
|
||||
<button name="Test" title="Test" onclick="testClick()">Test</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
File diff suppressed because it is too large
Load diff
|
@ -53,7 +53,9 @@ LightOverlayManager = function() {
|
|||
if (visible != isVisible) {
|
||||
visible = isVisible;
|
||||
for (var id in entityOverlays) {
|
||||
Overlays.editOverlay(entityOverlays[id], { visible: visible });
|
||||
Overlays.editOverlay(entityOverlays[id], {
|
||||
visible: visible
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -61,8 +63,7 @@ LightOverlayManager = function() {
|
|||
// Allocate or get an unused overlay
|
||||
function getOverlay() {
|
||||
if (unusedOverlays.length == 0) {
|
||||
var overlay = Overlays.addOverlay("image3d", {
|
||||
});
|
||||
var overlay = Overlays.addOverlay("image3d", {});
|
||||
allOverlays.push(overlay);
|
||||
} else {
|
||||
var overlay = unusedOverlays.pop();
|
||||
|
@ -72,7 +73,9 @@ LightOverlayManager = function() {
|
|||
|
||||
function releaseOverlay(overlay) {
|
||||
unusedOverlays.push(overlay);
|
||||
Overlays.editOverlay(overlay, { visible: false });
|
||||
Overlays.editOverlay(overlay, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
function addEntity(entityID) {
|
||||
|
@ -88,7 +91,11 @@ LightOverlayManager = function() {
|
|||
visible: visible,
|
||||
alpha: 0.9,
|
||||
scale: 0.5,
|
||||
color: { red: 255, green: 255, blue: 255 }
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -123,4 +130,4 @@ LightOverlayManager = function() {
|
|||
Overlays.deleteOverlay(allOverlays[i]);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
|
@ -271,3 +271,29 @@ hexToRgb = function(hex) {
|
|||
} : null;
|
||||
}
|
||||
|
||||
calculateHandSizeRatio = function() {
|
||||
// Get the ratio of the current avatar's hand to Owen's hand
|
||||
|
||||
var standardCenterHandPoint = 0.11288;
|
||||
var jointNames = MyAvatar.getJointNames();
|
||||
//get distance from handJoint up to leftHandIndex3 as a proxy for center of hand
|
||||
var wristToFingertipDistance = 0;;
|
||||
for (var i = 0; i < jointNames.length; i++) {
|
||||
var jointName = jointNames[i];
|
||||
print(jointName)
|
||||
if (jointName.indexOf("LeftHandIndex") !== -1) {
|
||||
// translations are relative to parent joint, so simply add them together
|
||||
// joints face down the y-axis
|
||||
var translation = MyAvatar.getDefaultJointTranslation(i).y;
|
||||
wristToFingertipDistance += translation;
|
||||
}
|
||||
}
|
||||
// Right now units are in cm, so convert to meters
|
||||
wristToFingertipDistance /= 100;
|
||||
|
||||
var centerHandPoint = wristToFingertipDistance/2;
|
||||
|
||||
// Compare against standard hand (Owen)
|
||||
var handSizeRatio = centerHandPoint/standardCenterHandPoint;
|
||||
return handSizeRatio;
|
||||
}
|
||||
|
|
29
examples/light_modifier/README.md
Normal file
29
examples/light_modifier/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
This PR demonstrates one way in-world editing of objects might work.
|
||||
|
||||
Running this script will show light overlay icons in-world. Enter edit mode by running your distance beam through a light overlay. Exit using the red X.
|
||||
|
||||
When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached.
|
||||
|
||||
To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js
|
||||
To reset, I recommend stopping all scripts then re-loading lightLoader.js
|
||||
|
||||
When you run the lightLoader.js script, several scripts will be loaded:
|
||||
- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end, will support blacklisting of entities for raypicking during search for objects)
|
||||
- lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties)
|
||||
- lightModifierTestScene.js (creates a light)
|
||||
- slider.js (attached to each slider entity)
|
||||
- lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent)
|
||||
- visiblePanel.js (the transparent panel)
|
||||
- closeButton.js (for closing the ui)
|
||||
- ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world)
|
||||
- ../libraries/entitySelectionTool.js (visualizes volume of the lights)
|
||||
|
||||
Current sliders are (top to bottom):
|
||||
red
|
||||
green
|
||||
blue
|
||||
intensity
|
||||
cutoff
|
||||
exponent
|
||||
|
||||

|
36
examples/light_modifier/closeButton.js
Normal file
36
examples/light_modifier/closeButton.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// closeButton.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Entity script that closes sliders when interacted with.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
function CloseButton() {
|
||||
return this;
|
||||
}
|
||||
|
||||
CloseButton.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
|
||||
this.initialProperties = entityProperties
|
||||
this.userData = JSON.parse(entityProperties.userData);
|
||||
},
|
||||
startNearGrab: function() {
|
||||
|
||||
},
|
||||
startFarTrigger: function() {
|
||||
Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup')
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return new CloseButton();
|
||||
});
|
20
examples/light_modifier/lightLoader.js
Normal file
20
examples/light_modifier/lightLoader.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// lightLoader.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Loads a test scene showing sliders that you can grab and move to change entity properties.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var grabScript = Script.resolvePath('../controllers/handControllerGrab.js?' + Math.random(0 - 100));
|
||||
Script.load(grabScript);
|
||||
var lightModifier = Script.resolvePath('lightModifier.js?' + Math.random(0 - 100));
|
||||
Script.load(lightModifier);
|
||||
Script.setTimeout(function() {
|
||||
var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js?' + Math.random(0 - 100));
|
||||
Script.load(lightModifierTestScene);
|
||||
}, 750)
|
876
examples/light_modifier/lightModifier.js
Normal file
876
examples/light_modifier/lightModifier.js
Normal file
|
@ -0,0 +1,876 @@
|
|||
//
|
||||
// lightModifier.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
//some experimental options
|
||||
var ONLY_I_CAN_EDIT = false;
|
||||
var SLIDERS_SHOULD_STAY_WITH_AVATAR = false;
|
||||
var VERTICAL_SLIDERS = false;
|
||||
var SHOW_OVERLAYS = true;
|
||||
var SHOW_LIGHT_VOLUME = true;
|
||||
var USE_PARENTED_PANEL = true;
|
||||
var VISIBLE_PANEL = true;
|
||||
var USE_LABELS = true;
|
||||
var LEFT_LABELS = false;
|
||||
var RIGHT_LABELS = true;
|
||||
var ROTATE_CLOSE_BUTTON = false;
|
||||
|
||||
//variables for managing overlays
|
||||
var selectionDisplay;
|
||||
var selectionManager;
|
||||
var lightOverlayManager;
|
||||
|
||||
//for when we make a 3d model of a light a parent for the light
|
||||
var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100));
|
||||
|
||||
if (SHOW_OVERLAYS === true) {
|
||||
|
||||
Script.include('../libraries/gridTool.js');
|
||||
Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100));
|
||||
Script.include('../libraries/lightOverlayManager.js');
|
||||
|
||||
var grid = Grid();
|
||||
gridTool = GridTool({
|
||||
horizontalGrid: grid
|
||||
});
|
||||
gridTool.setVisible(false);
|
||||
|
||||
selectionDisplay = SelectionDisplay;
|
||||
selectionManager = SelectionManager;
|
||||
lightOverlayManager = new LightOverlayManager();
|
||||
selectionManager.addEventListener(function() {
|
||||
selectionDisplay.updateHandles();
|
||||
lightOverlayManager.updatePositions();
|
||||
});
|
||||
lightOverlayManager.setVisible(true);
|
||||
}
|
||||
|
||||
var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}'
|
||||
|
||||
var AXIS_SCALE = 1;
|
||||
var COLOR_MAX = 255;
|
||||
var INTENSITY_MAX = 0.05;
|
||||
var CUTOFF_MAX = 360;
|
||||
var EXPONENT_MAX = 1;
|
||||
|
||||
var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100));
|
||||
var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx';
|
||||
var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx';
|
||||
var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100));
|
||||
var TRANSPARENT_PANEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/transparent_box_alpha_15.fbx';
|
||||
var VISIBLE_PANEL_SCRIPT_URL = Script.resolvePath('visiblePanel.js?' + Math.random(0, 100));
|
||||
|
||||
var RED = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var GREEN = {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var BLUE = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
var PURPLE = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
var WHITE = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
var ORANGE = {
|
||||
red: 255,
|
||||
green: 165,
|
||||
blue: 0
|
||||
}
|
||||
|
||||
var SLIDER_DIMENSIONS = {
|
||||
x: 0.075,
|
||||
y: 0.075,
|
||||
z: 0.075
|
||||
};
|
||||
|
||||
var CLOSE_BUTTON_DIMENSIONS = {
|
||||
x: 0.1,
|
||||
y: 0.025,
|
||||
z: 0.1
|
||||
}
|
||||
|
||||
var LIGHT_MODEL_DIMENSIONS = {
|
||||
x: 0.58,
|
||||
y: 1.21,
|
||||
z: 0.57
|
||||
}
|
||||
|
||||
var PER_ROW_OFFSET = {
|
||||
x: 0,
|
||||
y: -0.2,
|
||||
z: 0
|
||||
};
|
||||
var sliders = [];
|
||||
var slidersRef = {
|
||||
'color_red': null,
|
||||
'color_green': null,
|
||||
'color_blue': null,
|
||||
intensity: null,
|
||||
cutoff: null,
|
||||
exponent: null
|
||||
};
|
||||
var light = null;
|
||||
|
||||
var basePosition;
|
||||
var avatarRotation;
|
||||
|
||||
function entitySlider(light, color, sliderType, displayText, row) {
|
||||
this.light = light;
|
||||
this.lightID = light.id.replace(/[{}]/g, "");
|
||||
this.initialProperties = light.initialProperties;
|
||||
this.color = color;
|
||||
this.sliderType = sliderType;
|
||||
this.displayText = displayText;
|
||||
this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET);
|
||||
this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0);
|
||||
this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot)));
|
||||
this.basePosition.y += 1;
|
||||
basePosition = this.basePosition;
|
||||
avatarRot = this.avatarRot;
|
||||
|
||||
var message = {
|
||||
lightID: this.lightID,
|
||||
sliderType: this.sliderType,
|
||||
sliderValue: null
|
||||
};
|
||||
|
||||
if (this.sliderType === 'color_red') {
|
||||
message.sliderValue = this.initialProperties.color.red
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
if (this.sliderType === 'color_green') {
|
||||
message.sliderValue = this.initialProperties.color.green
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
if (this.sliderType === 'color_blue') {
|
||||
message.sliderValue = this.initialProperties.color.blue
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
|
||||
if (this.sliderType === 'intensity') {
|
||||
message.sliderValue = this.initialProperties.intensity
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
|
||||
if (this.sliderType === 'exponent') {
|
||||
message.sliderValue = this.initialProperties.exponent
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
|
||||
if (this.sliderType === 'cutoff') {
|
||||
message.sliderValue = this.initialProperties.cutoff
|
||||
this.setValueFromMessage(message);
|
||||
}
|
||||
|
||||
this.setInitialSliderPositions();
|
||||
this.createAxis();
|
||||
this.createSliderIndicator();
|
||||
if (USE_LABELS === true) {
|
||||
this.createLabel()
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
//what's the ux for adjusting values? start with simple entities, try image overlays etc
|
||||
entitySlider.prototype = {
|
||||
createAxis: function() {
|
||||
//start of line
|
||||
var position;
|
||||
var extension;
|
||||
|
||||
if (VERTICAL_SLIDERS == true) {
|
||||
position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot)))));
|
||||
//line starts on bottom and goes up
|
||||
var upVector = Quat.getUp(this.avatarRot);
|
||||
extension = Vec3.multiply(AXIS_SCALE, upVector);
|
||||
} else {
|
||||
position = Vec3.sum(this.basePosition, this.verticalOffset);
|
||||
//line starts on left and goes to right
|
||||
//set the end of the line to the right
|
||||
var rightVector = Quat.getRight(this.avatarRot);
|
||||
extension = Vec3.multiply(AXIS_SCALE, rightVector);
|
||||
}
|
||||
|
||||
|
||||
this.axisStart = position;
|
||||
this.endOfAxis = Vec3.sum(position, extension);
|
||||
this.createEndOfAxisEntity();
|
||||
|
||||
var properties = {
|
||||
type: 'Line',
|
||||
name: 'Hifi-Slider-Axis::' + this.sliderType,
|
||||
color: this.color,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 3,
|
||||
z: 3
|
||||
},
|
||||
position: position,
|
||||
linePoints: [{
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}, extension],
|
||||
lineWidth: 5,
|
||||
};
|
||||
|
||||
this.axis = Entities.addEntity(properties);
|
||||
},
|
||||
createEndOfAxisEntity: function() {
|
||||
//we use this to track the end of the axis while parented to a panel
|
||||
var properties = {
|
||||
name: 'Hifi-End-Of-Axis',
|
||||
type: 'Box',
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
dimensions: {
|
||||
x: 0.01,
|
||||
y: 0.01,
|
||||
z: 0.01
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
position: this.endOfAxis,
|
||||
parentID: this.axis,
|
||||
visible: false
|
||||
}
|
||||
|
||||
this.endOfAxisEntity = Entities.addEntity(this.endOfAxis);
|
||||
},
|
||||
createLabel: function() {
|
||||
|
||||
var LABEL_WIDTH = 0.25
|
||||
var PER_LETTER_SPACING = 0.1;
|
||||
var textWidth = this.displayText.length * PER_LETTER_SPACING;
|
||||
|
||||
var position;
|
||||
if (LEFT_LABELS === true) {
|
||||
var leftVector = Vec3.multiply(-1, Quat.getRight(this.avatarRot));
|
||||
|
||||
var extension = Vec3.multiply(textWidth, leftVector);
|
||||
|
||||
position = Vec3.sum(this.axisStart, extension);
|
||||
}
|
||||
|
||||
if (RIGHT_LABELS === true) {
|
||||
var rightVector = Quat.getRight(this.avatarRot);
|
||||
|
||||
var extension = Vec3.multiply(textWidth / 1.75, rightVector);
|
||||
|
||||
position = Vec3.sum(this.endOfAxis, extension);
|
||||
}
|
||||
|
||||
|
||||
var labelProperties = {
|
||||
name: 'Hifi-Slider-Label-' + this.sliderType,
|
||||
type: 'Text',
|
||||
dimensions: {
|
||||
x: textWidth,
|
||||
y: 0.2,
|
||||
z: 0.1
|
||||
},
|
||||
textColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
text: this.displayText,
|
||||
lineHeight: 0.14,
|
||||
backgroundColor: {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
position: position,
|
||||
rotation: this.avatarRot,
|
||||
}
|
||||
print('BEFORE CREATE LABEL' + JSON.stringify(labelProperties))
|
||||
this.label = Entities.addEntity(labelProperties);
|
||||
print('AFTER CREATE LABEL')
|
||||
},
|
||||
createSliderIndicator: function() {
|
||||
var extensionVector;
|
||||
var position;
|
||||
if (VERTICAL_SLIDERS == true) {
|
||||
position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot)))));
|
||||
extensionVector = Quat.getUp(this.avatarRot);
|
||||
|
||||
} else {
|
||||
position = Vec3.sum(this.basePosition, this.verticalOffset);
|
||||
extensionVector = Quat.getRight(this.avatarRot);
|
||||
|
||||
}
|
||||
|
||||
var initialDistance;
|
||||
if (this.sliderType === 'color_red') {
|
||||
initialDistance = this.distanceRed;
|
||||
}
|
||||
if (this.sliderType === 'color_green') {
|
||||
initialDistance = this.distanceGreen;
|
||||
}
|
||||
if (this.sliderType === 'color_blue') {
|
||||
initialDistance = this.distanceBlue;
|
||||
}
|
||||
if (this.sliderType === 'intensity') {
|
||||
initialDistance = this.distanceIntensity;
|
||||
}
|
||||
if (this.sliderType === 'cutoff') {
|
||||
initialDistance = this.distanceCutoff;
|
||||
}
|
||||
if (this.sliderType === 'exponent') {
|
||||
initialDistance = this.distanceExponent;
|
||||
}
|
||||
|
||||
var extension = Vec3.multiply(initialDistance, extensionVector);
|
||||
var sliderPosition = Vec3.sum(position, extension);
|
||||
|
||||
var properties = {
|
||||
type: 'Sphere',
|
||||
name: 'Hifi-Slider-' + this.sliderType,
|
||||
dimensions: SLIDER_DIMENSIONS,
|
||||
collisionsWillMove: true,
|
||||
color: this.color,
|
||||
position: sliderPosition,
|
||||
script: SLIDER_SCRIPT_URL,
|
||||
ignoreForCollisions: true,
|
||||
userData: JSON.stringify({
|
||||
lightModifierKey: {
|
||||
lightID: this.lightID,
|
||||
sliderType: this.sliderType,
|
||||
axisStart: position,
|
||||
axisEnd: this.endOfAxis,
|
||||
},
|
||||
handControllerKey: {
|
||||
disableReleaseVelocity: true,
|
||||
disableMoveWithHead: true,
|
||||
disableNearGrab:true
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
this.sliderIndicator = Entities.addEntity(properties);
|
||||
},
|
||||
setValueFromMessage: function(message) {
|
||||
|
||||
//message is not for our light
|
||||
if (message.lightID !== this.lightID) {
|
||||
// print('not our light')
|
||||
return;
|
||||
}
|
||||
|
||||
//message is not our type
|
||||
if (message.sliderType !== this.sliderType) {
|
||||
// print('not our slider type')
|
||||
return
|
||||
}
|
||||
|
||||
var lightProperties = Entities.getEntityProperties(this.lightID);
|
||||
|
||||
if (this.sliderType === 'color_red') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
color: {
|
||||
red: message.sliderValue,
|
||||
green: lightProperties.color.green,
|
||||
blue: lightProperties.color.blue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.sliderType === 'color_green') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
color: {
|
||||
red: lightProperties.color.red,
|
||||
green: message.sliderValue,
|
||||
blue: lightProperties.color.blue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.sliderType === 'color_blue') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
color: {
|
||||
red: lightProperties.color.red,
|
||||
green: lightProperties.color.green,
|
||||
blue: message.sliderValue,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.sliderType === 'intensity') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
intensity: message.sliderValue
|
||||
});
|
||||
}
|
||||
|
||||
if (this.sliderType === 'cutoff') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
cutoff: message.sliderValue
|
||||
});
|
||||
}
|
||||
|
||||
if (this.sliderType === 'exponent') {
|
||||
Entities.editEntity(this.lightID, {
|
||||
exponent: message.sliderValue
|
||||
});
|
||||
}
|
||||
},
|
||||
setInitialSliderPositions: function() {
|
||||
this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE;
|
||||
this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE;
|
||||
this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE;
|
||||
this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE;
|
||||
this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE;
|
||||
this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
var panel;
|
||||
var visiblePanel;
|
||||
|
||||
function makeSliders(light) {
|
||||
|
||||
if (USE_PARENTED_PANEL === true) {
|
||||
panel = createPanelEntity(MyAvatar.position);
|
||||
}
|
||||
|
||||
if (light.type === 'spotlight') {
|
||||
var USE_COLOR_SLIDER = true;
|
||||
var USE_INTENSITY_SLIDER = true;
|
||||
var USE_CUTOFF_SLIDER = true;
|
||||
var USE_EXPONENT_SLIDER = true;
|
||||
}
|
||||
if (light.type === 'pointlight') {
|
||||
var USE_COLOR_SLIDER = true;
|
||||
var USE_INTENSITY_SLIDER = true;
|
||||
var USE_CUTOFF_SLIDER = false;
|
||||
var USE_EXPONENT_SLIDER = false;
|
||||
}
|
||||
if (USE_COLOR_SLIDER === true) {
|
||||
slidersRef.color_red = new entitySlider(light, RED, 'color_red', 'Red', 1);
|
||||
slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 'Green', 2);
|
||||
slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 'Blue', 3);
|
||||
|
||||
sliders.push(slidersRef.color_red);
|
||||
sliders.push(slidersRef.color_green);
|
||||
sliders.push(slidersRef.color_blue);
|
||||
|
||||
}
|
||||
if (USE_INTENSITY_SLIDER === true) {
|
||||
slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 'Intensity', 4);
|
||||
sliders.push(slidersRef.intensity);
|
||||
}
|
||||
if (USE_CUTOFF_SLIDER === true) {
|
||||
slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 'Cutoff', 5);
|
||||
sliders.push(slidersRef.cutoff);
|
||||
}
|
||||
if (USE_EXPONENT_SLIDER === true) {
|
||||
slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 'Exponent', 6);
|
||||
sliders.push(slidersRef.exponent);
|
||||
}
|
||||
|
||||
createCloseButton(slidersRef.color_red.axisStart);
|
||||
|
||||
subscribeToSliderMessages();
|
||||
|
||||
if (USE_PARENTED_PANEL === true) {
|
||||
parentEntitiesToPanel(panel);
|
||||
}
|
||||
|
||||
if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) {
|
||||
parentPanelToAvatar(panel);
|
||||
}
|
||||
|
||||
if (VISIBLE_PANEL === true) {
|
||||
visiblePanel = createVisiblePanel();
|
||||
}
|
||||
};
|
||||
|
||||
function parentPanelToAvatar(panel) {
|
||||
//this is going to need some more work re: the sliders actually being grabbable. probably something to do with updating axis movement
|
||||
Entities.editEntity(panel, {
|
||||
parentID: MyAvatar.sessionUUID,
|
||||
//actually figure out which one to parent it to -- probably a spine or something.
|
||||
parentJointIndex: 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function parentEntitiesToPanel(panel) {
|
||||
|
||||
sliders.forEach(function(slider) {
|
||||
Entities.editEntity(slider.axis, {
|
||||
parentID: panel
|
||||
})
|
||||
Entities.editEntity(slider.sliderIndicator, {
|
||||
parentID: panel
|
||||
})
|
||||
})
|
||||
|
||||
closeButtons.forEach(function(button) {
|
||||
Entities.editEntity(button, {
|
||||
parentID: panel
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createPanelEntity(position) {
|
||||
print('CREATING PANEL at ' + JSON.stringify(position));
|
||||
var panelProperties = {
|
||||
name: 'Hifi-Slider-Panel',
|
||||
type: 'Box',
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1
|
||||
},
|
||||
visible: false,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true
|
||||
}
|
||||
|
||||
var panel = Entities.addEntity(panelProperties);
|
||||
return panel
|
||||
}
|
||||
|
||||
function createVisiblePanel() {
|
||||
var totalOffset = -PER_ROW_OFFSET.y * sliders.length;
|
||||
|
||||
var moveRight = Vec3.sum(basePosition, Vec3.multiply(AXIS_SCALE / 2, Quat.getRight(avatarRot)));
|
||||
|
||||
var moveDown = Vec3.sum(moveRight, Vec3.multiply((sliders.length + 1) / 2, PER_ROW_OFFSET))
|
||||
var panelProperties = {
|
||||
name: 'Hifi-Visible-Transparent-Panel',
|
||||
type: 'Model',
|
||||
modelURL: TRANSPARENT_PANEL_URL,
|
||||
dimensions: {
|
||||
x: AXIS_SCALE + 0.1,
|
||||
y: totalOffset,
|
||||
z: SLIDER_DIMENSIONS.z / 4
|
||||
},
|
||||
visible: true,
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
position: moveDown,
|
||||
rotation: avatarRot,
|
||||
script: VISIBLE_PANEL_SCRIPT_URL
|
||||
}
|
||||
|
||||
var panel = Entities.addEntity(panelProperties);
|
||||
|
||||
return panel
|
||||
}
|
||||
|
||||
|
||||
function createLightModel(position, rotation) {
|
||||
var blockProperties = {
|
||||
name: 'Hifi-Spotlight-Model',
|
||||
type: 'Model',
|
||||
shapeType: 'box',
|
||||
modelURL: LIGHT_MODEL_URL,
|
||||
dimensions: LIGHT_MODEL_DIMENSIONS,
|
||||
collisionsWillMove: true,
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
script: PARENT_SCRIPT_URL,
|
||||
userData: JSON.stringify({
|
||||
handControllerKey: {
|
||||
disableReleaseVelocity: true
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
var block = Entities.addEntity(blockProperties);
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
var closeButtons = [];
|
||||
|
||||
function createCloseButton(axisStart) {
|
||||
var MARGIN = 0.10;
|
||||
var VERTICAL_OFFFSET = {
|
||||
x: 0,
|
||||
y: 0.15,
|
||||
z: 0
|
||||
};
|
||||
var leftVector = Vec3.multiply(-1, Quat.getRight(avatarRot));
|
||||
var extension = Vec3.multiply(MARGIN, leftVector);
|
||||
var position = Vec3.sum(axisStart, extension);
|
||||
|
||||
var buttonProperties = {
|
||||
name: 'Hifi-Close-Button',
|
||||
type: 'Model',
|
||||
modelURL: CLOSE_BUTTON_MODEL_URL,
|
||||
dimensions: CLOSE_BUTTON_DIMENSIONS,
|
||||
position: Vec3.sum(position, VERTICAL_OFFFSET),
|
||||
rotation: Quat.multiply(avatarRot, Quat.fromPitchYawRollDegrees(90, 0, 45)),
|
||||
//rotation: Quat.fromPitchYawRollDegrees(0, 0, 90),
|
||||
collisionsWillMove: false,
|
||||
ignoreForCollisions: true,
|
||||
script: CLOSE_BUTTON_SCRIPT_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
wantsTrigger: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var button = Entities.addEntity(buttonProperties);
|
||||
|
||||
closeButtons.push(button);
|
||||
|
||||
if (ROTATE_CLOSE_BUTTON === true) {
|
||||
Script.update.connect(rotateCloseButtons);
|
||||
}
|
||||
}
|
||||
|
||||
function rotateCloseButtons() {
|
||||
closeButtons.forEach(function(button) {
|
||||
Entities.editEntity(button, {
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function subScribeToNewLights() {
|
||||
Messages.subscribe('Hifi-Light-Mod-Receiver');
|
||||
Messages.messageReceived.connect(handleLightModMessages);
|
||||
}
|
||||
|
||||
function subscribeToSliderMessages() {
|
||||
Messages.subscribe('Hifi-Slider-Value-Reciever');
|
||||
Messages.messageReceived.connect(handleValueMessages);
|
||||
}
|
||||
|
||||
function subscribeToLightOverlayRayCheckMessages() {
|
||||
Messages.subscribe('Hifi-Light-Overlay-Ray-Check');
|
||||
Messages.messageReceived.connect(handleLightOverlayRayCheckMessages);
|
||||
}
|
||||
|
||||
function subscribeToCleanupMessages() {
|
||||
Messages.subscribe('Hifi-Light-Modifier-Cleanup');
|
||||
Messages.messageReceived.connect(handleCleanupMessages);
|
||||
}
|
||||
|
||||
|
||||
function handleLightModMessages(channel, message, sender) {
|
||||
if (channel !== 'Hifi-Light-Mod-Receiver') {
|
||||
return;
|
||||
}
|
||||
if (sender !== MyAvatar.sessionUUID) {
|
||||
return;
|
||||
}
|
||||
var parsedMessage = JSON.parse(message);
|
||||
|
||||
makeSliders(parsedMessage.light);
|
||||
light = parsedMessage.light.id
|
||||
if (SHOW_LIGHT_VOLUME === true) {
|
||||
selectionManager.setSelections([parsedMessage.light.id]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleValueMessages(channel, message, sender) {
|
||||
|
||||
if (channel !== 'Hifi-Slider-Value-Reciever') {
|
||||
return;
|
||||
}
|
||||
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
|
||||
return;
|
||||
}
|
||||
var parsedMessage = JSON.parse(message);
|
||||
|
||||
slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage);
|
||||
}
|
||||
|
||||
var currentLight;
|
||||
var block;
|
||||
var oldParent = null;
|
||||
var hasParent = false;
|
||||
|
||||
function handleLightOverlayRayCheckMessages(channel, message, sender) {
|
||||
if (channel !== 'Hifi-Light-Overlay-Ray-Check') {
|
||||
return;
|
||||
}
|
||||
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = JSON.parse(message);
|
||||
|
||||
var doesIntersect = lightOverlayManager.findRayIntersection(pickRay);
|
||||
// print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects);
|
||||
if (doesIntersect.intersects === true) {
|
||||
// print('FULL MESSAGE:::' + JSON.stringify(doesIntersect))
|
||||
|
||||
var lightID = doesIntersect.entityID;
|
||||
if (currentLight === lightID) {
|
||||
// print('ALREADY HAVE A BLOCK, EXIT')
|
||||
return;
|
||||
}
|
||||
|
||||
currentLight = lightID;
|
||||
var lightProperties = Entities.getEntityProperties(lightID);
|
||||
if (lightProperties.parentID !== DEFAULT_PARENT_ID) {
|
||||
//this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end;
|
||||
oldParent = lightProperties.parentID;
|
||||
hasParent = true;
|
||||
block = lightProperties.parentID;
|
||||
if (lightProperties.parentJointIndex !== -1) {
|
||||
//should make sure to retain the parent too. but i don't actually know what the
|
||||
}
|
||||
} else {
|
||||
block = createLightModel(lightProperties.position, lightProperties.rotation);
|
||||
}
|
||||
|
||||
var light = {
|
||||
id: lightID,
|
||||
type: 'spotlight',
|
||||
initialProperties: lightProperties
|
||||
}
|
||||
|
||||
makeSliders(light);
|
||||
|
||||
if (SHOW_LIGHT_VOLUME === true) {
|
||||
selectionManager.setSelections([lightID]);
|
||||
}
|
||||
|
||||
Entities.editEntity(lightID, {
|
||||
parentID: block,
|
||||
parentJointIndex: -1
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function handleCleanupMessages(channel, message, sender) {
|
||||
|
||||
if (channel !== 'Hifi-Light-Modifier-Cleanup') {
|
||||
return;
|
||||
}
|
||||
if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) {
|
||||
return;
|
||||
}
|
||||
if (message === 'callCleanup') {
|
||||
cleanup(true);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSliderAxis() {
|
||||
sliders.forEach(function(slider) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function cleanup(fromMessage) {
|
||||
var i;
|
||||
for (i = 0; i < sliders.length; i++) {
|
||||
Entities.deleteEntity(sliders[i].axis);
|
||||
Entities.deleteEntity(sliders[i].sliderIndicator);
|
||||
Entities.deleteEntity(sliders[i].label);
|
||||
}
|
||||
|
||||
while (closeButtons.length > 0) {
|
||||
Entities.deleteEntity(closeButtons.pop());
|
||||
}
|
||||
|
||||
//if the light was already parented to something we will want to restore that. or come up with groups or something clever.
|
||||
if (oldParent !== null) {
|
||||
Entities.editEntity(currentLight, {
|
||||
parentID: oldParent,
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(currentLight, {
|
||||
parentID: null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (fromMessage !== true) {
|
||||
Messages.messageReceived.disconnect(handleLightModMessages);
|
||||
Messages.messageReceived.disconnect(handleValueMessages);
|
||||
Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages);
|
||||
lightOverlayManager.setVisible(false);
|
||||
}
|
||||
|
||||
|
||||
Entities.deleteEntity(panel);
|
||||
Entities.deleteEntity(visiblePanel);
|
||||
|
||||
selectionManager.clearSelections();
|
||||
|
||||
if (ROTATE_CLOSE_BUTTON === true) {
|
||||
Script.update.disconnect(rotateCloseButtons);
|
||||
}
|
||||
|
||||
if (hasParent === false) {
|
||||
Entities.deleteEntity(block);
|
||||
}
|
||||
|
||||
oldParent = null;
|
||||
hasParent = false;
|
||||
currentLight = null;
|
||||
sliders = [];
|
||||
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
lightOverlayManager.setVisible(false);
|
||||
})
|
||||
|
||||
|
||||
subscribeToLightOverlayRayCheckMessages();
|
||||
subScribeToNewLights();
|
||||
subscribeToCleanupMessages();
|
||||
|
||||
|
||||
|
||||
//other light properties
|
||||
// diffuseColor: { red: 255, green: 255, blue: 255 },
|
||||
// ambientColor: { red: 255, green: 255, blue: 255 },
|
||||
// specularColor: { red: 255, green: 255, blue: 255 },
|
||||
// constantAttenuation: 1,
|
||||
// linearAttenuation: 0,
|
||||
// quadraticAttenuation: 0,
|
||||
// exponent: 0,
|
||||
// cutoff: 180, // in degrees
|
73
examples/light_modifier/lightModifierTestScene.js
Normal file
73
examples/light_modifier/lightModifierTestScene.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// lightModifierTestScene.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Given a selected light, instantiate some entities that represent various values you can dynamically adjust.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100));
|
||||
var basePosition, avatarRot;
|
||||
avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0);
|
||||
basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot)));
|
||||
|
||||
var light;
|
||||
|
||||
function createLight() {
|
||||
var position = basePosition;
|
||||
position.y += 2;
|
||||
var lightTransform = evalLightWorldTransform(position, avatarRot);
|
||||
var lightProperties = {
|
||||
name: 'Hifi-Spotlight',
|
||||
type: "Light",
|
||||
isSpotlight: true,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 8
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 0.035,
|
||||
exponent: 1,
|
||||
cutoff: 30,
|
||||
lifetime: -1,
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q
|
||||
};
|
||||
|
||||
light = Entities.addEntity(lightProperties);
|
||||
|
||||
}
|
||||
|
||||
function evalLightWorldTransform(modelPos, modelRot) {
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.3,
|
||||
z: 0
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(light);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
createLight();
|
40
examples/light_modifier/lightParent.js
Normal file
40
examples/light_modifier/lightParent.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// lightParent.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Entity script that tells the light parent to update the selection tool when we move it.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
function LightParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
LightParent.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
|
||||
this.initialProperties = entityProperties
|
||||
this.userData = JSON.parse(entityProperties.userData);
|
||||
},
|
||||
startNearGrab: function() {},
|
||||
startDistantGrab: function() {
|
||||
|
||||
},
|
||||
continueNearGrab: function() {
|
||||
this.continueDistantGrab();
|
||||
},
|
||||
continueDistantGrab: function() {
|
||||
Messages.sendMessage('entityToolUpdates', 'callUpdate');
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
return new LightParent();
|
||||
});
|
105
examples/light_modifier/slider.js
Normal file
105
examples/light_modifier/slider.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// slider.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
var AXIS_SCALE = 1;
|
||||
var COLOR_MAX = 255;
|
||||
var INTENSITY_MAX = 0.05;
|
||||
var CUTOFF_MAX = 360;
|
||||
var EXPONENT_MAX = 1;
|
||||
|
||||
function Slider() {
|
||||
return this;
|
||||
}
|
||||
|
||||
Slider.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var entityProperties = Entities.getEntityProperties(this.entityID, "userData");
|
||||
var parsedUserData = JSON.parse(entityProperties.userData);
|
||||
this.userData = parsedUserData.lightModifierKey;
|
||||
},
|
||||
startNearGrab: function() {
|
||||
this.setInitialProperties();
|
||||
},
|
||||
startDistantGrab: function() {
|
||||
this.setInitialProperties();
|
||||
},
|
||||
setInitialProperties: function() {
|
||||
this.initialProperties = Entities.getEntityProperties(this.entityID);
|
||||
},
|
||||
continueNearGrab: function() {
|
||||
// this.continueDistantGrab();
|
||||
},
|
||||
continueDistantGrab: function() {
|
||||
this.setSliderValueBasedOnDistance();
|
||||
},
|
||||
setSliderValueBasedOnDistance: function() {
|
||||
var currentPosition = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
|
||||
var distance = Vec3.distance(this.userData.axisStart, currentPosition);
|
||||
|
||||
if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') {
|
||||
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, COLOR_MAX);
|
||||
}
|
||||
if (this.userData.sliderType === 'intensity') {
|
||||
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, INTENSITY_MAX);
|
||||
}
|
||||
if (this.userData.sliderType === 'cutoff') {
|
||||
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, CUTOFF_MAX);
|
||||
}
|
||||
if (this.userData.sliderType === 'exponent') {
|
||||
this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, EXPONENT_MAX);
|
||||
};
|
||||
|
||||
this.sendValueToSlider();
|
||||
},
|
||||
releaseGrab: function() {
|
||||
Entities.editEntity(this.entityID, {
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
})
|
||||
|
||||
this.sendValueToSlider();
|
||||
},
|
||||
scaleValueBasedOnDistanceFromStart: function(value, min2, max2) {
|
||||
var min1 = 0;
|
||||
var max1 = AXIS_SCALE;
|
||||
var min2 = min2;
|
||||
var max2 = max2;
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
},
|
||||
sendValueToSlider: function() {
|
||||
var _t = this;
|
||||
var message = {
|
||||
lightID: _t.userData.lightID,
|
||||
sliderType: _t.userData.sliderType,
|
||||
sliderValue: _t.sliderValue
|
||||
}
|
||||
Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message));
|
||||
if (_t.userData.sliderType === 'cutoff') {
|
||||
Messages.sendMessage('entityToolUpdates', 'callUpdate');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Slider();
|
||||
});
|
40
examples/light_modifier/visiblePanel.js
Normal file
40
examples/light_modifier/visiblePanel.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// visiblePanel.js
|
||||
//
|
||||
// Created by James Pollack @imgntn on 12/15/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Entity script that disables picking on this panel.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
function VisiblePanel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
VisiblePanel.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
var data = {
|
||||
action: 'add',
|
||||
id: this.entityID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data))
|
||||
},
|
||||
unload: function() {
|
||||
var data = {
|
||||
action: 'remove',
|
||||
id: this.entityID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data))
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return new VisiblePanel();
|
||||
});
|
|
@ -43,6 +43,8 @@ var MAX_STROKE_WIDTH = 0.04;
|
|||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png";
|
||||
|
||||
|
||||
|
||||
function MyController(hand, triggerAction) {
|
||||
|
@ -148,7 +150,8 @@ function MyController(hand, triggerAction) {
|
|||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
lifetime: 200
|
||||
lifetime: 200,
|
||||
textures: textureURL
|
||||
});
|
||||
this.strokePoints = [];
|
||||
this.strokeNormals = [];
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
var MIN_STROKE_WIDTH = 0.0005;
|
||||
var MAX_STROKE_WIDTH = 0.03;
|
||||
|
||||
var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png";
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
|
@ -168,6 +170,7 @@
|
|||
type: "PolyLine",
|
||||
name: "paintStroke",
|
||||
color: this.strokeColor,
|
||||
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png",
|
||||
dimensions: {
|
||||
x: 50,
|
||||
y: 50,
|
||||
|
|
|
@ -247,4 +247,4 @@ function cleanup() {
|
|||
|
||||
|
||||
// Uncomment this line to delete whiteboard and all associated entity on script close
|
||||
//Script.scriptEnding.connect(cleanup);
|
||||
// Script.scriptEnding.connect(cleanup);
|
||||
|
|
73
examples/rayPickingFilterExample.js
Normal file
73
examples/rayPickingFilterExample.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// rayPickingFilterExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on 12/24/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates the use of filtering entities for ray picking
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var whiteListBox = Entities.addEntity({
|
||||
type: "Box",
|
||||
color: {
|
||||
red: 10,
|
||||
green: 200,
|
||||
blue: 10
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.2,
|
||||
y: 0.2,
|
||||
z: 0.2
|
||||
},
|
||||
position: center
|
||||
});
|
||||
|
||||
var blackListBox = Entities.addEntity({
|
||||
type: "Box",
|
||||
color: {
|
||||
red: 100,
|
||||
green: 10,
|
||||
blue: 10
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.2,
|
||||
y: 0.2,
|
||||
z: 0.2
|
||||
},
|
||||
position: Vec3.sum(center, {
|
||||
x: 0,
|
||||
y: 0.3,
|
||||
z: 0
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function castRay(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
// In this example every entity will be pickable except the entities in the blacklist array
|
||||
// the third argument is the whitelist array,and the fourth and final is the blacklist array
|
||||
var pickResults = Entities.findRayIntersection(pickRay, true, [], [blackListBox]);
|
||||
|
||||
// With below example, only entities added to whitelist will be pickable
|
||||
// var pickResults = Entities.findRayIntersection(pickRay, true, [whiteListBox], []);
|
||||
|
||||
if (pickResults.intersects) {
|
||||
print("INTERSECTION!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(whiteListBox);
|
||||
Entities.deleteEntity(blackListBox);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(castRay);
|
32
examples/tests/qmlWebTest.js
Normal file
32
examples/tests/qmlWebTest.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
print("Launching web window");
|
||||
|
||||
webWindow = new OverlayWebWindow('Test Event Bridge', "file:///C:/Users/bdavis/Git/hifi/examples/html/qmlWebTest.html", 320, 240, false);
|
||||
print("JS Side window: " + webWindow);
|
||||
print("JS Side bridge: " + webWindow.eventBridge);
|
||||
webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||
print("JS Side event received: " + data);
|
||||
});
|
||||
|
||||
var titles = ["A", "B", "C"];
|
||||
var titleIndex = 0;
|
||||
|
||||
Script.setInterval(function() {
|
||||
webWindow.eventBridge.emitScriptEvent("JS Event sent");
|
||||
var size = webWindow.size;
|
||||
var position = webWindow.position;
|
||||
print("Window url: " + webWindow.url)
|
||||
print("Window visible: " + webWindow.visible)
|
||||
print("Window size: " + size.x + "x" + size.y)
|
||||
print("Window pos: " + position.x + "x" + position.y)
|
||||
webWindow.setVisible(!webWindow.visible);
|
||||
webWindow.setTitle(titles[titleIndex]);
|
||||
webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
||||
titleIndex += 1;
|
||||
titleIndex %= titles.length;
|
||||
}, 2 * 1000);
|
||||
|
||||
Script.setTimeout(function() {
|
||||
print("Closing script");
|
||||
webWindow.close();
|
||||
Script.stop();
|
||||
}, 15 * 1000)
|
|
@ -241,9 +241,9 @@
|
|||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
},
|
||||
creatorSessionUUID: MyAvatar.sessionUUID
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
var makeArrowStick = function(entityA, entityB, collision) {
|
||||
|
|
|
@ -38,6 +38,14 @@ var pingPongGun = Entities.addEntity({
|
|||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
relativePosition: {
|
||||
x: -0.05,
|
||||
y: 0,
|
||||
z: 0.0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
|
|
|
@ -384,10 +384,10 @@ var CHECK_MARK_COLOR = {
|
|||
y: newY
|
||||
});
|
||||
Overlays.editOverlay(this.checkMark, {
|
||||
y: newY
|
||||
y: newY + (0.25 * this.thumbSize)
|
||||
});
|
||||
Overlays.editOverlay(this.unCheckMark, {
|
||||
y: newY
|
||||
y: newY + (0.25 * this.thumbSize)
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -399,10 +399,10 @@ var CHECK_MARK_COLOR = {
|
|||
y: this.y
|
||||
});
|
||||
Overlays.editOverlay(this.checkMark, {
|
||||
y: this.y
|
||||
y: this.y + (0.25 * this.thumbSize)
|
||||
});
|
||||
Overlays.editOverlay(this.unCheckMark, {
|
||||
y: this.y
|
||||
y: this.y+ (0.25 * this.thumbSize)
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -814,12 +814,14 @@ var CHECK_MARK_COLOR = {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
CollapsablePanelItem.prototype.destroy = function() {
|
||||
Overlays.deleteOverlay(this.title);
|
||||
Overlays.deleteOverlay(this.thumb);
|
||||
};
|
||||
|
||||
CollapsablePanelItem.prototype.editTitle = function(opts) {
|
||||
Overlays.editOverlay(this.title, opts);
|
||||
};
|
||||
|
||||
CollapsablePanelItem.prototype.hide = function() {
|
||||
Overlays.editOverlay(this.title, {
|
||||
|
@ -1531,4 +1533,4 @@ Controller.captureKeyEvents({
|
|||
});
|
||||
Controller.captureKeyEvents({
|
||||
text: "right"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// SunLightExample.js
|
||||
// examples
|
||||
// renderEngineDebug.js
|
||||
// examples/utilities/tools
|
||||
//
|
||||
// Sam Gateau
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
@ -10,78 +11,93 @@
|
|||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var MENU = "Developer>Render>Debug Deferred Buffer";
|
||||
var ACTIONS = ["Off", "Diffuse", "Alpha", "Specular", "Roughness", "Normal", "Depth", "Lighting", "Custom"];
|
||||
var SETTINGS_KEY = "EngineDebugScript.DebugMode";
|
||||
|
||||
Number.prototype.clamp = function(min, max) {
|
||||
return Math.min(Math.max(this, min), max);
|
||||
};
|
||||
|
||||
var panel = new Panel(10, 100);
|
||||
|
||||
function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) {
|
||||
this.subPanel = panel.newSubPanel(name);
|
||||
function CounterWidget(parentPanel, name, counter) {
|
||||
var subPanel = parentPanel.newSubPanel(name);
|
||||
var widget = parentPanel.items[name];
|
||||
widget.editTitle({ width: 270 });
|
||||
|
||||
this.subPanel.newSlider("Num Feed", 0, 1,
|
||||
function(value) { },
|
||||
feedGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Num Drawn", 0, 1,
|
||||
function(value) { },
|
||||
drawGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Max Drawn", -1, 1,
|
||||
capSetter,
|
||||
capGetter,
|
||||
function(value) { return (value); });
|
||||
subPanel.newSlider('Max Drawn', -1, 1,
|
||||
function(value) { counter.maxDrawn = value; }, // setter
|
||||
function() { return counter.maxDrawn; }, // getter
|
||||
function(value) { return value; });
|
||||
|
||||
this.update = function () {
|
||||
var numFeed = this.subPanel.get("Num Feed");
|
||||
this.subPanel.set("Num Feed", numFeed);
|
||||
this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn"));
|
||||
var slider = subPanel.getWidget('Max Drawn');
|
||||
|
||||
var numMax = Math.max(numFeed, 1);
|
||||
this.subPanel.getWidget("Num Feed").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Num Drawn").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Max Drawn").setMaxValue(numMax);
|
||||
this.update = function () {
|
||||
var numDrawn = counter.numDrawn; // avoid double polling
|
||||
var numMax = Math.max(numDrawn, 1);
|
||||
var title = [
|
||||
' ' + name,
|
||||
numDrawn + ' / ' + counter.numFeed
|
||||
].join('\t');
|
||||
|
||||
widget.editTitle({ text: title });
|
||||
slider.setMaxValue(numMax);
|
||||
};
|
||||
};
|
||||
|
||||
var opaquesCounter = new CounterWidget(panel, "Opaques",
|
||||
function () { return Scene.getEngineNumFeedOpaqueItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOpaqueItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOpaqueItems(); }
|
||||
);
|
||||
var opaquesCounter = new CounterWidget(panel, "Opaques", Render.opaque);
|
||||
var transparentsCounter = new CounterWidget(panel, "Transparents", Render.transparent);
|
||||
var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D);
|
||||
|
||||
var transparentsCounter = new CounterWidget(panel, "Transparents",
|
||||
function () { return Scene.getEngineNumFeedTransparentItems(); },
|
||||
function () { return Scene.getEngineNumDrawnTransparentItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnTransparentItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnTransparentItems(); }
|
||||
);
|
||||
var resizing = false;
|
||||
var previousMode = Settings.getValue(SETTINGS_KEY, -1);
|
||||
Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]);
|
||||
Render.deferredDebugMode = previousMode;
|
||||
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size
|
||||
|
||||
var overlaysCounter = new CounterWidget(panel, "Overlays",
|
||||
function () { return Scene.getEngineNumFeedOverlay3DItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOverlay3DItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOverlay3DItems(); }
|
||||
);
|
||||
function setEngineDeferredDebugSize(eventX) {
|
||||
var scaledX = (2.0 * (eventX / Window.innerWidth) - 1.0).clamp(-1.0, 1.0);
|
||||
Render.deferredDebugSize = { x: scaledX, y: -1.0, z: 1.0, w: 1.0 };
|
||||
}
|
||||
function shouldStartResizing(eventX) {
|
||||
var x = Math.abs(eventX - Window.innerWidth * (1.0 + Render.deferredDebugSize.x) / 2.0);
|
||||
var mode = Render.deferredDebugMode;
|
||||
return mode !== -1 && x < 20;
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
var index = ACTIONS.indexOf(menuItem);
|
||||
if (index >= 0) {
|
||||
Render.deferredDebugMode = (index - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// see libraries/render/src/render/Engine.h
|
||||
var showDisplayStatusFlag = 1;
|
||||
var showNetworkStatusFlag = 2;
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value ?
|
||||
Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag :
|
||||
Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); },
|
||||
function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; },
|
||||
function(value) { Render.displayItemStatus = (value ?
|
||||
Render.displayItemStatus | showDisplayStatusFlag :
|
||||
Render.displayItemStatus & ~showDisplayStatusFlag); },
|
||||
function() { return (Render.displayItemStatus & showDisplayStatusFlag) > 0; },
|
||||
function(value) { return (value & showDisplayStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Network/Physics status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value ?
|
||||
Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag :
|
||||
Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); },
|
||||
function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; },
|
||||
function(value) { Render.displayItemStatus = (value ?
|
||||
Render.displayItemStatus | showNetworkStatusFlag :
|
||||
Render.displayItemStatus & ~showNetworkStatusFlag); },
|
||||
function() { return (Render.displayItemStatus & showNetworkStatusFlag) > 0; },
|
||||
function(value) { return (value & showNetworkStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newSlider("Tone Mapping Exposure", -10, 10,
|
||||
function (value) { Render.tone.exposure = value; },
|
||||
function() { return Render.tone.exposure; },
|
||||
function (value) { return (value); });
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
|
@ -91,11 +107,51 @@ function updateCounters() {
|
|||
}
|
||||
Script.setInterval(updateCounters, tickTackPeriod);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
function mouseMoveEvent(event) {
|
||||
if (resizing) {
|
||||
setEngineDeferredDebugSize(event.x);
|
||||
} else {
|
||||
panel.mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (shouldStartResizing(event.x)) {
|
||||
resizing = true;
|
||||
} else {
|
||||
panel.mousePressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
} else {
|
||||
panel.mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
panel.destroy();
|
||||
Menu.removeActionGroup(MENU);
|
||||
// Reset
|
||||
Settings.setValue(SETTINGS_KEY, Render.deferredDebugMode);
|
||||
Render.deferredDebugMode = -1;
|
||||
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 };
|
||||
Render.opaque.maxDrawn = -1;
|
||||
Render.transparent.maxDrawn = -1;
|
||||
Render.overlay3D.maxDrawn = -1;
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
// Collapse items
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Overlays"].y});
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Transparents"].y});
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Opaques"].y});
|
||||
|
|
73
examples/winterSmashUp/targetPractice/shooterPlatform.js
Normal file
73
examples/winterSmashUp/targetPractice/shooterPlatform.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// shooterPlatform.js
|
||||
// examples/winterSmashUp/targetPractice
|
||||
//
|
||||
// Created by Thijs Wenker on 12/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// The Winter Smash Up Target Practice Game using a bow.
|
||||
// This is the platform that spawns the bow and attaches it to the avatars hand,
|
||||
// then de-rez the bow on leaving to prevent walking up to the targets with the bow.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
var _this = this;
|
||||
|
||||
const GAME_CHANNEL = 'winterSmashUpGame';
|
||||
const SCRIPT_URL = Script.resolvePath('../../toybox/bow/bow.js');
|
||||
const MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow-deadly.fbx";
|
||||
const COLLISION_HULL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow_collision_hull.obj";
|
||||
const RIGHT_HAND = 1;
|
||||
const LEFT_HAND = 0;
|
||||
|
||||
var bowEntity = undefined;
|
||||
|
||||
_this.enterEntity = function(entityID) {
|
||||
print('entered bow game entity');
|
||||
|
||||
// Triggers a recording on an assignment client:
|
||||
Messages.sendMessage('PlayBackOnAssignment', 'BowShootingGameExplaination');
|
||||
|
||||
bowEntity = Entities.addEntity({
|
||||
name: 'Hifi-Bow-For-Game',
|
||||
type: 'Model',
|
||||
modelURL: MODEL_URL,
|
||||
position: MyAvatar.position,
|
||||
dimensions: {x: 0.04, y: 1.3, z: 0.21},
|
||||
collisionsWillMove: true,
|
||||
gravity: {x: 0, y: 0, z: 0},
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: SCRIPT_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true,
|
||||
spatialKey: {
|
||||
leftRelativePosition: {
|
||||
x: 0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
},
|
||||
rightRelativePosition: {
|
||||
x: -0.05,
|
||||
y: 0.06,
|
||||
z: -0.05
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
Messages.sendMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: bowEntity}));
|
||||
};
|
||||
|
||||
_this.leaveEntity = function(entityID) {
|
||||
if (bowEntity !== undefined) {
|
||||
Entities.deleteEntity(bowEntity);
|
||||
bowEntity = undefined;
|
||||
}
|
||||
};
|
||||
});
|
93
examples/winterSmashUp/targetPractice/startTargetPractice.js
Normal file
93
examples/winterSmashUp/targetPractice/startTargetPractice.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// startTargetPractice.js
|
||||
// examples/winterSmashUp/targetPractice
|
||||
//
|
||||
// Created by Thijs Wenker on 12/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// The Winter Smash Up Target Practice Game using a bow.
|
||||
// This script starts the game, when the entity that contains the script gets shot.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
var _this = this;
|
||||
var waitForScriptLoad = false;
|
||||
|
||||
const MAX_GAME_TIME = 60; //seconds
|
||||
const SCRIPT_URL = 'http://s3.amazonaws.com/hifi-public/scripts/winterSmashUp/targetPractice/targetPracticeGame.js';
|
||||
const GAME_CHANNEL = 'winterSmashUpGame';
|
||||
|
||||
var isScriptRunning = function(script) {
|
||||
script = script.toLowerCase().trim();
|
||||
var runningScripts = ScriptDiscoveryService.getRunning();
|
||||
for (i in runningScripts) {
|
||||
if (runningScripts[i].url.toLowerCase().trim() == script) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var sendStartSignal = function() {
|
||||
Messages.sendMessage(GAME_CHANNEL, JSON.stringify({
|
||||
action: 'start',
|
||||
gameEntityID: _this.entityID,
|
||||
playerSessionUUID: MyAvatar.sessionUUID
|
||||
}));
|
||||
}
|
||||
|
||||
var startGame = function() {
|
||||
//TODO: check here if someone is already playing this game instance by verifying the userData for playerSessionID
|
||||
// and startTime with a maximum timeout of X seconds (30?)
|
||||
|
||||
|
||||
if (!isScriptRunning(SCRIPT_URL)) {
|
||||
// Loads the script for the player if this isn't yet loaded
|
||||
Script.load(SCRIPT_URL);
|
||||
waitForScriptLoad = true;
|
||||
return;
|
||||
}
|
||||
sendStartSignal();
|
||||
};
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == GAME_CHANNEL) {
|
||||
var data = JSON.parse(message);
|
||||
switch (data.action) {
|
||||
case 'scriptLoaded':
|
||||
if (waitForPing) {
|
||||
sendStartSignal();
|
||||
waitForPing = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_this.preload = function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
};
|
||||
|
||||
_this.collisionWithEntity = function(entityA, entityB, collisionInfo) {
|
||||
if (entityA == _this.entityID) {
|
||||
try {
|
||||
var data = JSON.parse(Entities.getEntityProperties(entityB).userData);
|
||||
if (data.creatorSessionUUID === MyAvatar.sessionUUID) {
|
||||
print('attempting to startGame by collisionWithEntity.');
|
||||
startGame();
|
||||
}
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_this.onShot = function(forceDirection) {
|
||||
print('attempting to startGame by onShot.');
|
||||
startGame();
|
||||
};
|
||||
|
||||
Messages.subscribe(GAME_CHANNEL);
|
||||
});
|
227
examples/winterSmashUp/targetPractice/targetPracticeGame.js
Normal file
227
examples/winterSmashUp/targetPractice/targetPracticeGame.js
Normal file
|
@ -0,0 +1,227 @@
|
|||
//
|
||||
// targetPracticeGame.js
|
||||
// examples/winterSmashUp/targetPractice
|
||||
//
|
||||
// Created by Thijs Wenker on 12/21/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// The Winter Smash Up Target Practice Game using a bow.
|
||||
// This script runs on the client side (it is loaded through the platform trigger entity)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
const GAME_CHANNEL = 'winterSmashUpGame';
|
||||
const SCORE_POST_URL = 'https://script.google.com/macros/s/AKfycbwZAMx6cMBx6-8NGEhR8ELUA-dldtpa_4P55z38Q4vYHW6kneg/exec';
|
||||
const MODEL_URL = 'http://cdn.highfidelity.com/chris/production/winter/game/';
|
||||
const MAX_GAME_TIME = 120; //seconds
|
||||
const TARGET_CLOSE_OFFSET = 0.5;
|
||||
const MILLISECS_IN_SEC = 1000;
|
||||
const HIT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Clay_Pigeon_02.L.wav';
|
||||
const GRAVITY = -9.8;
|
||||
const MESSAGE_WIDTH = 100;
|
||||
const MESSAGE_HEIGHT = 50;
|
||||
|
||||
const TARGETS = [
|
||||
{pitch: -1, yaw: -20, maxDistance: 17},
|
||||
{pitch: -1, yaw: -15, maxDistance: 17},
|
||||
{pitch: -1, yaw: -10, maxDistance: 17},
|
||||
{pitch: -2, yaw: -5, maxDistance: 17},
|
||||
{pitch: -1, yaw: 0, maxDistance: 17},
|
||||
{pitch: 3, yaw: 10, maxDistance: 17},
|
||||
{pitch: -1, yaw: 15, maxDistance: 17},
|
||||
{pitch: -1, yaw: 20, maxDistance: 17},
|
||||
{pitch: 2, yaw: 25, maxDistance: 17},
|
||||
{pitch: 0, yaw: 30, maxDistance: 17}
|
||||
];
|
||||
|
||||
var gameRunning = false;
|
||||
var gameStartTime;
|
||||
var gameEntityID;
|
||||
var gameTimeoutTimer;
|
||||
var targetEntities = [];
|
||||
var hitSound = SoundCache.getSound(HIT_SOUND_URL);
|
||||
var messageOverlay = undefined;
|
||||
var messageExpire = 0;
|
||||
|
||||
var clearMessage = function() {
|
||||
if (messageOverlay !== undefined) {
|
||||
Overlays.deleteOverlay(messageOverlay);
|
||||
messageOverlay = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
var displayMessage = function(message, timeout) {
|
||||
clearMessage();
|
||||
messageExpire = Date.now() + timeout;
|
||||
messageOverlay = Overlays.addOverlay('text', {
|
||||
text: message,
|
||||
x: (Window.innerWidth / 2) - (MESSAGE_WIDTH / 2),
|
||||
y: (Window.innerHeight / 2) - (MESSAGE_HEIGHT / 2),
|
||||
width: MESSAGE_WIDTH,
|
||||
height: MESSAGE_HEIGHT,
|
||||
alpha: 1,
|
||||
backgroundAlpha: 0.0,
|
||||
font: {size: 20}
|
||||
});
|
||||
};
|
||||
|
||||
var getStatsText = function() {
|
||||
var timeLeft = MAX_GAME_TIME - ((Date.now() - gameStartTime) / MILLISECS_IN_SEC);
|
||||
return 'Time remaining: ' + timeLeft.toFixed(1) + 's\nTargets hit: ' + (TARGETS.length - targetEntities.length) + '/' + TARGETS.length;
|
||||
};
|
||||
|
||||
const timerOverlayWidth = 50;
|
||||
var timerOverlay = Overlays.addOverlay('text', {
|
||||
text: '',
|
||||
x: Window.innerWidth / 2 - (timerOverlayWidth / 2),
|
||||
y: 100,
|
||||
width: timerOverlayWidth,
|
||||
alpha: 1,
|
||||
backgroundAlpha: 0.0,
|
||||
visible: false,
|
||||
font: {size: 20}
|
||||
});
|
||||
|
||||
var cleanRemainingTargets = function() {
|
||||
while (targetEntities.length > 0) {
|
||||
Entities.deleteEntity(targetEntities.pop());
|
||||
}
|
||||
};
|
||||
|
||||
var createTarget = function(position, rotation, scale) {
|
||||
var initialDimensions = {x: 1.8437, y: 0.1614, z: 1.8438};
|
||||
var dimensions = Vec3.multiply(initialDimensions, scale);
|
||||
return Entities.addEntity({
|
||||
type: 'Model',
|
||||
rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)),
|
||||
lifetime: MAX_GAME_TIME,
|
||||
modelURL: MODEL_URL + 'target.fbx',
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: MODEL_URL + 'targetCollision.obj',
|
||||
dimensions: dimensions,
|
||||
position: position
|
||||
});
|
||||
};
|
||||
|
||||
var createTargetInDirection = function(startPosition, startRotation, pitch, yaw, maxDistance, scale) {
|
||||
var directionQuat = Quat.multiply(startRotation, Quat.fromPitchYawRollDegrees(pitch, yaw, 0.0));
|
||||
var directionVec = Vec3.multiplyQbyV(directionQuat, Vec3.FRONT);
|
||||
var intersection = Entities.findRayIntersection({direction: directionVec, origin: startPosition}, true);
|
||||
var distance = maxDistance;
|
||||
if (intersection.intersects && intersection.distance <= maxDistance) {
|
||||
distance = intersection.distance - TARGET_CLOSE_OFFSET;
|
||||
}
|
||||
return createTarget(Vec3.sum(startPosition, Vec3.multiplyQbyV(directionQuat, Vec3.multiply(Vec3.FRONT, distance))), startRotation, scale);
|
||||
};
|
||||
|
||||
var killTimer = function() {
|
||||
if (gameTimeoutTimer !== undefined) {
|
||||
Script.clearTimeout(gameTimeoutTimer);
|
||||
gameTimeoutTimer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
var submitScore = function() {
|
||||
gameRunning = false;
|
||||
killTimer();
|
||||
Overlays.editOverlay(timerOverlay, {visible: false});
|
||||
if (!GlobalServices.username) {
|
||||
displayMessage('Could not submit score, you are not logged in.', 5000);
|
||||
return;
|
||||
}
|
||||
var timeItTook = Date.now() - gameStartTime;
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('POST', SCORE_POST_URL, false);
|
||||
req.send(JSON.stringify({
|
||||
username: GlobalServices.username,
|
||||
time: timeItTook / MILLISECS_IN_SEC
|
||||
}));
|
||||
displayMessage('Your score has been submitted!', 5000);
|
||||
};
|
||||
|
||||
var onTargetHit = function(targetEntity, projectileEntity, collision) {
|
||||
var targetIndex = targetEntities.indexOf(targetEntity);
|
||||
if (targetIndex !== -1) {
|
||||
try {
|
||||
var data = JSON.parse(Entities.getEntityProperties(projectileEntity).userData);
|
||||
if (data.creatorSessionUUID === MyAvatar.sessionUUID) {
|
||||
this.audioInjector = Audio.playSound(hitSound, {
|
||||
position: collision.contactPoint,
|
||||
volume: 0.5
|
||||
});
|
||||
// Attach arrow to target for the nice effect
|
||||
Entities.editEntity(projectileEntity, {
|
||||
ignoreForCollisions: true,
|
||||
parentID: targetEntity
|
||||
});
|
||||
Entities.editEntity(targetEntity, {
|
||||
collisionsWillMove: true,
|
||||
gravity: {x: 0, y: GRAVITY, z: 0},
|
||||
velocity: {x: 0, y: -0.01, z: 0}
|
||||
});
|
||||
targetEntities.splice(targetIndex, 1);
|
||||
if (targetEntities.length === 0) {
|
||||
submitScore();
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var startGame = function(entityID) {
|
||||
cleanRemainingTargets();
|
||||
killTimer();
|
||||
gameEntityID = entityID;
|
||||
targetEntities = [];
|
||||
var parentEntity = Entities.getEntityProperties(gameEntityID);
|
||||
for (var i in TARGETS) {
|
||||
var target = TARGETS[i];
|
||||
var targetEntity = createTargetInDirection(parentEntity.position, parentEntity.rotation, target.pitch, target.yaw, target.maxDistance, 0.67);
|
||||
Script.addEventHandler(targetEntity, 'collisionWithEntity', onTargetHit);
|
||||
targetEntities.push(targetEntity);
|
||||
}
|
||||
gameStartTime = Date.now();
|
||||
gameTimeoutTimer = Script.setTimeout(function() {
|
||||
cleanRemainingTargets();
|
||||
Overlays.editOverlay(timerOverlay, {visible: false});
|
||||
gameRunning = false;
|
||||
displayMessage('Game timed out.', 5000);
|
||||
}, MAX_GAME_TIME * MILLISECS_IN_SEC);
|
||||
gameRunning = true;
|
||||
Overlays.editOverlay(timerOverlay, {visible: true, text: getStatsText()});
|
||||
displayMessage('Game started! GO GO GO!', 3000);
|
||||
};
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == GAME_CHANNEL) {
|
||||
var data = JSON.parse(message);
|
||||
switch (data.action) {
|
||||
case 'start':
|
||||
if (data.playerSessionUUID === MyAvatar.sessionUUID) {
|
||||
startGame(data.gameEntityID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Messages.subscribe(GAME_CHANNEL);
|
||||
Messages.sendMessage(GAME_CHANNEL, JSON.stringify({action: 'scriptLoaded'}));
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
if (gameRunning) {
|
||||
Overlays.editOverlay(timerOverlay, {text: getStatsText()});
|
||||
}
|
||||
if (messageOverlay !== undefined && Date.now() > messageExpire) {
|
||||
clearMessage();
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Overlays.deleteOverlay(timerOverlay);
|
||||
cleanRemainingTargets();
|
||||
clearMessage();
|
||||
});
|
|
@ -45,7 +45,9 @@ else ()
|
|||
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||
endif ()
|
||||
|
||||
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets)
|
||||
find_package(Qt5 COMPONENTS
|
||||
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
||||
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||
|
@ -175,9 +177,17 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
|
|||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
|
||||
)
|
||||
|
||||
# Issue causes build failure unless we add this directory.
|
||||
# See https://bugreports.qt.io/browse/QTBUG-43351
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
|
||||
endif()
|
||||
|
||||
# assume we are using a Qt build without bearer management
|
||||
add_definitions(-DQT_NO_BEARERMANAGEMENT)
|
||||
|
||||
|
@ -209,5 +219,9 @@ else (APPLE)
|
|||
endif()
|
||||
endif (APPLE)
|
||||
|
||||
if (WIN32)
|
||||
set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml")
|
||||
endif()
|
||||
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
|
|
|
@ -415,10 +415,25 @@
|
|||
"states": [
|
||||
{
|
||||
"id": "idle",
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 15,
|
||||
"interpTarget": 10,
|
||||
"interpDuration": 10,
|
||||
"transitions": [
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingForward", "state": "idleToWalkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||
{ "var": "isAway", "state": "awayIntro" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idleToWalkFwd",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "idleToWalkFwdOnDone", "state": "walkFwd" },
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
|
@ -429,7 +444,7 @@
|
|||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
"interpTarget": 6,
|
||||
"interpTarget": 15,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
|
@ -638,6 +653,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "idleToWalkFwd",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/idle_to_walk.fbx",
|
||||
"startFrame": 1.0,
|
||||
"endFrame": 19.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"type": "blendLinearMove",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebKit 3.0
|
||||
import QtWebEngine 1.1
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
|
@ -39,9 +39,10 @@ VrDialog {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: scrollView.top
|
||||
anchors.bottom: webview.top
|
||||
color: "white"
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
spacing: 4
|
||||
|
@ -112,26 +113,22 @@ VrDialog {
|
|||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
WebEngineView {
|
||||
id: webview
|
||||
url: "http://highfidelity.com"
|
||||
anchors.top: buttons.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
WebView {
|
||||
id: webview
|
||||
url: "http://highfidelity.com"
|
||||
anchors.fill: parent
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status == WebView.LoadSucceededStarted) {
|
||||
addressBar.text = loadRequest.url
|
||||
}
|
||||
}
|
||||
onIconChanged: {
|
||||
barIcon.source = icon
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||
addressBar.text = loadRequest.url
|
||||
}
|
||||
}
|
||||
onIconChanged: {
|
||||
console.log("New icon: " + icon)
|
||||
}
|
||||
}
|
||||
} // item
|
||||
|
||||
|
@ -146,5 +143,4 @@ VrDialog {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // dialog
|
||||
|
|
|
@ -2,7 +2,7 @@ import Hifi 1.0 as Hifi
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtWebKit 3.0
|
||||
import QtWebEngine 1.1
|
||||
import "controls"
|
||||
|
||||
VrDialog {
|
||||
|
@ -18,15 +18,11 @@ VrDialog {
|
|||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
ScrollView {
|
||||
WebEngineView {
|
||||
id: webview
|
||||
objectName: "WebView"
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
objectName: "WebView"
|
||||
id: webview
|
||||
url: infoView.url
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
url: infoView.url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Hifi 1.0
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtWebKit 3.0
|
||||
import QtWebEngine 1.1
|
||||
import "controls"
|
||||
|
||||
VrDialog {
|
||||
|
@ -24,27 +24,22 @@ VrDialog {
|
|||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
|
||||
ScrollView {
|
||||
WebEngineView {
|
||||
objectName: "WebView"
|
||||
id: webview
|
||||
url: "https://metaverse.highfidelity.com/marketplace"
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
objectName: "WebView"
|
||||
id: webview
|
||||
url: "https://metaverse.highfidelity.com/marketplace"
|
||||
anchors.fill: parent
|
||||
onNavigationRequested: {
|
||||
console.log(request.url)
|
||||
if (!marketplaceDialog.navigationRequested(request.url)) {
|
||||
console.log("Application absorbed the request")
|
||||
request.action = WebView.IgnoreRequest;
|
||||
return;
|
||||
}
|
||||
console.log("Application passed on the request")
|
||||
request.action = WebView.AcceptRequest;
|
||||
onNavigationRequested: {
|
||||
console.log(request.url)
|
||||
if (!marketplaceDialog.navigationRequested(request.url)) {
|
||||
console.log("Application absorbed the request")
|
||||
request.action = WebView.IgnoreRequest;
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log("Application passed on the request")
|
||||
request.action = WebView.AcceptRequest;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
73
interface/resources/qml/QmlWebWindow.qml
Normal file
73
interface/resources/qml/QmlWebWindow.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import QtWebSockets 1.0
|
||||
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
VrDialog {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
title: "WebWindow"
|
||||
resizable: true
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
backgroundColor: "#7f000000"
|
||||
property url source: "about:blank"
|
||||
|
||||
signal navigating(string url)
|
||||
function stop() {
|
||||
webview.stop();
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
enabled = true
|
||||
console.log("Web Window Created " + root);
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
webview.loadingChanged.connect(handleWebviewLoading)
|
||||
}
|
||||
|
||||
|
||||
function handleWebviewLoading(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var newUrl = loadRequest.url.toString();
|
||||
root.navigating(newUrl)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
implicitHeight: 600
|
||||
implicitWidth: 800
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
width: root.clientWidth
|
||||
height: root.clientHeight
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
url: root.source
|
||||
anchors.fill: parent
|
||||
onUrlChanged: {
|
||||
var currentUrl = url.toString();
|
||||
var newUrl = urlFixer.fixupUrl(currentUrl);
|
||||
if (newUrl != currentUrl) {
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
storageName: "qmlWebEngine"
|
||||
}
|
||||
}
|
||||
} // item
|
||||
} // dialog
|
|
@ -4,116 +4,7 @@ import Hifi 1.0
|
|||
|
||||
// Currently for testing a pure QML replacement menu
|
||||
Item {
|
||||
Item {
|
||||
objectName: "AllActions"
|
||||
Action {
|
||||
id: aboutApp
|
||||
objectName: "HifiAction_" + MenuConstants.AboutApp
|
||||
text: qsTr("About Interface")
|
||||
}
|
||||
|
||||
//
|
||||
// File Menu
|
||||
//
|
||||
Action {
|
||||
id: login
|
||||
objectName: "HifiAction_" + MenuConstants.Login
|
||||
text: qsTr("Login")
|
||||
}
|
||||
Action {
|
||||
id: quit
|
||||
objectName: "HifiAction_" + MenuConstants.Quit
|
||||
text: qsTr("Quit")
|
||||
//shortcut: StandardKey.Quit
|
||||
shortcut: "Ctrl+Q"
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Edit menu
|
||||
//
|
||||
Action {
|
||||
id: undo
|
||||
text: "Undo"
|
||||
shortcut: StandardKey.Undo
|
||||
}
|
||||
|
||||
Action {
|
||||
id: redo
|
||||
text: "Redo"
|
||||
shortcut: StandardKey.Redo
|
||||
}
|
||||
|
||||
Action {
|
||||
id: animations
|
||||
objectName: "HifiAction_" + MenuConstants.Animations
|
||||
text: qsTr("Animations...")
|
||||
}
|
||||
Action {
|
||||
id: attachments
|
||||
text: qsTr("Attachments...")
|
||||
}
|
||||
Action {
|
||||
id: explode
|
||||
text: qsTr("Explode on quit")
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
Action {
|
||||
id: freeze
|
||||
text: qsTr("Freeze on quit")
|
||||
checkable: true
|
||||
checked: false
|
||||
}
|
||||
ExclusiveGroup {
|
||||
Action {
|
||||
id: visibleToEveryone
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToEveryone
|
||||
text: qsTr("Everyone")
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
Action {
|
||||
id: visibleToFriends
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToFriends
|
||||
text: qsTr("Friends")
|
||||
checkable: true
|
||||
}
|
||||
Action {
|
||||
id: visibleToNoOne
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToNoOne
|
||||
text: qsTr("No one")
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
objectName: "rootMenu";
|
||||
Menu {
|
||||
title: "File"
|
||||
MenuItem { action: login }
|
||||
MenuItem { action: explode }
|
||||
MenuItem { action: freeze }
|
||||
MenuItem { action: quit }
|
||||
}
|
||||
Menu {
|
||||
title: "Tools"
|
||||
Menu {
|
||||
title: "I Am Visible To"
|
||||
MenuItem { action: visibleToEveryone }
|
||||
MenuItem { action: visibleToFriends }
|
||||
MenuItem { action: visibleToNoOne }
|
||||
}
|
||||
MenuItem { action: animations }
|
||||
}
|
||||
Menu {
|
||||
title: "Long menu name top menu"
|
||||
MenuItem { action: aboutApp }
|
||||
}
|
||||
Menu {
|
||||
title: "Help"
|
||||
MenuItem { action: aboutApp }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebKit 3.0
|
||||
import QtWebEngine 1.1
|
||||
|
||||
WebView {
|
||||
WebEngineView {
|
||||
id: root
|
||||
objectName: "webview"
|
||||
anchors.fill: parent
|
||||
objectName: "webview"
|
||||
url: "about:blank"
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
#include <RenderableWebEntityItem.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <RenderScriptingInterface.h>
|
||||
#include <SceneScriptingInterface.h>
|
||||
#include <RecordingScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
|
@ -101,6 +102,7 @@
|
|||
#include <VrMenu.h>
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
#include <QmlWebWindowClass.h>
|
||||
|
||||
#include "AnimDebugDraw.h"
|
||||
#include "AudioClient.h"
|
||||
|
@ -341,6 +343,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
#endif
|
||||
DependencyManager::set<DiscoverabilityManager>();
|
||||
DependencyManager::set<SceneScriptingInterface>();
|
||||
DependencyManager::set<RenderScriptingInterface>();
|
||||
DependencyManager::set<OffscreenUi>();
|
||||
DependencyManager::set<AutoUpdater>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
|
@ -362,6 +365,17 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
|||
int _keyboardFocusHighlightID{ -1 };
|
||||
PluginContainer* _pluginContainer;
|
||||
|
||||
|
||||
// FIXME hack access to the internal share context for the Chromium helper
|
||||
// Normally we'd want to use QWebEngine::initialize(), but we can't because
|
||||
// our primary context is a QGLWidget, which can't easily be initialized to share
|
||||
// from a QOpenGLContext.
|
||||
//
|
||||
// So instead we create a new offscreen context to share with the QGLWidget,
|
||||
// and manually set THAT to be the shared context for the Chromium helper
|
||||
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
||||
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||
QApplication(argc, argv),
|
||||
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
|
||||
|
@ -623,6 +637,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_glWidget->makeCurrent();
|
||||
_glWidget->initializeGL();
|
||||
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->create(_glWidget->context()->contextHandle());
|
||||
_chromiumShareContext->makeCurrent();
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
_offscreenContext->create(_glWidget->context()->contextHandle());
|
||||
_offscreenContext->makeCurrent();
|
||||
|
@ -686,13 +705,37 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||
VrMenu::toggle(); // show context menu even on non-stereo displays
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||
auto globalPos = QCursor::pos();
|
||||
globalPos.setX(globalPos.x() + state);
|
||||
QCursor::setPos(globalPos);
|
||||
auto oldPos = QCursor::pos();
|
||||
auto newPos = oldPos;
|
||||
newPos.setX(oldPos.x() + state);
|
||||
QCursor::setPos(newPos);
|
||||
|
||||
|
||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||
// remove it after we're done
|
||||
const float REASONABLE_CHANGE = 50.0f;
|
||||
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
||||
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
||||
auto distance = glm::distance(oldPosG, newPosG);
|
||||
if (distance > REASONABLE_CHANGE) {
|
||||
qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
||||
}
|
||||
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
||||
auto globalPos = QCursor::pos();
|
||||
globalPos.setY(globalPos.y() + state);
|
||||
QCursor::setPos(globalPos);
|
||||
auto oldPos = QCursor::pos();
|
||||
auto newPos = oldPos;
|
||||
newPos.setY(oldPos.y() + state);
|
||||
QCursor::setPos(newPos);
|
||||
|
||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||
// remove it after we're done
|
||||
const float REASONABLE_CHANGE = 50.0f;
|
||||
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
||||
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
||||
auto distance = glm::distance(oldPosG, newPosG);
|
||||
if (distance > REASONABLE_CHANGE) {
|
||||
qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -706,9 +749,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
|
||||
}));
|
||||
|
||||
userInputMapper->registerDevice(_applicationStateDevice);
|
||||
|
||||
|
@ -1161,26 +1204,15 @@ void Application::paintGL() {
|
|||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
PerformanceTimer perfTimer("Mirror");
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
renderArgs._blitFramebuffer = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
|
||||
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
|
||||
{
|
||||
float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale());
|
||||
// Flip the src and destination rect horizontally to do the mirror
|
||||
auto mirrorRect = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio);
|
||||
auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w);
|
||||
|
||||
auto selfieFbo = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(selfieFbo);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest);
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1260,15 +1292,14 @@ void Application::paintGL() {
|
|||
hmdOffset.x = -hmdOffset.x;
|
||||
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0)
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0)
|
||||
+ mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ mirrorBodyOrientation * hmdOffset);
|
||||
|
||||
} else {
|
||||
_myCamera.setRotation(myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0)
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0)
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
}
|
||||
|
@ -1352,65 +1383,11 @@ void Application::paintGL() {
|
|||
renderArgs._context->setStereoProjections(eyeProjections);
|
||||
renderArgs._context->setStereoViews(eyeOffsets);
|
||||
}
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._context->enableStereo(false);
|
||||
|
||||
// Blit primary to final FBO
|
||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
|
||||
if (renderArgs._renderMode == RenderArgs::MIRROR_RENDER_MODE) {
|
||||
if (displayPlugin->isStereo()) {
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
gpu::Vec4i srcRectLeft;
|
||||
srcRectLeft.z = size.width() / 2;
|
||||
srcRectLeft.w = size.height();
|
||||
|
||||
gpu::Vec4i srcRectRight;
|
||||
srcRectRight.x = size.width() / 2;
|
||||
srcRectRight.z = size.width();
|
||||
srcRectRight.w = size.height();
|
||||
|
||||
gpu::Vec4i destRectLeft;
|
||||
destRectLeft.x = srcRectLeft.z;
|
||||
destRectLeft.z = srcRectLeft.x;
|
||||
destRectLeft.y = srcRectLeft.y;
|
||||
destRectLeft.w = srcRectLeft.w;
|
||||
|
||||
gpu::Vec4i destRectRight;
|
||||
destRectRight.x = srcRectRight.z;
|
||||
destRectRight.z = srcRectRight.x;
|
||||
destRectRight.y = srcRectRight.y;
|
||||
destRectRight.w = srcRectRight.w;
|
||||
|
||||
batch.setFramebuffer(finalFramebuffer);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 1.0f, 0.0f));
|
||||
// BLit left to right and right to left in stereo
|
||||
batch.blit(primaryFbo, srcRectRight, finalFramebuffer, destRectLeft);
|
||||
batch.blit(primaryFbo, srcRectLeft, finalFramebuffer, destRectRight);
|
||||
});
|
||||
} else {
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
gpu::Vec4i srcRect;
|
||||
srcRect.z = size.width();
|
||||
srcRect.w = size.height();
|
||||
gpu::Vec4i destRect;
|
||||
destRect.x = size.width();
|
||||
destRect.y = 0;
|
||||
destRect.z = 0;
|
||||
destRect.w = size.height();
|
||||
batch.setFramebuffer(finalFramebuffer);
|
||||
batch.blit(primaryFbo, srcRect, finalFramebuffer, destRect);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
gpu::Vec4i rect;
|
||||
rect.z = size.width();
|
||||
rect.w = size.height();
|
||||
batch.setFramebuffer(finalFramebuffer);
|
||||
batch.blit(primaryFbo, rect, finalFramebuffer, rect);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay Composition, needs to occur after screen space effects have completed
|
||||
|
@ -1418,7 +1395,9 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/compositor");
|
||||
PerformanceTimer perfTimer("compositor");
|
||||
|
||||
auto primaryFbo = finalFramebuffer;
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo));
|
||||
if (displayPlugin->isStereo()) {
|
||||
QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height()));
|
||||
|
@ -1443,7 +1422,9 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
|
||||
auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0);
|
||||
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
|
||||
Q_ASSERT(0 != finalTexture);
|
||||
|
||||
|
@ -2571,7 +2552,7 @@ void Application::init() {
|
|||
|
||||
_environment.init();
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->init(this);
|
||||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
|
||||
DependencyManager::get<AvatarManager>()->init();
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
|
@ -2989,6 +2970,9 @@ void Application::update(float deltaTime) {
|
|||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachAction([&](EntityActionPointer action) {
|
||||
action->prepareForPhysicsSimulation();
|
||||
});
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
|
@ -3401,7 +3385,7 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) {
|
|||
renderArgs->_renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
renderRearViewMirror(renderArgs, QRect(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), true);
|
||||
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
|
||||
renderArgs->_context->downloadFramebuffer(primaryFbo, glm::ivec4(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), image);
|
||||
|
||||
|
@ -3671,34 +3655,19 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
// For now every frame pass the renderContext
|
||||
{
|
||||
PerformanceTimer perfTimer("EngineRun");
|
||||
render::RenderContext renderContext;
|
||||
|
||||
auto sceneInterface = DependencyManager::get<SceneScriptingInterface>();
|
||||
|
||||
renderContext._cullOpaque = sceneInterface->doEngineCullOpaque();
|
||||
renderContext._sortOpaque = sceneInterface->doEngineSortOpaque();
|
||||
renderContext._renderOpaque = sceneInterface->doEngineRenderOpaque();
|
||||
renderContext._cullTransparent = sceneInterface->doEngineCullTransparent();
|
||||
renderContext._sortTransparent = sceneInterface->doEngineSortTransparent();
|
||||
renderContext._renderTransparent = sceneInterface->doEngineRenderTransparent();
|
||||
|
||||
renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems();
|
||||
renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems();
|
||||
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
|
||||
|
||||
renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
|
||||
renderContext._drawItemStatus |= render::showNetworkStatusFlag;
|
||||
}
|
||||
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
|
||||
|
||||
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||
auto renderInterface = DependencyManager::get<RenderScriptingInterface>();
|
||||
auto renderContext = renderInterface->getRenderContext();
|
||||
|
||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||
|
||||
renderContext.args = renderArgs;
|
||||
renderArgs->_viewFrustum = getDisplayViewFrustum();
|
||||
renderContext.setArgs(renderArgs);
|
||||
|
||||
bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||
bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned);
|
||||
renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus);
|
||||
|
||||
_renderEngine->setRenderContext(renderContext);
|
||||
|
||||
// Before the deferred pass, let's try to use the render engine
|
||||
|
@ -3706,15 +3675,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
_renderEngine->run();
|
||||
myAvatar->endRenderRun();
|
||||
|
||||
auto engineRC = _renderEngine->getRenderContext();
|
||||
sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems);
|
||||
sceneInterface->setEngineDrawnOpaqueItems(engineRC->_numDrawnOpaqueItems);
|
||||
|
||||
sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems);
|
||||
sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems);
|
||||
|
||||
sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems);
|
||||
sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems);
|
||||
auto engineContext = _renderEngine->getRenderContext();
|
||||
renderInterface->setItemCounts(engineContext->getItemsConfig());
|
||||
}
|
||||
|
||||
activeRenderingThread = nullptr;
|
||||
|
@ -4140,6 +4102,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
LocationScriptingInterface::locationSetter);
|
||||
|
||||
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
|
||||
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
|
@ -4168,11 +4131,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);
|
||||
|
||||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget());
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) {
|
||||
bool Application::canAcceptURL(const QString& urlString) const {
|
||||
QUrl url(urlString);
|
||||
if (urlString.startsWith(HIFI_URL_SCHEME)) {
|
||||
return true;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <StDev.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <AbstractUriHandler.h>
|
||||
|
||||
#include "avatar/AvatarUpdate.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
|
@ -88,7 +89,7 @@ class Application;
|
|||
#endif
|
||||
#define qApp (static_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface {
|
||||
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler {
|
||||
Q_OBJECT
|
||||
|
||||
// TODO? Get rid of those
|
||||
|
@ -219,8 +220,8 @@ public:
|
|||
QString getScriptsLocation();
|
||||
void setScriptsLocation(const QString& scriptsLocation);
|
||||
|
||||
bool canAcceptURL(const QString& url);
|
||||
bool acceptURL(const QString& url, bool defaultUpload = false);
|
||||
virtual bool canAcceptURL(const QString& url) const override;
|
||||
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
|
||||
|
||||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||
int getMaxOctreePacketsPerSecond();
|
||||
|
|
|
@ -1085,6 +1085,26 @@ void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) {
|
|||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) {
|
||||
auto menu = addMenu(groupName);
|
||||
|
||||
QActionGroup* actionGroup = new QActionGroup(menu);
|
||||
actionGroup->setExclusive(true);
|
||||
|
||||
auto menuScriptingInterface = MenuScriptingInterface::getInstance();
|
||||
for (auto action : actionList) {
|
||||
auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected,
|
||||
menuScriptingInterface,
|
||||
SLOT(menuItemTriggered()));
|
||||
actionGroup->addAction(item);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::removeActionGroup(const QString& groupName) {
|
||||
removeMenu(groupName);
|
||||
}
|
||||
|
||||
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
|
|
|
@ -105,6 +105,8 @@ public slots:
|
|||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString());
|
||||
void removeActionGroup(const QString& groupName);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
|
|
|
@ -99,7 +99,8 @@ Avatar::Avatar(RigPointer rig) :
|
|||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(qApp->thread());
|
||||
|
||||
setAvatarScale(1.0f);
|
||||
setScale(glm::vec3(1.0f)); // avatar scale is uniform
|
||||
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
_headData = static_cast<HeadData*>(new Head(this));
|
||||
_handData = static_cast<HandData*>(new Hand(this));
|
||||
|
@ -143,15 +144,35 @@ AABox Avatar::getBounds() const {
|
|||
|
||||
float Avatar::getLODDistance() const {
|
||||
return DependencyManager::get<LODManager>()->getAvatarLODDistanceMultiplier() *
|
||||
glm::distance(qApp->getCamera()->getPosition(), getPosition()) / getAvatarScale();
|
||||
glm::distance(qApp->getCamera()->getPosition(), getPosition()) / getUniformScale();
|
||||
}
|
||||
|
||||
void Avatar::animateScaleChanges(float deltaTime) {
|
||||
float currentScale = getUniformScale();
|
||||
if (currentScale != _targetScale) {
|
||||
// use exponential decay toward _targetScale
|
||||
const float SCALE_ANIMATION_TIMESCALE = 0.5f;
|
||||
float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);
|
||||
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale;
|
||||
|
||||
// snap to the end when we get close enough
|
||||
const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
|
||||
if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) {
|
||||
animatedScale = _targetScale;
|
||||
}
|
||||
|
||||
setScale(glm::vec3(animatedScale)); // avatar scale is uniform
|
||||
rebuildCollisionShape();
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
if (getAvatarScale() != _targetScale) {
|
||||
setAvatarScale(_targetScale);
|
||||
if (!isDead() && !_motionState) {
|
||||
DependencyManager::get<AvatarManager>()->addAvatarToSimulation(this);
|
||||
}
|
||||
animateScaleChanges(deltaTime);
|
||||
|
||||
// update the billboard render flag
|
||||
const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f;
|
||||
|
@ -164,7 +185,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
_shouldRenderBillboard = true;
|
||||
qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance();
|
||||
}
|
||||
|
||||
|
||||
const bool isControllerLogging = DependencyManager::get<AvatarManager>()->getRenderDistanceControllerIsLogging();
|
||||
float renderDistance = DependencyManager::get<AvatarManager>()->getRenderDistance();
|
||||
const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION;
|
||||
|
@ -210,7 +231,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
_skeletonModel.getHeadPosition(headPosition);
|
||||
Head* head = getHead();
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(getAvatarScale());
|
||||
head->setScale(getUniformScale());
|
||||
head->simulate(deltaTime, false, _shouldRenderBillboard);
|
||||
}
|
||||
}
|
||||
|
@ -238,12 +259,12 @@ void Avatar::simulate(float deltaTime) {
|
|||
measureMotionDerivatives(deltaTime);
|
||||
}
|
||||
|
||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) {
|
||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
||||
const float HEAD_SPHERE_RADIUS = 0.1f;
|
||||
glm::vec3 theirLookAt = dynamic_pointer_cast<Avatar>(avatar)->getHead()->getLookAtPosition();
|
||||
glm::vec3 myEyePosition = getHead()->getEyePosition();
|
||||
|
||||
return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getAvatarScale());
|
||||
return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getUniformScale());
|
||||
}
|
||||
|
||||
void Avatar::slamPosition(const glm::vec3& newPosition) {
|
||||
|
@ -423,7 +444,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
const float BASE_LIGHT_DISTANCE = 2.0f;
|
||||
const float LIGHT_EXPONENT = 1.0f;
|
||||
const float LIGHT_CUTOFF = glm::radians(80.0f);
|
||||
float distance = BASE_LIGHT_DISTANCE * getAvatarScale();
|
||||
float distance = BASE_LIGHT_DISTANCE * getUniformScale();
|
||||
glm::vec3 position = glm::mix(_skeletonModel.getTranslation(), getHead()->getFaceModel().getTranslation(), 0.9f);
|
||||
glm::quat orientation = getOrientation();
|
||||
foreach (const AvatarManager::LocalLight& light, DependencyManager::get<AvatarManager>()->getLocalLights()) {
|
||||
|
@ -436,7 +457,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
|
||||
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
|
||||
const float BOUNDING_SHAPE_ALPHA = 0.7f;
|
||||
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
|
||||
}
|
||||
|
||||
// If this is the avatar being looked at, render a little ball above their head
|
||||
|
@ -479,7 +501,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT),
|
||||
Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT),
|
||||
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
|
||||
|
||||
position = getHead()->getRightEyePosition();
|
||||
|
@ -489,7 +511,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
eyeDiameter = DEFAULT_EYE_DIAMETER;
|
||||
}
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT),
|
||||
Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT),
|
||||
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
|
||||
|
||||
}
|
||||
|
@ -590,9 +612,9 @@ void Avatar::simulateAttachments(float deltaTime) {
|
|||
glm::quat jointRotation;
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getAvatarScale());
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, getAvatarScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
model->simulate(deltaTime);
|
||||
|
@ -647,7 +669,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
float Avatar::getBillboardSize() const {
|
||||
return getAvatarScale() * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
return getUniformScale() * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -796,7 +818,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
|
|||
}
|
||||
|
||||
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||
const float MAX_OFFSET_LENGTH = getAvatarScale() * 0.5f;
|
||||
const float MAX_OFFSET_LENGTH = getUniformScale() * 0.5f;
|
||||
float offsetLength = glm::length(offset);
|
||||
if (offsetLength > MAX_OFFSET_LENGTH) {
|
||||
_skeletonOffset = (MAX_OFFSET_LENGTH / offsetLength) * offset;
|
||||
|
@ -905,7 +927,7 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const {
|
|||
|
||||
void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
|
||||
//Scale a world space vector as if it was relative to the position
|
||||
positionToScale = getPosition() + getAvatarScale() * (positionToScale - getPosition());
|
||||
positionToScale = getPosition() + getUniformScale() * (positionToScale - getPosition());
|
||||
}
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
|
@ -945,7 +967,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||
_attachmentModels[i]->setScaleToFit(true, getAvatarScale() * _attachmentData.at(i).scale);
|
||||
_attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1033,15 +1055,6 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::setAvatarScale(float scale) {
|
||||
if (_targetScale * (1.0f - RESCALING_TOLERANCE) < scale &&
|
||||
scale < _targetScale * (1.0f + RESCALING_TOLERANCE)) {
|
||||
setScale(glm::vec3(_targetScale));
|
||||
} else {
|
||||
setScale(glm::vec3(scale));
|
||||
}
|
||||
}
|
||||
|
||||
float Avatar::getSkeletonHeight() const {
|
||||
Extents extents = _skeletonModel.getBindExtents();
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
|
@ -1053,7 +1066,7 @@ float Avatar::getHeadHeight() const {
|
|||
|
||||
// HACK: We have a really odd case when fading out for some models where this value explodes
|
||||
float result = extents.maximum.y - extents.minimum.y;
|
||||
if (result >= 0.0f && result < 100.0f * getAvatarScale() ) {
|
||||
if (result >= 0.0f && result < 100.0f * getUniformScale() ) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1096,13 +1109,21 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
|
||||
// virtual
|
||||
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight());
|
||||
shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset());
|
||||
float uniformScale = getUniformScale();
|
||||
shapeInfo.setCapsuleY(uniformScale * _skeletonModel.getBoundingCapsuleRadius(),
|
||||
0.5f * uniformScale * _skeletonModel.getBoundingCapsuleHeight());
|
||||
shapeInfo.setOffset(uniformScale * _skeletonModel.getBoundingCapsuleOffset());
|
||||
}
|
||||
|
||||
void Avatar::setMotionState(AvatarMotionState* motionState) {
|
||||
_motionState = motionState;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::rebuildSkeletonBody() {
|
||||
DependencyManager::get<AvatarManager>()->updateAvatarPhysicsShape(this);
|
||||
void Avatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getLeftPalmPosition() {
|
||||
|
|
|
@ -36,7 +36,6 @@ namespace render {
|
|||
|
||||
static const float SCALING_RATIO = .05f;
|
||||
static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1
|
||||
static const float RESCALING_TOLERANCE = .02f;
|
||||
|
||||
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
|
||||
static const float BILLBOARD_DISTANCE = 5.56f; // meters
|
||||
|
@ -89,7 +88,7 @@ public:
|
|||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
float getAvatarScale() const { return getScale().y; }
|
||||
float getUniformScale() const { return getScale().y; }
|
||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||
|
@ -154,11 +153,10 @@ public:
|
|||
// (otherwise floating point error will cause problems at large positions).
|
||||
void applyPositionDelta(const glm::vec3& delta);
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
virtual void rebuildCollisionShape();
|
||||
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||
|
||||
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
virtual void setPosition(const glm::vec3& position) override;
|
||||
|
@ -173,6 +171,10 @@ public slots:
|
|||
glm::quat getRightPalmRotation();
|
||||
|
||||
protected:
|
||||
friend class AvatarManager;
|
||||
|
||||
void setMotionState(AvatarMotionState* motionState);
|
||||
|
||||
SkeletonModel _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
QVector<Model*> _attachmentModels;
|
||||
|
@ -199,14 +201,15 @@ protected:
|
|||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
bool isLookingAtMe(AvatarSharedPointer avatar);
|
||||
|
||||
// protected methods...
|
||||
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
||||
|
||||
virtual void animateScaleChanges(float deltaTime);
|
||||
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setAvatarScale(float scale);
|
||||
void measureMotionDerivatives(float deltaTime);
|
||||
|
||||
float getSkeletonHeight() const;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QVariantGLM.h>
|
||||
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "CharacterController.h"
|
||||
|
||||
const uint16_t AvatarActionHold::holdVersion = 1;
|
||||
|
||||
|
@ -32,6 +33,64 @@ AvatarActionHold::~AvatarActionHold() {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool AvatarActionHold::getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
MyCharacterController* controller = myAvatar ? myAvatar->getCharacterController() : nullptr;
|
||||
if (!controller) {
|
||||
qDebug() << "AvatarActionHold::getAvatarRigidBodyLocation failed to get character controller";
|
||||
return false;
|
||||
}
|
||||
controller->getRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AvatarActionHold::prepareForPhysicsSimulation() {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||
|
||||
if (!holdingAvatar || !holdingAvatar->isMyAvatar()) {
|
||||
return;
|
||||
}
|
||||
|
||||
withWriteLock([&]{
|
||||
if (_ignoreIK) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 palmPosition;
|
||||
glm::quat palmRotation;
|
||||
if (_hand == "right") {
|
||||
palmPosition = holdingAvatar->getRightPalmPosition();
|
||||
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||
} else {
|
||||
palmPosition = holdingAvatar->getLeftPalmPosition();
|
||||
palmRotation = holdingAvatar->getLeftPalmRotation();
|
||||
}
|
||||
|
||||
glm::vec3 avatarRigidBodyPosition;
|
||||
glm::quat avatarRigidBodyRotation;
|
||||
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||
|
||||
// determine the difference in translation and rotation between the avatar's
|
||||
// rigid body and the palm position. The avatar's rigid body will be moved by bullet
|
||||
// between this call and the call to getTarget, below. A call to get*PalmPosition in
|
||||
// getTarget would get the palm position of the previous location of the avatar (because
|
||||
// bullet has moved the av's rigid body but the rigid body's location has not yet been
|
||||
// copied out into the Avatar class.
|
||||
//glm::quat avatarRotationInverse = glm::inverse(avatarRigidBodyRotation);
|
||||
|
||||
// the offset should be in the frame of the avatar, but something about the order
|
||||
// things are updated makes this wrong:
|
||||
// _palmOffsetFromRigidBody = avatarRotationInverse * (palmPosition - avatarRigidBodyPosition);
|
||||
// I'll leave it here as a comment in case avatar handling changes.
|
||||
_palmOffsetFromRigidBody = palmPosition - avatarRigidBodyPosition;
|
||||
|
||||
// rotation should also be needed, but again, the order of updates makes this unneeded. leaving
|
||||
// code here for future reference.
|
||||
// _palmRotationFromRigidBody = avatarRotationInverse * palmRotation;
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||
|
@ -40,11 +99,11 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::ve
|
|||
return holdingAvatar;
|
||||
}
|
||||
|
||||
withTryReadLock([&]{
|
||||
withReadLock([&]{
|
||||
bool isRightHand = (_hand == "right");
|
||||
glm::vec3 palmPosition { Vectors::ZERO };
|
||||
glm::quat palmRotation { Quaternions::IDENTITY };
|
||||
|
||||
|
||||
if (_ignoreIK && holdingAvatar->isMyAvatar()) {
|
||||
// We cannot ignore other avatars IK and this is not the point of this option
|
||||
// This is meant to make the grabbing behavior more reactive.
|
||||
|
@ -55,6 +114,31 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::ve
|
|||
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition();
|
||||
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation();
|
||||
}
|
||||
} else if (holdingAvatar->isMyAvatar()) {
|
||||
glm::vec3 avatarRigidBodyPosition;
|
||||
glm::quat avatarRigidBodyRotation;
|
||||
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||
|
||||
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
|
||||
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
|
||||
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
|
||||
// be stale. Instead, determine the current palm position with the current avatar's rigid body
|
||||
// location and the saved offsets.
|
||||
|
||||
// this line is more correct but breaks for the current way avatar data is updated.
|
||||
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
|
||||
// instead, use this for now:
|
||||
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
|
||||
|
||||
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
|
||||
// than that of the rigid body. leaving this next line here for future reference:
|
||||
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
|
||||
|
||||
if (isRightHand) {
|
||||
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||
} else {
|
||||
palmRotation = holdingAvatar->getLeftPalmRotation();
|
||||
}
|
||||
} else {
|
||||
if (isRightHand) {
|
||||
palmPosition = holdingAvatar->getRightPalmPosition();
|
||||
|
@ -103,21 +187,19 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
if (valid && holdCount > 0) {
|
||||
position /= holdCount;
|
||||
|
||||
bool gotLock = withTryWriteLock([&]{
|
||||
withWriteLock([&]{
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
_positionalTargetSet = true;
|
||||
_rotationalTargetSet = true;
|
||||
_active = true;
|
||||
});
|
||||
if (gotLock) {
|
||||
if (_kinematic) {
|
||||
doKinematicUpdate(deltaTimeStep);
|
||||
} else {
|
||||
activateBody();
|
||||
forceBodyNonStatic();
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
if (_kinematic) {
|
||||
doKinematicUpdate(deltaTimeStep);
|
||||
} else {
|
||||
activateBody();
|
||||
forceBodyNonStatic();
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,21 @@ public:
|
|||
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionHold();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual QVariantMap getArguments();
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
virtual void updateActionWorker(float deltaTimeStep) override;
|
||||
|
||||
QByteArray serialize() const;
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
|
||||
virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); }
|
||||
virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); }
|
||||
|
||||
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
|
||||
|
||||
virtual void prepareForPhysicsSimulation() override;
|
||||
|
||||
private:
|
||||
void doKinematicUpdate(float deltaTimeStep);
|
||||
|
||||
|
@ -56,6 +59,10 @@ private:
|
|||
|
||||
float _previousDeltaTimeStep = 0.0f;
|
||||
glm::vec3 _previousPositionalDelta;
|
||||
|
||||
glm::vec3 _palmOffsetFromRigidBody;
|
||||
// leaving this here for future refernece.
|
||||
// glm::quat _palmRotationFromRigidBody;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionHold_h
|
||||
|
|
|
@ -203,7 +203,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
while (fadingIterator != _avatarFades.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
||||
avatar->startUpdate();
|
||||
avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE);
|
||||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
|
@ -223,14 +223,14 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
|||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
|
||||
auto rawRenderableAvatar = std::static_pointer_cast<Avatar>(newAvatar);
|
||||
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
|
||||
rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
|
||||
return newAvatar;
|
||||
}
|
||||
|
||||
|
@ -251,7 +251,7 @@ void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
|||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
|
||||
auto removedAvatar = _avatarHash.take(sessionUUID);
|
||||
if (removedAvatar) {
|
||||
handleRemovedAvatar(removedAvatar);
|
||||
|
@ -260,7 +260,8 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
|||
|
||||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) {
|
||||
AvatarHashMap::handleRemovedAvatar(removedAvatar);
|
||||
|
||||
|
||||
removedAvatar->die();
|
||||
removeAvatarMotionState(removedAvatar);
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
}
|
||||
|
@ -268,7 +269,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
void AvatarManager::clearOtherAvatars() {
|
||||
// clear any avatars that came from an avatar-mixer
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||
|
@ -278,7 +279,7 @@ void AvatarManager::clearOtherAvatars() {
|
|||
} else {
|
||||
auto removedAvatar = avatarIterator.value();
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
|
||||
|
||||
handleRemovedAvatar(removedAvatar);
|
||||
}
|
||||
}
|
||||
|
@ -374,20 +375,18 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) {
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
motionState->addDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||
} else {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
if (shape) {
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
}
|
||||
void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
|
||||
assert(!avatar->getMotionState());
|
||||
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
if (shape) {
|
||||
// we don't add to the simulation now, we put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,6 +415,6 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
|
|||
if (sessionID == _myAvatar->getSessionUUID()) {
|
||||
return _myAvatar;
|
||||
}
|
||||
|
||||
|
||||
return findAvatar(sessionID);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
glm::vec3 color;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
||||
|
||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||
// Currently, your own avatar will be included as the null avatar id.
|
||||
|
@ -66,8 +66,8 @@ public:
|
|||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
void updateAvatarPhysicsShape(Avatar* avatar);
|
||||
|
||||
void addAvatarToSimulation(Avatar* avatar);
|
||||
|
||||
// Expose results and parameter-tuning operations to other systems, such as stats and javascript.
|
||||
Q_INVOKABLE float getRenderDistance() { return _renderDistance; }
|
||||
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
|
||||
|
||||
|
||||
public slots:
|
||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
@ -90,19 +90,19 @@ private:
|
|||
AvatarManager(const AvatarManager& other);
|
||||
|
||||
void simulateAvatarFades(float deltaTime);
|
||||
|
||||
|
||||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
|
||||
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar);
|
||||
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
|
||||
QVector<AvatarManager::LocalLight> _localLights;
|
||||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
virtual float getObjectFriction() const;
|
||||
virtual float getObjectLinearDamping() const;
|
||||
virtual float getObjectAngularDamping() const;
|
||||
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const;
|
||||
virtual glm::quat getObjectRotation() const;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const;
|
||||
|
|
|
@ -37,7 +37,7 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
||||
float avatarScale = 1.0f;
|
||||
if (_owningAvatar) {
|
||||
avatarScale = _owningAvatar->getAvatarScale();
|
||||
avatarScale = _owningAvatar->getUniformScale();
|
||||
}
|
||||
|
||||
const float alpha = 1.0f;
|
||||
|
@ -62,7 +62,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
|||
transform.setRotation(palm.getRotation());
|
||||
transform.postScale(SPHERE_RADIUS);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch, transform, grayColor);
|
||||
|
||||
|
||||
// draw a green sphere at the old "finger tip"
|
||||
transform = Transform();
|
||||
position = palm.getTipPosition();
|
||||
|
@ -72,7 +72,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
|||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch, transform, greenColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const float AXIS_RADIUS = 0.1f * SPHERE_RADIUS;
|
||||
const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
|
||||
|
||||
|
|
|
@ -287,10 +287,7 @@ extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avata
|
|||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
if (getAvatarScale() != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * getAvatarScale() + SMOOTHING_RATIO * _targetScale;
|
||||
setAvatarScale(scale);
|
||||
}
|
||||
animateScaleChanges(deltaTime);
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("transform");
|
||||
|
@ -337,7 +334,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
headPosition = getPosition();
|
||||
}
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(getAvatarScale());
|
||||
head->setScale(getUniformScale());
|
||||
head->simulate(deltaTime, true);
|
||||
}
|
||||
|
||||
|
@ -512,25 +509,25 @@ glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
|||
controller::Pose MyAvatar::getLeftHandPose() const {
|
||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
||||
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
||||
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandPose() const {
|
||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
||||
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
||||
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
||||
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
||||
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandTipPose() const {
|
||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
||||
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
||||
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -539,7 +536,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
if (!_shouldRender) {
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
|
||||
Avatar::render(renderArgs, cameraPosition);
|
||||
}
|
||||
|
||||
|
@ -681,7 +678,7 @@ void MyAvatar::loadData() {
|
|||
|
||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
setAvatarScale(getAvatarScale());
|
||||
setScale(glm::vec3(_targetScale));
|
||||
|
||||
_animGraphUrl = settings.value("animGraphURL", "").toString();
|
||||
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
|
||||
|
@ -802,14 +799,14 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
|
||||
|
||||
AvatarHash hash = DependencyManager::get<AvatarManager>()->getHashCopy();
|
||||
|
||||
|
||||
foreach (const AvatarSharedPointer& avatarPointer, hash) {
|
||||
auto avatar = static_pointer_cast<Avatar>(avatarPointer);
|
||||
bool isCurrentTarget = avatar->getIsLookAtTarget();
|
||||
float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition);
|
||||
avatar->setIsLookAtTarget(false);
|
||||
if (!avatar->isMyAvatar() && avatar->isInitialized() &&
|
||||
(distanceTo < GREATEST_LOOKING_AT_DISTANCE * getAvatarScale())) {
|
||||
(distanceTo < GREATEST_LOOKING_AT_DISTANCE * getUniformScale())) {
|
||||
float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition));
|
||||
if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) {
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
|
@ -1036,14 +1033,15 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
|
|||
return Avatar::getPosition();
|
||||
}
|
||||
|
||||
void MyAvatar::rebuildSkeletonBody() {
|
||||
void MyAvatar::rebuildCollisionShape() {
|
||||
// compute localAABox
|
||||
float radius = _skeletonModel.getBoundingCapsuleRadius();
|
||||
float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius;
|
||||
float scale = getUniformScale();
|
||||
float radius = scale * _skeletonModel.getBoundingCapsuleRadius();
|
||||
float height = scale * _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius;
|
||||
glm::vec3 corner(-radius, -0.5f * height, -radius);
|
||||
corner += _skeletonModel.getBoundingCapsuleOffset();
|
||||
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
|
||||
_characterController.setLocalBoundingBox(corner, scale);
|
||||
corner += scale * _skeletonModel.getBoundingCapsuleOffset();
|
||||
glm::vec3 diagonal(2.0f * radius, height, 2.0f * radius);
|
||||
_characterController.setLocalBoundingBox(corner, diagonal);
|
||||
}
|
||||
|
||||
void MyAvatar::prepareForPhysicsSimulation() {
|
||||
|
@ -1177,7 +1175,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
|
|||
if (!_skeletonModel.isRenderable()) {
|
||||
return; // wait until all models are loaded
|
||||
}
|
||||
|
||||
|
||||
fixupModelsInScene();
|
||||
|
||||
// Render head so long as the camera isn't inside it
|
||||
|
@ -1331,7 +1329,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
|
|||
bool MyAvatar::cameraInsideHead() const {
|
||||
const Head* head = getHead();
|
||||
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getAvatarScale());
|
||||
return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
|
||||
}
|
||||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||
|
@ -1455,11 +1453,11 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
if (isHovering) {
|
||||
// we're flying --> complex acceleration curve with high max speed
|
||||
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
||||
float finalMaxMotorSpeed = getAvatarScale() * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float finalMaxMotorSpeed = getUniformScale() * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float speedGrowthTimescale = 2.0f;
|
||||
float speedIncreaseFactor = 1.8f;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = getAvatarScale() * MAX_BOOST_SPEED;
|
||||
const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED;
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
|
|
|
@ -249,7 +249,7 @@ public slots:
|
|||
|
||||
Q_INVOKABLE void updateMotionBehaviorFromMenu();
|
||||
|
||||
virtual void rebuildSkeletonBody() override;
|
||||
virtual void rebuildCollisionShape() override;
|
||||
|
||||
Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; }
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ MyCharacterController::~MyCharacterController() {
|
|||
|
||||
void MyCharacterController::updateShapeIfNecessary() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
||||
_pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE;
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_SHAPE;
|
||||
|
||||
// compute new dimensions from avatar's bounding box
|
||||
float x = _boxScale.x;
|
||||
|
|
|
@ -69,7 +69,7 @@ void SkeletonModel::initJointStates() {
|
|||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||
_headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP);
|
||||
|
||||
_owningAvatar->rebuildSkeletonBody();
|
||||
_owningAvatar->rebuildCollisionShape();
|
||||
emit skeletonLoaded();
|
||||
}
|
||||
|
||||
|
@ -338,35 +338,38 @@ void SkeletonModel::computeBoundingShape() {
|
|||
return;
|
||||
}
|
||||
|
||||
_rig->computeAvatarBoundingCapsule(geometry,
|
||||
_boundingCapsuleRadius,
|
||||
_boundingCapsuleHeight,
|
||||
_boundingCapsuleLocalOffset);
|
||||
float radius, height;
|
||||
glm::vec3 offset;
|
||||
_rig->computeAvatarBoundingCapsule(geometry, radius, height, offset);
|
||||
float invScale = 1.0f / _owningAvatar->getUniformScale();
|
||||
_boundingCapsuleRadius = invScale * radius;
|
||||
_boundingCapsuleHeight = invScale * height;
|
||||
_boundingCapsuleLocalOffset = invScale * offset;
|
||||
}
|
||||
|
||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
|
||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float scale, float alpha) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
// draw a blue sphere at the capsule top point
|
||||
glm::vec3 topPoint = _translation + getRotation() * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::vec3 topPoint = _translation + getRotation() * (scale * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * Vectors::UNIT_Y));
|
||||
|
||||
deferredLighting->renderSolidSphereInstance(batch,
|
||||
Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius),
|
||||
Transform().setTranslation(topPoint).postScale(scale * _boundingCapsuleRadius),
|
||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, scale * _boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 axis = topPoint - bottomPoint;
|
||||
|
||||
deferredLighting->renderSolidSphereInstance(batch,
|
||||
Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius),
|
||||
Transform().setTranslation(bottomPoint).postScale(scale * _boundingCapsuleRadius),
|
||||
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.0f);
|
||||
batch.setModelTransform(Transform().setTranslation(bottomPoint));
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius,
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, scale * _boundingCapsuleRadius, scale * _boundingCapsuleRadius,
|
||||
glm::vec4(0.6f, 0.8f, 0.6f, alpha));
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
/// \return whether or not the head was found.
|
||||
glm::vec3 getDefaultEyeModelPosition() const;
|
||||
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float scale, float alpha);
|
||||
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
||||
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
||||
const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; }
|
||||
|
|
|
@ -84,6 +84,19 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
|
|||
return result;
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList,
|
||||
const QString& selected) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup",
|
||||
Q_ARG(const QString&, groupName),
|
||||
Q_ARG(const QStringList&, actionList),
|
||||
Q_ARG(const QString&, selected));
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::removeActionGroup(const QString& groupName) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "removeActionGroup",
|
||||
Q_ARG(const QString&, groupName));
|
||||
}
|
||||
|
||||
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
||||
|
|
|
@ -42,6 +42,10 @@ public slots:
|
|||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList,
|
||||
const QString& selected = QString());
|
||||
void removeActionGroup(const QString& groupName);
|
||||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
static const quint64 MSECS_TO_USECS = 1000ULL;
|
||||
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||
|
||||
static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
|
||||
static const float reticleSize = TWO_PI / 100.0f;
|
||||
|
||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||
|
@ -386,7 +385,7 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
|
|||
glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
|
||||
|
||||
float t;
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getAvatarScale(), &t)){
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getUniformScale(), &t)){
|
||||
result = position + direction * t;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ void PreferencesDialog::loadPreferences() {
|
|||
|
||||
ui.leanScaleSpin->setValue(myAvatar->getLeanScale());
|
||||
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getAvatarScale());
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getUniformScale());
|
||||
ui.avatarAnimationEdit->setText(myAvatar->getAnimGraphUrl().toString());
|
||||
|
||||
ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
|
||||
|
@ -256,7 +256,7 @@ void PreferencesDialog::savePreferences() {
|
|||
|
||||
myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum());
|
||||
myAvatar->setLeanScale(ui.leanScaleSpin->value());
|
||||
myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value());
|
||||
myAvatar->setTargetScaleVerbose(ui.avatarScaleSpin->value());
|
||||
if (myAvatar->getAnimGraphUrl() != ui.avatarAnimationEdit->text()) { // If changed, destroy the old and start with the new
|
||||
myAvatar->setAnimGraphUrl(ui.avatarAnimationEdit->text());
|
||||
}
|
||||
|
|
|
@ -26,13 +26,15 @@
|
|||
QString const Image3DOverlay::TYPE = "image3d";
|
||||
|
||||
Image3DOverlay::Image3DOverlay() {
|
||||
_isLoaded = false;
|
||||
_isLoaded = false;
|
||||
_emissive = false;
|
||||
}
|
||||
|
||||
Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) :
|
||||
Billboard3DOverlay(image3DOverlay),
|
||||
_url(image3DOverlay->_url),
|
||||
_texture(image3DOverlay->_texture),
|
||||
_emissive(image3DOverlay->_emissive),
|
||||
_fromImage(image3DOverlay->_fromImage)
|
||||
{
|
||||
}
|
||||
|
@ -93,8 +95,8 @@ void Image3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
batch->setModelTransform(transform);
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, false, false, true);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, false, _emissive, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)
|
||||
|
@ -144,6 +146,11 @@ void Image3DOverlay::setProperties(const QScriptValue &properties) {
|
|||
setClipFromSource(subImageRect);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue emissiveValue = properties.property("emissive");
|
||||
if (emissiveValue.isValid()) {
|
||||
_emissive = emissiveValue.toBool();
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue Image3DOverlay::getProperty(const QString& property) {
|
||||
|
@ -156,6 +163,9 @@ QScriptValue Image3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "offsetPosition") {
|
||||
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
|
||||
}
|
||||
if (property == "emissive") {
|
||||
return _emissive;
|
||||
}
|
||||
|
||||
return Billboard3DOverlay::getProperty(property);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
private:
|
||||
QString _url;
|
||||
NetworkTexturePointer _texture;
|
||||
bool _emissive;
|
||||
|
||||
QRect _fromImage; // where from in the image to sample
|
||||
};
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace render {
|
|||
glm::vec3 myAvatarPosition = avatar->getPosition();
|
||||
float angle = glm::degrees(glm::angle(myAvatarRotation));
|
||||
glm::vec3 axis = glm::axis(myAvatarRotation);
|
||||
float myAvatarScale = avatar->getAvatarScale();
|
||||
float myAvatarScale = avatar->getUniformScale();
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(myAvatarPosition);
|
||||
transform.setRotation(glm::angleAxis(angle, axis));
|
||||
|
|
|
@ -122,5 +122,5 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) {
|
|||
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children.front());
|
||||
assert(clipNode);
|
||||
const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f;
|
||||
_phase = fmodf(frame, NUM_FRAMES);
|
||||
_phase = fmodf(frame / NUM_FRAMES, 1.0f);
|
||||
}
|
||||
|
|
|
@ -140,6 +140,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
|||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DUMP_FBX_JOINTS
|
||||
void AnimSkeleton::dump() const {
|
||||
qCDebug(animation) << "[";
|
||||
for (int i = 0; i < getNumJoints(); i++) {
|
||||
|
@ -151,21 +152,22 @@ void AnimSkeleton::dump() const {
|
|||
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
|
||||
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
||||
#ifdef DUMP_FBX_JOINTS
|
||||
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
||||
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
||||
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
||||
qCDebug(animation) << " translation =" << _joints[i].translation;
|
||||
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
||||
qCDebug(animation) << " preRotation =" << _joints[i].preRotation;
|
||||
qCDebug(animation) << " rotation =" << _joints[i].rotation;
|
||||
qCDebug(animation) << " postRotation =" << _joints[i].postRotation;
|
||||
qCDebug(animation) << " postTransform =" << _joints[i].postTransform;
|
||||
qCDebug(animation) << " transform =" << _joints[i].transform;
|
||||
qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax;
|
||||
qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation;
|
||||
qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation;
|
||||
qCDebug(animation) << " bindTransform" << _joints[i].bindTransform;
|
||||
qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint;
|
||||
qCDebug(animation) << " fbxJoint =";
|
||||
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
||||
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
||||
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
||||
qCDebug(animation) << " translation =" << _joints[i].translation;
|
||||
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
||||
qCDebug(animation) << " preRotation =" << _joints[i].preRotation;
|
||||
qCDebug(animation) << " rotation =" << _joints[i].rotation;
|
||||
qCDebug(animation) << " postRotation =" << _joints[i].postRotation;
|
||||
qCDebug(animation) << " postTransform =" << _joints[i].postTransform;
|
||||
qCDebug(animation) << " transform =" << _joints[i].transform;
|
||||
qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax;
|
||||
qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation;
|
||||
qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation;
|
||||
qCDebug(animation) << " bindTransform" << _joints[i].bindTransform;
|
||||
qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint;
|
||||
#endif
|
||||
if (getParentIndex(i) >= 0) {
|
||||
qCDebug(animation) << " parent =" << getJointName(getParentIndex(i));
|
||||
|
|
|
@ -289,7 +289,6 @@ uint64_t AudioInjector::injectNextFrame() {
|
|||
_currentSendOffset = 0;
|
||||
} else {
|
||||
// we weren't to loop, say that we're done now
|
||||
qDebug() << "AudioInjector::injectNextFrame has sent all data and was not asked to loop - calling finish().";
|
||||
finish();
|
||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() {
|
|||
// There are a number of possible strategies for this set of tools through endRender, below.
|
||||
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||
avatarLock.lock();
|
||||
Transform trans;
|
||||
Transform trans = getTransform();
|
||||
trans.setTranslation(position);
|
||||
trans.setRotation(orientation);
|
||||
SpatiallyNestable::setTransform(trans);
|
||||
|
@ -129,13 +129,10 @@ float AvatarData::getTargetScale() const {
|
|||
}
|
||||
|
||||
void AvatarData::setTargetScale(float targetScale) {
|
||||
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale));
|
||||
_targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
}
|
||||
|
||||
void AvatarData::setClampedTargetScale(float targetScale) {
|
||||
|
||||
targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
|
||||
void AvatarData::setTargetScaleVerbose(float targetScale) {
|
||||
setTargetScale(targetScale);
|
||||
qCDebug(avatars) << "Changed scale to " << _targetScale;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ public:
|
|||
|
||||
AvatarData();
|
||||
virtual ~AvatarData();
|
||||
|
||||
|
||||
static const QUrl& defaultFullAvatarModelUrl();
|
||||
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
|
@ -237,7 +237,7 @@ public:
|
|||
// Scale
|
||||
float getTargetScale() const;
|
||||
void setTargetScale(float targetScale);
|
||||
void setClampedTargetScale(float targetScale);
|
||||
void setTargetScaleVerbose(float targetScale);
|
||||
|
||||
// Hand State
|
||||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||
|
@ -261,7 +261,7 @@ public:
|
|||
Q_INVOKABLE bool isJointDataValid(const QString& name) const;
|
||||
Q_INVOKABLE glm::quat getJointRotation(const QString& name) const;
|
||||
Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const;
|
||||
|
||||
|
||||
Q_INVOKABLE virtual QVector<glm::quat> getJointRotations() const;
|
||||
Q_INVOKABLE virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
Q_INVOKABLE virtual void setJointTranslations(QVector<glm::vec3> jointTranslations);
|
||||
|
@ -342,6 +342,9 @@ public:
|
|||
|
||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
||||
|
||||
void die() { _isDead = true; }
|
||||
bool isDead() const { return _isDead; }
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
@ -413,6 +416,8 @@ protected:
|
|||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition;
|
||||
|
||||
bool _isDead { false };
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
static QUrl _defaultFullAvatarModelUrl;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <GeometryUtil.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include "AvatarData.h"
|
||||
#include "HandData.h"
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ PalmData& HandData::addNewPalm(Hand whichHand) {
|
|||
PalmData HandData::getCopyOfPalmData(Hand hand) const {
|
||||
QReadLocker locker(&_palmsLock);
|
||||
|
||||
// the palms are not necessarily added in left-right order,
|
||||
// the palms are not necessarily added in left-right order,
|
||||
// so we have to search for the correct hand
|
||||
for (const auto& palm : _palms) {
|
||||
if (palm.whichHand() == hand && palm.isActive()) {
|
||||
|
@ -64,7 +64,7 @@ void PalmData::addToPosition(const glm::vec3& delta) {
|
|||
_rawPosition += _owningHandData->worldToLocalVector(delta);
|
||||
}
|
||||
|
||||
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||
const PalmData*& collidingPalm) const {
|
||||
QReadLocker locker(&_palmsLock);
|
||||
|
||||
|
@ -93,7 +93,7 @@ glm::vec3 HandData::getBasePosition() const {
|
|||
float HandData::getBaseScale() const {
|
||||
return _owningAvatarData->getTargetScale();
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 PalmData::getFingerDirection() const {
|
||||
// finger points along yAxis in hand-frame
|
||||
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f);
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
|
||||
HandData(AvatarData* owningAvatar);
|
||||
virtual ~HandData() {}
|
||||
|
||||
|
||||
// position conversion
|
||||
glm::vec3 localToWorldPosition(const glm::vec3& localPosition) {
|
||||
return getBasePosition() + getBaseOrientation() * localPosition * getBaseScale();
|
||||
|
@ -60,7 +60,7 @@ public:
|
|||
/// \param penetration[out] the vector in which to store the penetration
|
||||
/// \param collidingPalm[out] a const PalmData* to the palm that was collided with
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||
const PalmData*& collidingPalm) const;
|
||||
|
||||
glm::quat getBaseOrientation() const;
|
||||
|
@ -74,7 +74,7 @@ protected:
|
|||
AvatarData* _owningAvatarData;
|
||||
std::vector<PalmData> _palms;
|
||||
mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
|
||||
|
||||
|
||||
glm::vec3 getBasePosition() const;
|
||||
float getBaseScale() const;
|
||||
|
||||
|
@ -112,13 +112,12 @@ public:
|
|||
|
||||
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
|
||||
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
|
||||
glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); }
|
||||
|
||||
void addToPosition(const glm::vec3& delta);
|
||||
|
||||
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
|
||||
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.0f); }
|
||||
|
||||
|
||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
||||
const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); }
|
||||
const glm::vec3& getTipRawPosition() const { return _tipPosition; }
|
||||
|
@ -126,16 +125,16 @@ public:
|
|||
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
|
||||
const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); }
|
||||
const glm::vec3& getTipRawVelocity() const { return _tipVelocity; }
|
||||
|
||||
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
|
||||
|
||||
// FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
|
||||
// from an action and/or the UserInputMapper instead of piping it through here.
|
||||
void setTrigger(float trigger) { _trigger = trigger; }
|
||||
float getTrigger() const { return _trigger; }
|
||||
|
||||
|
||||
// return world-frame:
|
||||
glm::vec3 getFingerDirection() const;
|
||||
glm::vec3 getNormal() const;
|
||||
|
@ -148,13 +147,13 @@ private:
|
|||
glm::vec3 _rawAngularVelocity;
|
||||
glm::quat _rawDeltaRotation;
|
||||
glm::quat _lastRotation;
|
||||
|
||||
|
||||
glm::vec3 _tipPosition;
|
||||
glm::vec3 _tipVelocity;
|
||||
glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
|
||||
|
||||
float _trigger;
|
||||
|
||||
|
||||
bool _isActive; /// This has current valid data
|
||||
int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
|
||||
HandData* _owningHandData;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
namespace controller {
|
||||
|
||||
Pose::Pose(const vec3& translation, const quat& rotation,
|
||||
const vec3& velocity, const quat& angularVelocity) :
|
||||
const vec3& velocity, const vec3& angularVelocity) :
|
||||
translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { }
|
||||
|
||||
bool Pose::operator==(const Pose& right) const {
|
||||
|
@ -26,7 +26,7 @@ namespace controller {
|
|||
}
|
||||
|
||||
// FIXME add margin of error? Or add an additional withinEpsilon function?
|
||||
return translation == right.getTranslation() && rotation == right.getRotation() &&
|
||||
return translation == right.getTranslation() && rotation == right.getRotation() &&
|
||||
velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity();
|
||||
}
|
||||
|
||||
|
@ -35,14 +35,29 @@ namespace controller {
|
|||
obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
|
||||
obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation));
|
||||
obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
|
||||
obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity));
|
||||
obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity));
|
||||
obj.setProperty("valid", pose.valid);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) {
|
||||
// nothing for now...
|
||||
auto translation = object.property("translation");
|
||||
auto rotation = object.property("rotation");
|
||||
auto velocity = object.property("velocity");
|
||||
auto angularVelocity = object.property("angularVelocity");
|
||||
if (translation.isValid() &&
|
||||
rotation.isValid() &&
|
||||
velocity.isValid() &&
|
||||
angularVelocity.isValid()) {
|
||||
vec3FromScriptValue(translation, pose.translation);
|
||||
quatFromScriptValue(rotation, pose.rotation);
|
||||
vec3FromScriptValue(velocity, pose.velocity);
|
||||
vec3FromScriptValue(angularVelocity, pose.angularVelocity);
|
||||
pose.valid = true;
|
||||
} else {
|
||||
pose.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue