mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 19:23:58 +02:00
Merge remote-tracking branch 'upstream/master' into 19734
Conflicts: interface/CMakeLists.txt interface/src/avatar/SkeletonModel.cpp
This commit is contained in:
commit
abacefa723
101 changed files with 3287 additions and 467 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -50,5 +50,9 @@ interface/external/faceplus/*
|
|||
interface/external/priovr/*
|
||||
!interface/external/priovr/readme.txt
|
||||
|
||||
# Ignore RtMidi
|
||||
interface/external/rtmidi/*
|
||||
!interface/external/rtmidi/readme.txt
|
||||
|
||||
# Ignore interfaceCache for Linux users
|
||||
interface/interfaceCache/
|
||||
|
|
|
@ -10,6 +10,7 @@ add_definitions(-DGLM_FORCE_RADIANS)
|
|||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")
|
||||
|
|
|
@ -12,6 +12,9 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
|||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
||||
|
||||
find_package(Qt5 COMPONENTS Script)
|
||||
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||
|
||||
# set up the external glm library
|
||||
include("${MACRO_DIR}/IncludeGLM.cmake")
|
||||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
@ -35,4 +38,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
|||
# add a definition for ssize_t so that windows doesn't bail
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkDiskCache>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <AvatarData.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <ResourceCache.h>
|
||||
|
@ -208,12 +208,14 @@ void Agent::run() {
|
|||
scriptURL = QUrl(_payload);
|
||||
}
|
||||
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply *reply = networkManager->get(QNetworkRequest(scriptURL));
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache(networkManager);
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL));
|
||||
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
|
||||
networkManager->setCache(cache);
|
||||
cache->moveToThread(networkAccessManager.thread());
|
||||
networkAccessManager.setCache(cache);
|
||||
|
||||
qDebug() << "Downloading script at" << scriptURL.toString();
|
||||
|
||||
|
@ -222,10 +224,6 @@ void Agent::run() {
|
|||
|
||||
loop.exec();
|
||||
|
||||
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager
|
||||
AvatarData::setNetworkAccessManager(networkManager);
|
||||
ResourceCache::setNetworkAccessManager(networkManager);
|
||||
|
||||
QString scriptContents(reply->readAll());
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents;
|
||||
|
@ -272,4 +270,5 @@ void Agent::run() {
|
|||
|
||||
void Agent::aboutToFinish() {
|
||||
_scriptEngine.stop();
|
||||
NetworkAccessManager::getInstance().clearAccessCache();
|
||||
}
|
||||
|
|
|
@ -104,6 +104,8 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
// connections to AccountManager for authentication
|
||||
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
|
||||
this, &AssignmentClient::handleAuthenticationRequest);
|
||||
|
||||
NetworkAccessManager::getInstance();
|
||||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
|
|
|
@ -38,11 +38,11 @@
|
|||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
@ -89,6 +89,10 @@ AudioMixer::~AudioMixer() {
|
|||
delete _listenerUnattenuatedZone;
|
||||
}
|
||||
|
||||
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
|
||||
const float ATTENUATION_AMOUNT_PER_DOUBLING_IN_DISTANCE = 0.18f;
|
||||
const float ATTENUATION_EPSILON_DISTANCE = 0.1f;
|
||||
|
||||
void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,
|
||||
AvatarAudioRingBuffer* listeningNodeBuffer) {
|
||||
float bearingRelativeAngleToSource = 0.0f;
|
||||
|
@ -99,11 +103,12 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
|
|||
bool shouldAttenuate = (bufferToAdd != listeningNodeBuffer);
|
||||
|
||||
if (shouldAttenuate) {
|
||||
|
||||
// if the two buffer pointers do not match then these are different buffers
|
||||
glm::vec3 relativePosition = bufferToAdd->getPosition() - listeningNodeBuffer->getPosition();
|
||||
|
||||
float distanceBetween = glm::length(relativePosition);
|
||||
|
||||
|
||||
if (distanceBetween < EPSILON) {
|
||||
distanceBetween = EPSILON;
|
||||
}
|
||||
|
@ -120,6 +125,12 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
|
|||
shouldAttenuate = !bufferToAdd->getListenerUnattenuatedZone()->contains(listeningNodeBuffer->getPosition());
|
||||
}
|
||||
|
||||
if (bufferToAdd->getType() == PositionalAudioRingBuffer::Injector) {
|
||||
attenuationCoefficient *= reinterpret_cast<InjectedAudioRingBuffer*>(bufferToAdd)->getAttenuationRatio();
|
||||
}
|
||||
|
||||
shouldAttenuate = shouldAttenuate && distanceBetween > ATTENUATION_EPSILON_DISTANCE;
|
||||
|
||||
if (shouldAttenuate) {
|
||||
glm::quat inverseOrientation = glm::inverse(listeningNodeBuffer->getOrientation());
|
||||
|
||||
|
@ -127,9 +138,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
|
|||
float radius = 0.0f;
|
||||
|
||||
if (bufferToAdd->getType() == PositionalAudioRingBuffer::Injector) {
|
||||
InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) bufferToAdd;
|
||||
radius = injectedBuffer->getRadius();
|
||||
attenuationCoefficient *= injectedBuffer->getAttenuationRatio();
|
||||
radius = reinterpret_cast<InjectedAudioRingBuffer*>(bufferToAdd)->getRadius();
|
||||
}
|
||||
|
||||
if (radius == 0 || (distanceSquareToSource > radius * radius)) {
|
||||
|
@ -154,7 +163,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
|
|||
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
|
||||
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
|
||||
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / PI_OVER_TWO));
|
||||
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / PI_OVER_TWO));
|
||||
|
||||
// multiply the current attenuation coefficient by the calculated off axis coefficient
|
||||
attenuationCoefficient *= offAxisCoefficient;
|
||||
|
@ -162,19 +171,18 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
|
|||
|
||||
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
|
||||
|
||||
const float DISTANCE_SCALE = 2.5f;
|
||||
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
|
||||
const float DISTANCE_LOG_BASE = 2.5f;
|
||||
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
|
||||
|
||||
// calculate the distance coefficient using the distance to this node
|
||||
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
|
||||
DISTANCE_SCALE_LOG +
|
||||
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient);
|
||||
|
||||
// multiply the current attenuation coefficient by the distance coefficient
|
||||
attenuationCoefficient *= distanceCoefficient;
|
||||
if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
||||
// calculate the distance coefficient using the distance to this node
|
||||
float distanceCoefficient = 1 - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f)
|
||||
* ATTENUATION_AMOUNT_PER_DOUBLING_IN_DISTANCE);
|
||||
|
||||
if (distanceCoefficient < 0) {
|
||||
distanceCoefficient = 0;
|
||||
}
|
||||
|
||||
// multiply the current attenuation coefficient by the distance coefficient
|
||||
attenuationCoefficient *= distanceCoefficient;
|
||||
}
|
||||
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
rotatedSourcePosition.y = 0.0f;
|
||||
|
@ -482,8 +490,8 @@ void AudioMixer::run() {
|
|||
|
||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||
|
||||
// setup a QNetworkAccessManager to ask the domain-server for our settings
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
// setup a NetworkAccessManager to ask the domain-server for our settings
|
||||
NetworkAccessManager& networkManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl settingsJSONURL;
|
||||
settingsJSONURL.setScheme("http");
|
||||
|
@ -500,7 +508,7 @@ void AudioMixer::run() {
|
|||
qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString();
|
||||
|
||||
while (!reply || reply->error() != QNetworkReply::NoError) {
|
||||
reply = networkManager->get(QNetworkRequest(settingsJSONURL));
|
||||
reply = networkManager.get(QNetworkRequest(settingsJSONURL));
|
||||
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
|
|
@ -101,8 +101,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
|||
|
||||
if (!matchingInjectedRingBuffer) {
|
||||
// we don't have a matching injected audio ring buffer, so add it
|
||||
matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier,
|
||||
AudioMixer::getUseDynamicJitterBuffers());
|
||||
matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier, AudioMixer::getUseDynamicJitterBuffers());
|
||||
_ringBuffers.push_back(matchingInjectedRingBuffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -216,9 +216,13 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
}
|
||||
|
||||
const SharedNodePointer& destinationNode = NodeList::getInstance()->getNodeHash().value(nodeUUID);
|
||||
const QSet<unsigned short int>& missingSequenceNumbers = nodeStats.getIncomingEditSequenceNumberStats().getMissingSet();
|
||||
|
||||
// retrieve sequence number stats of node, prune its missing set
|
||||
SequenceNumberStats& sequenceNumberStats = nodeStats.getIncomingEditSequenceNumberStats();
|
||||
sequenceNumberStats.pruneMissingSet();
|
||||
|
||||
// construct nack packet(s) for this node
|
||||
const QSet<unsigned short int>& missingSequenceNumbers = sequenceNumberStats.getMissingSet();
|
||||
int numSequenceNumbersAvailable = missingSequenceNumbers.size();
|
||||
QSet<unsigned short int>::const_iterator missingSequenceNumberIterator = missingSequenceNumbers.constBegin();
|
||||
while (numSequenceNumbersAvailable > 0) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
|
||||
|
||||
const SequenceNumberStats& getIncomingEditSequenceNumberStats() const { return _incomingEditSequenceNumberStats; }
|
||||
SequenceNumberStats& getIncomingEditSequenceNumberStats() { return _incomingEditSequenceNumberStats; }
|
||||
|
||||
void trackInboundPacket(unsigned short int incomingSequence, quint64 transitTime,
|
||||
int editsInPacket, quint64 processTime, quint64 lockWaitTime);
|
||||
|
|
|
@ -42,7 +42,7 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_lastRootTimestamp(0),
|
||||
_myPacketType(PacketTypeUnknown),
|
||||
_isShuttingDown(false),
|
||||
_sentPacketHistory(1000)
|
||||
_sentPacketHistory()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
//
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
|
||||
|
|
33
cmake/modules/FindRtMidi.cmake
Normal file
33
cmake/modules/FindRtMidi.cmake
Normal file
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# FindRtMidd.cmake
|
||||
#
|
||||
# Try to find the RtMidi library
|
||||
#
|
||||
# You can provide a RTMIDI_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# RTMIDI_FOUND - system found RtMidi
|
||||
# RTMIDI_INCLUDE_DIRS - the RtMidi include directory
|
||||
# RTMIDI_CPP - Include this with src to use RtMidi
|
||||
#
|
||||
# Created on 6/30/2014 by Stephen Birarda
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
if (RTMIDI_LIBRARIES AND RTMIDI_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(RTMIDI_FOUND TRUE)
|
||||
else ()
|
||||
|
||||
set(RTMIDI_SEARCH_DIRS "${RTMIDI_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/rtmidi")
|
||||
|
||||
find_path(RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
find_file(RTMIDI_CPP NAMES RtMidi.cpp PATH_SUFFIXES src HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIR RTMIDI_CPP)
|
||||
endif ()
|
|
@ -51,8 +51,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
|
||||
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
|
||||
// we either read a certificate and private key or were not passed one
|
||||
// and completed login or did not need to
|
||||
|
@ -1196,7 +1194,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
|||
QNetworkRequest tokenRequest(tokenRequestUrl);
|
||||
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||
|
||||
qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID);
|
||||
|
||||
|
@ -1233,7 +1231,7 @@ void DomainServer::handleTokenRequestFinished() {
|
|||
profileURL.setPath("/api/v1/users/profile");
|
||||
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
|
||||
|
||||
QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL));
|
||||
QNetworkReply* profileReply = NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL));
|
||||
|
||||
qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID);
|
||||
|
||||
|
|
|
@ -103,8 +103,6 @@ private:
|
|||
|
||||
bool _isUsingDTLS;
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
|
||||
QUrl _oauthProviderURL;
|
||||
QString _oauthClientID;
|
||||
QString _oauthClientSecret;
|
||||
|
|
|
@ -36,9 +36,12 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
|||
|
||||
// load the existing config file to get the current values
|
||||
QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_CONFIG_FILE_RELATIVE_PATH);
|
||||
configFile.open(QIODevice::ReadOnly);
|
||||
|
||||
_settingsMap = QJsonDocument::fromJson(configFile.readAll()).toVariant().toMap();
|
||||
if (configFile.exists()) {
|
||||
configFile.open(QIODevice::ReadOnly);
|
||||
|
||||
_settingsMap = QJsonDocument::fromJson(configFile.readAll()).toVariant().toMap();
|
||||
}
|
||||
}
|
||||
|
||||
const QString DESCRIPTION_SETTINGS_KEY = "settings";
|
||||
|
|
32
examples/animationStateExample.js
Normal file
32
examples/animationStateExample.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// animationStateExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that runs in a loop and displays a counter to the log
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
|
||||
function displayAnimationDetails(deltaTime) {
|
||||
print("count =" + count + " deltaTime=" + deltaTime);
|
||||
count++;
|
||||
var animationDetails = MyAvatar.getAnimationDetailsByRole("idle");
|
||||
print("animationDetails.frameIndex=" + animationDetails.frameIndex);
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
print("SCRIPT ENDNG!!!\n");
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(displayAnimationDetails);
|
||||
|
||||
// register our scriptEnding callback
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -19,6 +19,7 @@ set(PRIOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/priovr")
|
|||
set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense")
|
||||
set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage")
|
||||
set(LEAPMOTION_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/leapmotion")
|
||||
set(RTMIDI_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/rtmidi")
|
||||
|
||||
find_package(Qt5LinguistTools REQUIRED)
|
||||
find_package(Qt5LinguistToolsMacros)
|
||||
|
@ -111,6 +112,16 @@ if (APPLE)
|
|||
SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/interface.icns")
|
||||
endif()
|
||||
|
||||
# RtMidi for scripted MIDI control
|
||||
find_package(RtMidi)
|
||||
|
||||
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI)
|
||||
add_definitions(-DHAVE_RTMIDI)
|
||||
include_directories(SYSTEM ${RTMIDI_INCLUDE_DIR})
|
||||
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${RTMIDI_CPP}")
|
||||
endif ()
|
||||
|
||||
# create the executable, make it a bundle on OS X
|
||||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
|
||||
|
||||
|
@ -145,58 +156,58 @@ find_package(Qxmpp)
|
|||
|
||||
# include the Sixense library for Razer Hydra if available
|
||||
if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
|
||||
add_definitions(-DHAVE_SIXENSE)
|
||||
include_directories(SYSTEM "${SIXENSE_INCLUDE_DIRS}")
|
||||
if (APPLE OR UNIX)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}")
|
||||
endif (APPLE OR UNIX)
|
||||
target_link_libraries(${TARGET_NAME} "${SIXENSE_LIBRARIES}")
|
||||
add_definitions(-DHAVE_SIXENSE)
|
||||
include_directories(SYSTEM "${SIXENSE_INCLUDE_DIRS}")
|
||||
if (APPLE OR UNIX)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}")
|
||||
endif (APPLE OR UNIX)
|
||||
target_link_libraries(${TARGET_NAME} "${SIXENSE_LIBRARIES}")
|
||||
endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
|
||||
|
||||
# likewise with Visage library for webcam feature tracking
|
||||
if (VISAGE_FOUND AND NOT DISABLE_VISAGE)
|
||||
add_definitions(-DHAVE_VISAGE -DVISAGE_STATIC)
|
||||
include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}")
|
||||
if (APPLE)
|
||||
add_definitions(-DMAC_OS_X)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment")
|
||||
include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}")
|
||||
find_library(AVFoundation AVFoundation)
|
||||
find_library(CoreMedia CoreMedia)
|
||||
find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/)
|
||||
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
|
||||
endif (APPLE)
|
||||
target_link_libraries(${TARGET_NAME} "${VISAGE_LIBRARIES}")
|
||||
add_definitions(-DHAVE_VISAGE -DVISAGE_STATIC)
|
||||
include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}")
|
||||
if (APPLE)
|
||||
add_definitions(-DMAC_OS_X)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment")
|
||||
include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}")
|
||||
find_library(AVFoundation AVFoundation)
|
||||
find_library(CoreMedia CoreMedia)
|
||||
find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/)
|
||||
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
|
||||
endif (APPLE)
|
||||
target_link_libraries(${TARGET_NAME} "${VISAGE_LIBRARIES}")
|
||||
endif (VISAGE_FOUND AND NOT DISABLE_VISAGE)
|
||||
|
||||
# and with Faceplus library, also for webcam feature tracking
|
||||
if (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS)
|
||||
add_definitions(-DHAVE_FACEPLUS)
|
||||
include_directories(SYSTEM "${FACEPLUS_INCLUDE_DIRS}")
|
||||
target_link_libraries(${TARGET_NAME} "${FACEPLUS_LIBRARIES}")
|
||||
add_definitions(-DHAVE_FACEPLUS)
|
||||
include_directories(SYSTEM "${FACEPLUS_INCLUDE_DIRS}")
|
||||
target_link_libraries(${TARGET_NAME} "${FACEPLUS_LIBRARIES}")
|
||||
endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS)
|
||||
|
||||
# and with LibOVR for Oculus Rift
|
||||
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
||||
add_definitions(-DHAVE_LIBOVR)
|
||||
include_directories(SYSTEM "${LIBOVR_INCLUDE_DIRS}")
|
||||
add_definitions(-DHAVE_LIBOVR)
|
||||
include_directories(SYSTEM "${LIBOVR_INCLUDE_DIRS}")
|
||||
|
||||
if (APPLE OR UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (APPLE OR UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(${TARGET_NAME} optimized "${LIBOVR_RELEASE_LIBRARIES}" debug "${LIBOVR_DEBUG_LIBRARIES}")
|
||||
else()
|
||||
target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}")
|
||||
endif()
|
||||
if (WIN32)
|
||||
target_link_libraries(${TARGET_NAME} optimized "${LIBOVR_RELEASE_LIBRARIES}" debug "${LIBOVR_DEBUG_LIBRARIES}")
|
||||
else ()
|
||||
target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}")
|
||||
endif ()
|
||||
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
||||
|
||||
# and with PrioVR library
|
||||
if (PRIOVR_FOUND AND NOT DISABLE_PRIOVR)
|
||||
add_definitions(-DHAVE_PRIOVR)
|
||||
include_directories(SYSTEM "${PRIOVR_INCLUDE_DIRS}")
|
||||
target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}")
|
||||
add_definitions(-DHAVE_PRIOVR)
|
||||
include_directories(SYSTEM "${PRIOVR_INCLUDE_DIRS}")
|
||||
target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}")
|
||||
endif (PRIOVR_FOUND AND NOT DISABLE_PRIOVR)
|
||||
|
||||
# and with LeapMotion library
|
||||
|
@ -208,19 +219,26 @@ endif (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION)
|
|||
|
||||
# and with SDL for joysticks
|
||||
if (SDL_FOUND AND NOT DISABLE_SDL)
|
||||
add_definitions(-DHAVE_SDL)
|
||||
include_directories(SYSTEM "${SDL_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${SDL_LIBRARY}")
|
||||
add_definitions(-DHAVE_SDL)
|
||||
include_directories(SYSTEM "${SDL_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${SDL_LIBRARY}")
|
||||
endif (SDL_FOUND AND NOT DISABLE_SDL)
|
||||
|
||||
# and with qxmpp for chat
|
||||
if (QXMPP_FOUND AND NOT DISABLE_QXMPP)
|
||||
add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC)
|
||||
include_directories(SYSTEM ${QXMPP_INCLUDE_DIR})
|
||||
add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC)
|
||||
include_directories(SYSTEM ${QXMPP_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${QXMPP_LIBRARY}")
|
||||
target_link_libraries(${TARGET_NAME} "${QXMPP_LIBRARY}")
|
||||
endif (QXMPP_FOUND AND NOT DISABLE_QXMPP)
|
||||
|
||||
# link CoreMIDI if we're using RtMidi
|
||||
if (RTMIDI_FOUND AND APPLE)
|
||||
find_library(CoreMIDI CoreMIDI)
|
||||
add_definitions(-D__MACOSX_CORE__)
|
||||
target_link_libraries(${TARGET_NAME} ${CoreMIDI})
|
||||
endif()
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes")
|
||||
|
||||
|
|
5
interface/external/oculus/readme.txt
vendored
5
interface/external/oculus/readme.txt
vendored
|
@ -10,4 +10,7 @@ You can download the Oculus SDK from https://developer.oculusvr.com/ (account cr
|
|||
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
|
||||
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'oculus' that contains the three folders mentioned above.
|
||||
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
NOTE: For Windows users, you should copy libovr.lib and libovrd.lib from the \oculus\Lib\Win32\VS2010 directory to the \oculus\Lib\Win32\ directory.
|
||||
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
|
||||
|
|
41
interface/external/rtmidi/readme.txt
vendored
Normal file
41
interface/external/rtmidi/readme.txt
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
Instructions for adding the RtMidi library to Interface
|
||||
Stephen Birarda, June 30, 2014
|
||||
|
||||
1. Download the RtMidi tarball from High Fidelity S3.
|
||||
http://highfidelity-public.s3.amazonaws.com/dependencies/rtmidi-2.1.0.tar.gz
|
||||
|
||||
2. Copy RtMidi.h to externals/rtmidi/include.
|
||||
|
||||
3. Copy RtMidi.cpp to externals/rtmidi/src
|
||||
|
||||
4. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
||||
=========================
|
||||
|
||||
RtMidi: realtime MIDI i/o C++ classes<BR>
|
||||
Copyright (c) 2003-2014 Gary P. Scavone
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -11,18 +11,114 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the texture containing our permutations and normals
|
||||
uniform sampler2D permutationNormalTexture;
|
||||
// implementation based on Ken Perlin's Improved Noise reference implementation (orig. in Java) at
|
||||
// http://mrl.nyu.edu/~perlin/noise/
|
||||
|
||||
uniform sampler2D permutationTexture;
|
||||
|
||||
// the noise frequency
|
||||
const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128
|
||||
const float frequency = 256.0;
|
||||
//const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128
|
||||
|
||||
// the noise amplitude
|
||||
const float amplitude = 0.1;
|
||||
const float amplitude = 0.5;
|
||||
|
||||
// the position in model space
|
||||
varying vec3 position;
|
||||
|
||||
// gradient based on gradients from cube edge centers rather than random from texture lookup
|
||||
float randomEdgeGrad(int hash, vec3 position){
|
||||
int h = int(mod(hash, 16));
|
||||
float u = h < 8 ? position.x : position.y;
|
||||
float v = h < 4 ? position.y : h == 12 || h == 14 ? position.x : position.z;
|
||||
bool even = mod(hash, 2) == 0;
|
||||
bool four = mod(hash, 4) == 0;
|
||||
return (even ? u : -u) + (four ? v : -v);
|
||||
}
|
||||
|
||||
// still have the option to lookup based on texture
|
||||
float randomTextureGrad(int hash, vec3 position){
|
||||
float u = float(hash) / 256.0;
|
||||
vec3 g = -1 + 2 * texture2D(permutationTexture, vec2(u, 0.75)).rgb;
|
||||
return dot(position, g);
|
||||
}
|
||||
|
||||
float improvedGrad(int hash, vec3 position){
|
||||
// Untested whether texture lookup is faster than math, uncomment one line or the other to try out
|
||||
// cube edge gradients versus random spherical gradients sent in texture.
|
||||
|
||||
// return randomTextureGrad(hash, position);
|
||||
return randomEdgeGrad(hash, position);
|
||||
}
|
||||
|
||||
// 5th order fade function to remove 2nd order discontinuties
|
||||
vec3 fade3(vec3 t){
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
int permutation(int index){
|
||||
float u = float(index) / 256.0;
|
||||
float t = texture2D(permutationTexture, vec2(u, 0.25)).r;
|
||||
return int(t * 256);
|
||||
}
|
||||
|
||||
float improvedNoise(vec3 position){
|
||||
int X = int(mod(floor(position.x), 256));
|
||||
int Y = int(mod(floor(position.y), 256));
|
||||
int Z = int(mod(floor(position.z), 256));
|
||||
|
||||
vec3 fracs = fract(position);
|
||||
|
||||
vec3 fades = fade3(fracs);
|
||||
|
||||
int A = permutation(X + 0) + Y;
|
||||
int AA = permutation(A + 0) + Z;
|
||||
int AB = permutation(A + 1) + Z;
|
||||
int B = permutation(X + 1) + Y;
|
||||
int BA = permutation(B + 0) + Z;
|
||||
int BB = permutation(B + 1) + Z;
|
||||
|
||||
float gradAA0 = improvedGrad(permutation(AA + 0), vec3(fracs.x , fracs.y , fracs.z ));
|
||||
float gradBA0 = improvedGrad(permutation(BA + 0), vec3(fracs.x - 1, fracs.y , fracs.z ));
|
||||
float gradAB0 = improvedGrad(permutation(AB + 0), vec3(fracs.x , fracs.y - 1, fracs.z ));
|
||||
float gradBB0 = improvedGrad(permutation(BB + 0), vec3(fracs.x - 1, fracs.y - 1, fracs.z ));
|
||||
float gradAA1 = improvedGrad(permutation(AA + 1), vec3(fracs.x , fracs.y , fracs.z - 1));
|
||||
float gradBA1 = improvedGrad(permutation(BA + 1), vec3(fracs.x - 1, fracs.y , fracs.z - 1));
|
||||
float gradAB1 = improvedGrad(permutation(AB + 1), vec3(fracs.x , fracs.y - 1, fracs.z - 1));
|
||||
float gradBB1 = improvedGrad(permutation(BB + 1), vec3(fracs.x - 1, fracs.y - 1, fracs.z - 1));
|
||||
|
||||
return mix(mix(mix(gradAA0, gradBA0, fades.x), mix(gradAB0, gradBB0, fades.x), fades.y), mix(mix(gradAA1, gradBA1, fades.x), mix(gradAB1, gradBB1, fades.x), fades.y), fades.z);
|
||||
}
|
||||
|
||||
float turbulence(vec3 position, float power){
|
||||
return (1.0f / power) * improvedNoise(power * position);
|
||||
}
|
||||
|
||||
float turb(vec3 position){
|
||||
return turbulence(position, 1)
|
||||
+ turbulence(position, 2),
|
||||
+ turbulence(position, 4)
|
||||
+ turbulence(position, 8)
|
||||
+ turbulence(position, 16)
|
||||
+ turbulence(position, 32)
|
||||
+ turbulence(position, 64)
|
||||
+ turbulence(position, 128)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void main(void) {
|
||||
|
||||
// get noise in range 0 .. 1
|
||||
float noise = clamp(0.5f + amplitude * turb(position * frequency), 0, 1);
|
||||
|
||||
// apply vertex lighting
|
||||
vec3 color = gl_Color.rgb * vec3(noise, noise, noise);
|
||||
gl_FragColor = vec4(color, 1);
|
||||
}
|
||||
|
||||
|
||||
/* old implementation
|
||||
// returns the gradient at a single corner of our sampling cube
|
||||
vec3 grad(vec3 location) {
|
||||
float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r;
|
||||
|
@ -60,7 +156,4 @@ float perlin(vec3 location) {
|
|||
mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y),
|
||||
params.z);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(gl_Color.rgb * (1.0 + amplitude*(perlin(position * frequency) - 1.0)), 1.0);
|
||||
}
|
||||
*/
|
|
@ -32,7 +32,6 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QMenuBar>
|
||||
#include <QMouseEvent>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
@ -53,23 +52,25 @@
|
|||
|
||||
#include <AccountManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <LocalVoxelsList.h>
|
||||
#include <Logging.h>
|
||||
#include <ModelsScriptingInterface.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <OctalCode.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
#include <PerfStat.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <LocalVoxelsList.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceVersion.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelUploader.h"
|
||||
#include "Util.h"
|
||||
#include "devices/MIDIManager.h"
|
||||
#include "devices/OculusManager.h"
|
||||
#include "devices/TV3DManager.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
@ -316,12 +317,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
// Make sure cache on same thread than its parent (NetworkAccessManager)
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||
cache->moveToThread(networkAccessManager.thread());
|
||||
cache->setParent(&networkAccessManager);
|
||||
|
||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||
_networkAccessManager->setCache(cache);
|
||||
networkAccessManager.setCache(cache);
|
||||
|
||||
ResourceCache::setNetworkAccessManager(_networkAccessManager);
|
||||
ResourceCache::setRequestLimit(3);
|
||||
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
@ -397,21 +402,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig();
|
||||
|
||||
_trayIcon->show();
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
// setup the MIDIManager
|
||||
MIDIManager& midiManagerInstance = MIDIManager::getInstance();
|
||||
midiManagerInstance.openDefaultPort();
|
||||
#endif
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
saveSettings();
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
|
||||
int DELAY_TIME = 1000;
|
||||
UserActivityLogger::getInstance().close(DELAY_TIME);
|
||||
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
// make sure we don't call the idle timer any more
|
||||
delete idleTimer;
|
||||
|
||||
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
|
||||
saveSettings();
|
||||
|
||||
delete _voxelImporter;
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
|
@ -434,8 +446,6 @@ Application::~Application() {
|
|||
_particleEditSender.terminate();
|
||||
_modelEditSender.terminate();
|
||||
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
@ -443,8 +453,6 @@ Application::~Application() {
|
|||
_myAvatar = NULL;
|
||||
|
||||
delete _glWidget;
|
||||
|
||||
AccountManager::getInstance().destroy();
|
||||
}
|
||||
|
||||
void Application::saveSettings() {
|
||||
|
@ -592,13 +600,17 @@ void Application::paintGL() {
|
|||
//Note, the camera distance is set in Camera::setMode() so we dont have to do it here.
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation());
|
||||
if (OculusManager::isConnected()) {
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation());
|
||||
} else {
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
|
||||
}
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setTightness(0.0f);
|
||||
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0));
|
||||
}
|
||||
|
||||
// Update camera position
|
||||
|
@ -636,6 +648,10 @@ void Application::paintGL() {
|
|||
//If we aren't using the glow shader, we have to clear the color and depth buffer
|
||||
if (!glowEnabled) {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
} else if (OculusManager::isConnected()) {
|
||||
//Clear the color buffer to ensure that there isnt any residual color
|
||||
//Left over from when OR was not connected.
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
|
@ -647,13 +663,8 @@ void Application::paintGL() {
|
|||
}
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
if (glowEnabled) {
|
||||
_glowEffect.prepare();
|
||||
}
|
||||
|
||||
TV3DManager::display(whichCamera);
|
||||
if (glowEnabled) {
|
||||
_glowEffect.render();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (glowEnabled) {
|
||||
|
@ -1141,7 +1152,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
|
||||
if (_mouseHidden && showMouse && !OculusManager::isConnected()) {
|
||||
if (_mouseHidden && showMouse && !OculusManager::isConnected() && !TV3DManager::isConnected()) {
|
||||
getGLWidget()->setCursor(Qt::ArrowCursor);
|
||||
_mouseHidden = false;
|
||||
_seenMouseMove = true;
|
||||
|
@ -2197,11 +2208,11 @@ int Application::sendNackPackets() {
|
|||
_octreeSceneStatsLock.unlock();
|
||||
continue;
|
||||
}
|
||||
OctreeSceneStats& stats = _octreeServerSceneStats[nodeUUID];
|
||||
|
||||
// make copy of missing sequence numbers from stats
|
||||
const QSet<OCTREE_PACKET_SEQUENCE> missingSequenceNumbers =
|
||||
stats.getIncomingOctreeSequenceNumberStats().getMissingSet();
|
||||
// get sequence number stats of node, prune its missing set, and make a copy of the missing set
|
||||
SequenceNumberStats& sequenceNumberStats = _octreeServerSceneStats[nodeUUID].getIncomingOctreeSequenceNumberStats();
|
||||
sequenceNumberStats.pruneMissingSet();
|
||||
const QSet<OCTREE_PACKET_SEQUENCE> missingSequenceNumbers = sequenceNumberStats.getMissingSet();
|
||||
|
||||
_octreeSceneStatsLock.unlock();
|
||||
|
||||
|
@ -3639,6 +3650,10 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
|
||||
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
|
||||
#endif
|
||||
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
||||
|
@ -3817,7 +3832,8 @@ void Application::initAvatarAndViewFrustum() {
|
|||
void Application::checkVersion() {
|
||||
QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL)));
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
connect(Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest), SIGNAL(finished()), SLOT(parseVersionXml()));
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest);
|
||||
connect(reply, SIGNAL(finished()), SLOT(parseVersionXml()));
|
||||
}
|
||||
|
||||
void Application::parseVersionXml() {
|
||||
|
|
|
@ -97,7 +97,6 @@ class QActionGroup;
|
|||
class QGLWidget;
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QNetworkAccessManager;
|
||||
class QSettings;
|
||||
class QWheelEvent;
|
||||
|
||||
|
@ -236,7 +235,6 @@ public:
|
|||
void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); }
|
||||
void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); }
|
||||
|
||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
GeometryCache* getGeometryCache() { return &_geometryCache; }
|
||||
AnimationCache* getAnimationCache() { return &_animationCache; }
|
||||
TextureCache* getTextureCache() { return &_textureCache; }
|
||||
|
@ -423,7 +421,6 @@ private:
|
|||
QThread* _nodeThread;
|
||||
DatagramProcessor _datagramProcessor;
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
QMutex _settingsMutex;
|
||||
QSettings* _settings;
|
||||
int _numChangedSettings;
|
||||
|
|
|
@ -67,7 +67,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_proceduralAudioOutput(NULL),
|
||||
_proceduralOutputDevice(NULL),
|
||||
_inputRingBuffer(0),
|
||||
_ringBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL),
|
||||
_ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO),
|
||||
_isStereoInput(false),
|
||||
_averagedLatency(0.0),
|
||||
_measuredJitter(0),
|
||||
|
@ -93,7 +93,7 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_processSpatialAudio(false),
|
||||
_spatialAudioStart(0),
|
||||
_spatialAudioFinish(0),
|
||||
_spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, true), // random access mode
|
||||
_spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, true), // random access mode
|
||||
_scopeEnabled(false),
|
||||
_scopeEnabledPause(false),
|
||||
_scopeInputOffset(0),
|
||||
|
@ -432,7 +432,7 @@ void Audio::handleAudioInput() {
|
|||
|
||||
float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes);
|
||||
|
||||
unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio;
|
||||
int inputSamplesRequired = (int)((float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
|
||||
|
||||
QByteArray inputByteArray = _inputDevice->readAll();
|
||||
|
||||
|
@ -1379,8 +1379,10 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
|
|||
|
||||
// cleanup any previously initialized device
|
||||
if (_audioInput) {
|
||||
// The call to stop() causes _inputDevice to be destructed.
|
||||
// That in turn causes it to be disconnected (see for example
|
||||
// http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect).
|
||||
_audioInput->stop();
|
||||
disconnect(_inputDevice);
|
||||
_inputDevice = NULL;
|
||||
|
||||
delete _audioInput;
|
||||
|
|
|
@ -149,13 +149,19 @@ void DatagramProcessor::processDatagrams() {
|
|||
break;
|
||||
}
|
||||
case PacketTypeVoxelEditNack:
|
||||
application->_voxelEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_voxelEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
case PacketTypeParticleEditNack:
|
||||
application->_particleEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_particleEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
case PacketTypeModelEditNack:
|
||||
application->_modelEditSender.processNackPacket(incomingPacket);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
|
||||
application->_modelEditSender.processNackPacket(incomingPacket);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||
|
|
|
@ -91,6 +91,7 @@ Menu::Menu() :
|
|||
_jsConsole(NULL),
|
||||
_octreeStatsDialog(NULL),
|
||||
_lodToolsDialog(NULL),
|
||||
_userLocationsDialog(NULL),
|
||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||
|
@ -166,6 +167,11 @@ Menu::Menu() :
|
|||
Qt::CTRL | Qt::Key_N,
|
||||
this,
|
||||
SLOT(nameLocation()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::MyLocations,
|
||||
Qt::CTRL | Qt::Key_K,
|
||||
this,
|
||||
SLOT(toggleLocationList()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoTo,
|
||||
Qt::Key_At,
|
||||
|
@ -1184,6 +1190,17 @@ void Menu::namedLocationCreated(LocationManager::NamedLocationCreateResponse res
|
|||
msgBox.exec();
|
||||
}
|
||||
|
||||
void Menu::toggleLocationList() {
|
||||
if (!_userLocationsDialog) {
|
||||
_userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow());
|
||||
}
|
||||
if (_userLocationsDialog->isVisible()) {
|
||||
_userLocationsDialog->hide();
|
||||
} else {
|
||||
_userLocationsDialog->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::nameLocation() {
|
||||
// check if user is logged in or show login dialog if not
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "ui/JSConsole.h"
|
||||
#include "ui/LoginDialog.h"
|
||||
#include "ui/ScriptEditorWindow.h"
|
||||
#include "ui/UserLocationsDialog.h"
|
||||
|
||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||
const float ADJUST_LOD_UP_FPS = 55.0;
|
||||
|
@ -199,6 +200,7 @@ private slots:
|
|||
void goToDomainDialog();
|
||||
void goToLocation();
|
||||
void nameLocation();
|
||||
void toggleLocationList();
|
||||
void bandwidthDetailsClosed();
|
||||
void octreeStatsDetailsClosed();
|
||||
void lodToolsClosed();
|
||||
|
@ -265,6 +267,7 @@ private:
|
|||
QDialog* _jsConsole;
|
||||
OctreeStatsDialog* _octreeStatsDialog;
|
||||
LodToolsDialog* _lodToolsDialog;
|
||||
UserLocationsDialog* _userLocationsDialog;
|
||||
int _maxVoxels;
|
||||
float _voxelSizeScale;
|
||||
float _oculusUIAngularSize;
|
||||
|
@ -395,6 +398,7 @@ namespace MenuOption {
|
|||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString MyLocations = "My Locations...";
|
||||
const QString NameLocation = "Name this location";
|
||||
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
||||
const QString OctreeStats = "Voxel and Particle Statistics";
|
||||
|
|
|
@ -396,7 +396,7 @@ void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QS
|
|||
void ModelUploader::checkS3() {
|
||||
qDebug() << "Checking S3 for " << _url;
|
||||
QNetworkRequest request(_url);
|
||||
QNetworkReply* reply = _networkAccessManager.head(request);
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().head(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(processCheck()));
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ private:
|
|||
bool _readyToSend;
|
||||
|
||||
QHttpMultiPart* _dataMultiPart;
|
||||
QNetworkAccessManager _networkAccessManager;
|
||||
|
||||
int _numberOfChecks;
|
||||
QTimer _timer;
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
|
||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||
static const QString PUBLIC_URL = "http://public.highfidelity.io";
|
||||
|
@ -30,8 +31,6 @@ static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
|||
static const QString CONTAINER_NAME = "Contents";
|
||||
static const QString KEY_NAME = "Key";
|
||||
|
||||
static const int SCRIPT_PATH = Qt::UserRole;
|
||||
|
||||
ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) :
|
||||
_filename(filename),
|
||||
_fullPath(fullPath) {
|
||||
|
@ -113,14 +112,15 @@ void ScriptsModel::requestRemoteFiles(QString marker) {
|
|||
}
|
||||
url.setQuery(query);
|
||||
|
||||
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this);
|
||||
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
|
||||
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
accessManager->get(request);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
|
||||
}
|
||||
|
||||
void ScriptsModel::downloadFinished(QNetworkReply* reply) {
|
||||
void ScriptsModel::downloadFinished() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
bool finished = true;
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
|
||||
protected slots:
|
||||
void updateScriptsLocation(const QString& newPath);
|
||||
void downloadFinished(QNetworkReply* reply);
|
||||
void downloadFinished();
|
||||
void reloadLocalFiles();
|
||||
void reloadRemoteFiles();
|
||||
|
||||
|
|
246
interface/src/UserLocationsModel.cpp
Normal file
246
interface/src/UserLocationsModel.cpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// UserLocationsModel.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Ryan Huffman on 06/24/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "Application.h"
|
||||
#include "UserLocationsModel.h"
|
||||
|
||||
static const QString PLACES_GET = "/api/v1/places";
|
||||
static const QString PLACES_UPDATE = "/api/v1/places/%1";
|
||||
static const QString PLACES_DELETE= "/api/v1/places/%1";
|
||||
|
||||
UserLocation::UserLocation(QString id, QString name, QString location) :
|
||||
_id(id),
|
||||
_name(name),
|
||||
_location(location),
|
||||
_previousName(name),
|
||||
_updating(false) {
|
||||
}
|
||||
|
||||
void UserLocation::requestRename(const QString& newName) {
|
||||
if (!_updating && newName.toLower() != _name) {
|
||||
_updating = true;
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleRenameResponse", this, "handleRenameError");
|
||||
QJsonObject jsonNameObject;
|
||||
jsonNameObject.insert("name", QJsonValue(newName));
|
||||
QJsonDocument jsonDocument(jsonNameObject);
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_UPDATE.arg(_id),
|
||||
QNetworkAccessManager::PutOperation,
|
||||
callbackParams,
|
||||
jsonDocument.toJson());
|
||||
_previousName = _name;
|
||||
_name = newName;
|
||||
|
||||
emit updated(_name);
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocation::handleRenameResponse(const QJsonObject& responseData) {
|
||||
_updating = false;
|
||||
|
||||
QJsonValue status = responseData["status"];
|
||||
if (!status.isUndefined() && status.toString() == "success") {
|
||||
QString updatedName = responseData["data"].toObject()["name"].toString();
|
||||
_name = updatedName;
|
||||
} else {
|
||||
_name = _previousName;
|
||||
|
||||
QString msg = "There was an error renaming location '" + _name + "'";
|
||||
|
||||
QJsonValue data = responseData["data"];
|
||||
if (!data.isUndefined()) {
|
||||
QJsonValue nameError = data.toObject()["name"];
|
||||
if (!nameError.isUndefined()) {
|
||||
msg += ": " + nameError.toString();
|
||||
}
|
||||
}
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
}
|
||||
|
||||
emit updated(_name);
|
||||
}
|
||||
|
||||
void UserLocation::handleRenameError(QNetworkReply::NetworkError error, const QString& errorString) {
|
||||
_updating = false;
|
||||
|
||||
QString msg = "There was an error renaming location '" + _name + "': " + errorString;
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
|
||||
emit updated(_name);
|
||||
}
|
||||
|
||||
void UserLocation::requestDelete() {
|
||||
if (!_updating) {
|
||||
_updating = true;
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleDeleteResponse", this, "handleDeleteError");
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_DELETE.arg(_id),
|
||||
QNetworkAccessManager::DeleteOperation,
|
||||
callbackParams);
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocation::handleDeleteResponse(const QJsonObject& responseData) {
|
||||
_updating = false;
|
||||
|
||||
QJsonValue status = responseData["status"];
|
||||
if (!status.isUndefined() && status.toString() == "success") {
|
||||
emit deleted(_name);
|
||||
} else {
|
||||
QString msg = "There was an error deleting location '" + _name + "'";
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocation::handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString) {
|
||||
_updating = false;
|
||||
|
||||
QString msg = "There was an error deleting location '" + _name + "': " + errorString;
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
}
|
||||
|
||||
UserLocationsModel::UserLocationsModel(QObject* parent) :
|
||||
QAbstractListModel(parent),
|
||||
_updating(false) {
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
UserLocationsModel::~UserLocationsModel() {
|
||||
qDeleteAll(_locations);
|
||||
_locations.clear();
|
||||
}
|
||||
|
||||
void UserLocationsModel::update() {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void UserLocationsModel::deleteLocation(const QModelIndex& index) {
|
||||
UserLocation* location = _locations[index.row()];
|
||||
location->requestDelete();
|
||||
}
|
||||
|
||||
void UserLocationsModel::renameLocation(const QModelIndex& index, const QString& newName) {
|
||||
UserLocation* location = _locations[index.row()];
|
||||
location->requestRename(newName);
|
||||
}
|
||||
|
||||
void UserLocationsModel::refresh() {
|
||||
if (!_updating) {
|
||||
beginResetModel();
|
||||
qDeleteAll(_locations);
|
||||
_locations.clear();
|
||||
_updating = true;
|
||||
endResetModel();
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleLocationsResponse");
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_GET,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocationsModel::handleLocationsResponse(const QJsonObject& responseData) {
|
||||
_updating = false;
|
||||
|
||||
QJsonValue status = responseData["status"];
|
||||
if (!status.isUndefined() && status.toString() == "success") {
|
||||
beginResetModel();
|
||||
QJsonArray locations = responseData["data"].toObject()["places"].toArray();
|
||||
for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) {
|
||||
QJsonObject location = (*it).toObject();
|
||||
QJsonObject address = location["address"].toObject();
|
||||
UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(),
|
||||
"hifi://" + address["domain"].toString()
|
||||
+ "/" + address["position"].toString()
|
||||
+ "/" + address["orientation"].toString());
|
||||
_locations.append(userLocation);
|
||||
connect(userLocation, &UserLocation::deleted, this, &UserLocationsModel::removeLocation);
|
||||
connect(userLocation, &UserLocation::updated, this, &UserLocationsModel::update);
|
||||
}
|
||||
endResetModel();
|
||||
} else {
|
||||
qDebug() << "Error loading location data";
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocationsModel::removeLocation(const QString& name) {
|
||||
beginResetModel();
|
||||
for (QList<UserLocation*>::iterator it = _locations.begin(); it != _locations.end(); it++) {
|
||||
if ((*it)->name() == name) {
|
||||
_locations.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int UserLocationsModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_updating) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return _locations.length();
|
||||
}
|
||||
|
||||
QVariant UserLocationsModel::data(const QModelIndex& index, int role) const {
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (_updating) {
|
||||
return QVariant("Updating...");
|
||||
} else if (index.row() > _locations.length()) {
|
||||
return QVariant();
|
||||
} else if (index.column() == NameColumn) {
|
||||
return _locations[index.row()]->name();
|
||||
} else if (index.column() == LocationColumn) {
|
||||
return QVariant(_locations[index.row()]->location());
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
||||
}
|
||||
QVariant UserLocationsModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case NameColumn: return "Name";
|
||||
case LocationColumn: return "Location";
|
||||
default: return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags UserLocationsModel::flags(const QModelIndex& index) const {
|
||||
if (index.row() < _locations.length()) {
|
||||
UserLocation* ul = _locations[index.row()];
|
||||
if (ul->isUpdating()) {
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
}
|
||||
|
||||
return QAbstractListModel::flags(index);
|
||||
}
|
82
interface/src/UserLocationsModel.h
Normal file
82
interface/src/UserLocationsModel.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// UserLocationsModel.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Ryan Huffman on 06/24/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_UserLocationsModel_h
|
||||
#define hifi_UserLocationsModel_h
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QModelIndex>
|
||||
#include <QVariant>
|
||||
|
||||
|
||||
class UserLocation : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
UserLocation(QString id, QString name, QString location);
|
||||
bool isUpdating() { return _updating; }
|
||||
void requestRename(const QString& newName);
|
||||
void requestDelete();
|
||||
|
||||
QString id() { return _id; }
|
||||
QString name() { return _name; }
|
||||
QString location() { return _location; }
|
||||
|
||||
public slots:
|
||||
void handleRenameResponse(const QJsonObject& responseData);
|
||||
void handleRenameError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||
void handleDeleteResponse(const QJsonObject& responseData);
|
||||
void handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||
|
||||
signals:
|
||||
void updated(const QString& name);
|
||||
void deleted(const QString& name);
|
||||
|
||||
private:
|
||||
QString _id;
|
||||
QString _name;
|
||||
QString _location;
|
||||
QString _previousName;
|
||||
bool _updating;
|
||||
|
||||
};
|
||||
|
||||
class UserLocationsModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
UserLocationsModel(QObject* parent = NULL);
|
||||
~UserLocationsModel();
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const { return 2; };
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
void deleteLocation(const QModelIndex& index);
|
||||
void renameLocation(const QModelIndex& index, const QString& newName);
|
||||
|
||||
enum Columns {
|
||||
NameColumn = 0,
|
||||
LocationColumn
|
||||
};
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
void update();
|
||||
void handleLocationsResponse(const QJsonObject& responseData);
|
||||
void removeLocation(const QString& name);
|
||||
|
||||
private:
|
||||
bool _updating;
|
||||
QList<UserLocation*> _locations;
|
||||
};
|
||||
|
||||
#endif // hifi_UserLocationsModel_h
|
|
@ -51,10 +51,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
|||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||
state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation;
|
||||
* joint.rotation);
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
@ -68,8 +68,8 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ
|
|||
_owningHead->getSaccade() - _translation, 1.0f));
|
||||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
state._rotationInParentFrame = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation;
|
||||
state.setRotationInParentFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation);
|
||||
}
|
||||
|
||||
void FaceModel::updateJointState(int index) {
|
||||
|
|
|
@ -580,6 +580,40 @@ void MyAvatar::stopAnimation(const QString& url) {
|
|||
}
|
||||
}
|
||||
|
||||
AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
|
||||
AnimationDetails result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_ARG(const QString&, role));
|
||||
return result;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
result = handle->getAnimationDetails();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
|
||||
AnimationDetails result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_ARG(const QString&, url));
|
||||
return result;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
result = handle->getAnimationDetails();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MyAvatar::saveData(QSettings* settings) {
|
||||
settings->beginGroup("Avatar");
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ public:
|
|||
|
||||
/// Stops an animation identified by its role.
|
||||
Q_INVOKABLE void stopAnimationByRole(const QString& role);
|
||||
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* settings);
|
||||
|
|
|
@ -11,21 +11,36 @@
|
|||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <VerletCapsuleShape.h>
|
||||
#include <VerletSphereShape.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "Hand.h"
|
||||
#include "Menu.h"
|
||||
#include "SkeletonModel.h"
|
||||
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
||||
_owningAvatar(owningAvatar) {
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
||||
Model(parent),
|
||||
Ragdoll(),
|
||||
_owningAvatar(owningAvatar),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.0f) {
|
||||
}
|
||||
|
||||
void SkeletonModel::setJointStates(QVector<JointState> states) {
|
||||
Model::setJointStates(states);
|
||||
|
||||
if (isActive() && _owningAvatar->isMyAvatar()) {
|
||||
_ragDoll.init(_jointStates);
|
||||
// the SkeletonModel override of updateJointState() will clear the translation part
|
||||
// of its root joint and we need that done before we try to build shapes hence we
|
||||
// recompute all joint transforms at this time.
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i);
|
||||
}
|
||||
|
||||
clearShapes();
|
||||
if (_enableShapes) {
|
||||
buildShapes();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +81,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
const float HAND_RESTORATION_RATE = 0.25f;
|
||||
if ( (leftPalmIndex == -1) && ( rightPalmIndex == -1) ) { // NO palm active
|
||||
if (leftPalmIndex == -1 || rightPalmIndex == -1) {
|
||||
// palms are not yet set, use mouse
|
||||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
@ -77,39 +92,22 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else if ( (leftPalmIndex == -1) && ( rightPalmIndex != -1)) { // Just right hand active
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else if ( (leftPalmIndex != -1) && ( rightPalmIndex == -1) ) { // Just left hand active
|
||||
} else if (leftPalmIndex == rightPalmIndex) {
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else {
|
||||
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
|
||||
}
|
||||
|
||||
simulateRagDoll(deltaTime);
|
||||
}
|
||||
|
||||
void SkeletonModel::simulateRagDoll(float deltaTime) {
|
||||
_ragDoll.slaveToSkeleton(_jointStates, 0.1f); // fraction = 0.1f left intentionally low for demo purposes
|
||||
|
||||
float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm
|
||||
int MAX_ITERATIONS = 4;
|
||||
int iterations = 0;
|
||||
float delta = 0.0f;
|
||||
do {
|
||||
delta = _ragDoll.enforceConstraints();
|
||||
++iterations;
|
||||
} while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS);
|
||||
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
|
||||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
|
||||
if (jointIndex < 0 || jointIndex >= int(_jointShapes.size())) {
|
||||
if (jointIndex < 0 || jointIndex >= int(_shapes.size())) {
|
||||
return;
|
||||
}
|
||||
if (jointIndex == getLeftHandJointIndex()
|
||||
|
@ -119,18 +117,21 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes)
|
|||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
const FBXJoint& joint = geometry.joints[i];
|
||||
int parentIndex = joint.parentIndex;
|
||||
Shape* shape = _shapes[i];
|
||||
if (i == jointIndex) {
|
||||
// this shape is the hand
|
||||
shapes.push_back(_jointShapes[i]);
|
||||
if (parentIndex != -1) {
|
||||
// also add the forearm
|
||||
shapes.push_back(_jointShapes[parentIndex]);
|
||||
if (shape) {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
} else {
|
||||
if (parentIndex != -1 && _shapes[parentIndex]) {
|
||||
// also add the forearm
|
||||
shapes.push_back(_shapes[parentIndex]);
|
||||
}
|
||||
} else if (shape) {
|
||||
while (parentIndex != -1) {
|
||||
if (parentIndex == jointIndex) {
|
||||
// this shape is a child of the hand
|
||||
shapes.push_back(_jointShapes[i]);
|
||||
shapes.push_back(shape);
|
||||
break;
|
||||
}
|
||||
parentIndex = geometry.joints[parentIndex].parentIndex;
|
||||
|
@ -150,7 +151,7 @@ void SkeletonModel::renderIKConstraints() {
|
|||
renderJointConstraints(getRightHandJointIndex());
|
||||
renderJointConstraints(getLeftHandJointIndex());
|
||||
//if (isActive() && _owningAvatar->isMyAvatar()) {
|
||||
// renderRagDoll();
|
||||
// renderRagdoll();
|
||||
//}
|
||||
}
|
||||
|
||||
|
@ -218,10 +219,9 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_jointStates[jointIndex]._rotationInParentFrame = glm::quat();
|
||||
_jointStates[jointIndex].setRotationInParentFrame(glm::quat());
|
||||
} else {
|
||||
setJointPosition(jointIndex, palmPosition, palmRotation,
|
||||
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,15 +249,6 @@ void SkeletonModel::updateJointState(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateShapePositions() {
|
||||
if (isActive() && _owningAvatar->isMyAvatar() &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagDoll)) {
|
||||
_ragDoll.updateShapes(_jointShapes, _rotation, _translation);
|
||||
} else {
|
||||
Model::updateShapePositions();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) {
|
||||
return;
|
||||
|
@ -266,9 +257,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
|||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||
state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
||||
glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
glm::normalize(inverse * axes[0])) * joint.rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
@ -483,22 +474,21 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco
|
|||
return false;
|
||||
}
|
||||
|
||||
void SkeletonModel::renderRagDoll() {
|
||||
void SkeletonModel::renderRagdoll() {
|
||||
const int BALL_SUBDIVISIONS = 6;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glPushMatrix();
|
||||
|
||||
Application::getInstance()->loadTranslatedViewMatrix(_translation);
|
||||
QVector<glm::vec3> points = _ragDoll.getPoints();
|
||||
int numPoints = points.size();
|
||||
int numPoints = _ragdollPoints.size();
|
||||
float alpha = 0.3f;
|
||||
float radius1 = 0.008f;
|
||||
float radius2 = 0.01f;
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
glPushMatrix();
|
||||
// draw each point as a yellow hexagon with black border
|
||||
glm::vec3 position = _rotation * points[i];
|
||||
glm::vec3 position = _rotation * _ragdollPoints[i]._position;
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, alpha);
|
||||
glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
@ -510,3 +500,272 @@ void SkeletonModel::renderRagDoll() {
|
|||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
// virtual
|
||||
void SkeletonModel::initRagdollPoints() {
|
||||
clearRagdollConstraintsAndPoints();
|
||||
|
||||
// one point for each joint
|
||||
int numJoints = _jointStates.size();
|
||||
_ragdollPoints.fill(VerletPoint(), numJoints);
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
const JointState& state = _jointStates.at(i);
|
||||
glm::vec3 position = state.getPosition();
|
||||
_ragdollPoints[i]._position = position;
|
||||
_ragdollPoints[i]._lastPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::buildRagdollConstraints() {
|
||||
// NOTE: the length of DistanceConstraints is computed and locked in at this time
|
||||
// so make sure the ragdoll positions are in a normal configuration before here.
|
||||
const int numPoints = _ragdollPoints.size();
|
||||
assert(numPoints == _jointStates.size());
|
||||
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
const JointState& state = _jointStates.at(i);
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f));
|
||||
_ragdollConstraints.push_back(anchor);
|
||||
} else {
|
||||
DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex]));
|
||||
_ragdollConstraints.push_back(bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateVisibleJointStates() {
|
||||
Model::updateVisibleJointStates();
|
||||
// TODO: implement this to move visible joints to agree with joint shape positions
|
||||
}
|
||||
|
||||
// virtual
|
||||
void SkeletonModel::stepRagdollForward(float deltaTime) {
|
||||
const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f;
|
||||
float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f);
|
||||
moveShapesTowardJoints(fraction);
|
||||
}
|
||||
|
||||
float DENSITY_OF_WATER = 1000.0f; // kg/m^3
|
||||
float MIN_JOINT_MASS = 1.0f;
|
||||
float VERY_BIG_MASS = 1.0e6f;
|
||||
|
||||
// virtual
|
||||
void SkeletonModel::buildShapes() {
|
||||
if (!_geometry || _rootIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
initRagdollPoints();
|
||||
|
||||
float uniformScale = extractUniformScale(_scale);
|
||||
const int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
JointState& state = _jointStates[i];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
float radius = uniformScale * joint.boneRadius;
|
||||
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
|
||||
Shape::Type type = joint.shapeType;
|
||||
if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) {
|
||||
// this shape is forced to be a sphere
|
||||
type = Shape::SPHERE_SHAPE;
|
||||
}
|
||||
Shape* shape = NULL;
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (type == Shape::SPHERE_SHAPE) {
|
||||
shape = new VerletSphereShape(radius, &(_ragdollPoints[i]));
|
||||
shape->setEntity(this);
|
||||
_ragdollPoints[i]._mass = glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume());
|
||||
} else if (type == Shape::CAPSULE_SHAPE) {
|
||||
assert(parentIndex != -1);
|
||||
shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i]));
|
||||
shape->setEntity(this);
|
||||
_ragdollPoints[i]._mass = glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume());
|
||||
}
|
||||
if (parentIndex != -1) {
|
||||
// always disable collisions between joint and its parent
|
||||
disableCollisions(i, parentIndex);
|
||||
} else {
|
||||
// give the base joint a very large mass since it doesn't actually move
|
||||
// in the local-frame simulation (it defines the origin)
|
||||
_ragdollPoints[i]._mass = VERY_BIG_MASS;
|
||||
}
|
||||
_shapes.push_back(shape);
|
||||
}
|
||||
|
||||
// This method moves the shapes to their default positions in Model frame.
|
||||
computeBoundingShape(geometry);
|
||||
|
||||
// While the shapes are in their default position we disable collisions between
|
||||
// joints that are currently colliding.
|
||||
disableCurrentSelfCollisions();
|
||||
|
||||
buildRagdollConstraints();
|
||||
|
||||
// ... then move shapes back to current joint positions
|
||||
moveShapesTowardJoints(1.0f);
|
||||
enforceRagdollConstraints();
|
||||
}
|
||||
|
||||
void SkeletonModel::moveShapesTowardJoints(float fraction) {
|
||||
const int numStates = _jointStates.size();
|
||||
assert(_jointStates.size() == _ragdollPoints.size());
|
||||
assert(fraction >= 0.0f && fraction <= 1.0f);
|
||||
if (_ragdollPoints.size() == numStates) {
|
||||
float oneMinusFraction = 1.0f - fraction;
|
||||
int numJoints = _jointStates.size();
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
||||
_ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||
// compute default joint transforms
|
||||
int numJoints = geometry.joints.size();
|
||||
if (numJoints != _ragdollPoints.size()) {
|
||||
return;
|
||||
}
|
||||
QVector<glm::mat4> transforms;
|
||||
transforms.fill(glm::mat4(), numJoints);
|
||||
|
||||
// compute the default transforms and slam the ragdoll positions accordingly
|
||||
// (which puts the shapes where we want them)
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
const FBXJoint& joint = geometry.joints.at(i);
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
transforms[i] = _jointStates[i].getTransform();
|
||||
_ragdollPoints[i]._position = extractTranslation(transforms[i]);
|
||||
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
||||
* joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform;
|
||||
// setting the ragdollPoints here slams the VerletShapes into their default positions
|
||||
_ragdollPoints[i]._position = extractTranslation(transforms[i]);
|
||||
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
||||
}
|
||||
|
||||
// compute bounding box that encloses all shapes
|
||||
Extents totalExtents;
|
||||
totalExtents.reset();
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
Shape* shape = _shapes[i];
|
||||
if (!shape) {
|
||||
continue;
|
||||
}
|
||||
// TODO: skip hand and arm shapes for bounding box calculation
|
||||
Extents shapeExtents;
|
||||
shapeExtents.reset();
|
||||
glm::vec3 localPosition = shape->getTranslation();
|
||||
int type = shape->getType();
|
||||
if (type == Shape::CAPSULE_SHAPE) {
|
||||
// add the two furthest surface points of the capsule
|
||||
CapsuleShape* capsule = static_cast<CapsuleShape*>(shape);
|
||||
glm::vec3 axis;
|
||||
capsule->computeNormalizedAxis(axis);
|
||||
float radius = capsule->getRadius();
|
||||
float halfHeight = capsule->getHalfHeight();
|
||||
axis = halfHeight * axis + glm::vec3(radius);
|
||||
|
||||
shapeExtents.addPoint(localPosition + axis);
|
||||
shapeExtents.addPoint(localPosition - axis);
|
||||
totalExtents.addExtents(shapeExtents);
|
||||
} else if (type == Shape::SPHERE_SHAPE) {
|
||||
float radius = shape->getBoundingRadius();
|
||||
glm::vec3 axis = glm::vec3(radius);
|
||||
shapeExtents.addPoint(localPosition + axis);
|
||||
shapeExtents.addPoint(localPosition - axis);
|
||||
totalExtents.addExtents(shapeExtents);
|
||||
}
|
||||
}
|
||||
|
||||
// compute bounding shape parameters
|
||||
// NOTE: we assume that the longest side of totalExtents is the yAxis...
|
||||
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
|
||||
// ... and assume the radius is half the RMS of the X and Z sides:
|
||||
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
_boundingShape.setRadius(capsuleRadius);
|
||||
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
|
||||
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum);
|
||||
_boundingRadius = 0.5f * glm::length(diagonal);
|
||||
}
|
||||
|
||||
void SkeletonModel::resetShapePositionsToDefaultPose() {
|
||||
// DEBUG method.
|
||||
// Moves shapes to the joint default locations for debug visibility into
|
||||
// how the bounding shape is computed.
|
||||
|
||||
if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) {
|
||||
// geometry or joints have not yet been created
|
||||
return;
|
||||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The shapes are moved to their default positions in computeBoundingShape().
|
||||
computeBoundingShape(geometry);
|
||||
|
||||
// Then we move them into world frame for rendering at the Model's location.
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
Shape* shape = _shapes[i];
|
||||
if (shape) {
|
||||
shape->setTranslation(_translation + _rotation * shape->getTranslation());
|
||||
shape->setRotation(_rotation * shape->getRotation());
|
||||
}
|
||||
}
|
||||
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
|
||||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderBoundingCollisionShapes(float alpha) {
|
||||
const int BALL_SUBDIVISIONS = 10;
|
||||
if (_shapes.isEmpty()) {
|
||||
// the bounding shape has not been propery computed
|
||||
// so no need to render it
|
||||
return;
|
||||
}
|
||||
glPushMatrix();
|
||||
|
||||
Application::getInstance()->loadTranslatedViewMatrix(_translation);
|
||||
|
||||
// draw a blue sphere at the capsule endpoint
|
||||
glm::vec3 endPoint;
|
||||
_boundingShape.getEndPoint(endPoint);
|
||||
endPoint = endPoint - _translation;
|
||||
glTranslatef(endPoint.x, endPoint.y, endPoint.z);
|
||||
glColor4f(0.6f, 0.6f, 0.8f, alpha);
|
||||
glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
||||
// draw a yellow sphere at the capsule startpoint
|
||||
glm::vec3 startPoint;
|
||||
_boundingShape.getStartPoint(startPoint);
|
||||
startPoint = startPoint - _translation;
|
||||
glm::vec3 axis = endPoint - startPoint;
|
||||
glTranslatef(-axis.x, -axis.y, -axis.z);
|
||||
glColor4f(0.8f, 0.8f, 0.6f, alpha);
|
||||
glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.0f);
|
||||
glColor4f(0.6f, 0.8f, 0.6f, alpha);
|
||||
Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius());
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ public:
|
|||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
virtual void updateVisibleJointStates();
|
||||
|
||||
// virtual overrride from Ragdoll
|
||||
virtual void stepRagdollForward(float deltaTime);
|
||||
|
@ -104,7 +106,7 @@ public:
|
|||
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||
|
||||
void resetShapePositions(); // DEBUG method
|
||||
void resetShapePositionsToDefaultPose(); // DEBUG method
|
||||
|
||||
void renderRagdoll();
|
||||
protected:
|
||||
|
|
68
interface/src/devices/MIDIManager.cpp
Normal file
68
interface/src/devices/MIDIManager.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// MIDIManager.cpp
|
||||
//
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "MIDIManager.h"
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
|
||||
MIDIManager& MIDIManager::getInstance() {
|
||||
static MIDIManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
void MIDIManager::midiCallback(double deltaTime, std::vector<unsigned char>* message, void* userData) {
|
||||
|
||||
MIDIEvent callbackEvent;
|
||||
callbackEvent.deltaTime = deltaTime;
|
||||
|
||||
callbackEvent.type = message->at(0);
|
||||
|
||||
if (message->size() > 1) {
|
||||
callbackEvent.data1 = message->at(1);
|
||||
}
|
||||
|
||||
if (message->size() > 2) {
|
||||
callbackEvent.data2 = message->at(2);
|
||||
}
|
||||
|
||||
emit getInstance().midiEvent(callbackEvent);
|
||||
}
|
||||
|
||||
MIDIManager::~MIDIManager() {
|
||||
delete _midiInput;
|
||||
}
|
||||
|
||||
const int DEFAULT_MIDI_PORT = 0;
|
||||
|
||||
void MIDIManager::openDefaultPort() {
|
||||
if (!_midiInput) {
|
||||
_midiInput = new RtMidiIn();
|
||||
|
||||
if (_midiInput->getPortCount() > 0) {
|
||||
qDebug() << "MIDIManager opening port" << DEFAULT_MIDI_PORT;
|
||||
|
||||
_midiInput->openPort(DEFAULT_MIDI_PORT);
|
||||
|
||||
// don't ignore sysex, timing, or active sensing messages
|
||||
_midiInput->ignoreTypes(false, false, false);
|
||||
|
||||
_midiInput->setCallback(&MIDIManager::midiCallback);
|
||||
} else {
|
||||
qDebug() << "MIDIManager openDefaultPort called but there are no ports available.";
|
||||
delete _midiInput;
|
||||
_midiInput = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
56
interface/src/devices/MIDIManager.h
Normal file
56
interface/src/devices/MIDIManager.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// MIDIManager.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MIDIManager_h
|
||||
#define hifi_MIDIManager_h
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <MIDIEvent.h>
|
||||
|
||||
#include <RtMidi.h>
|
||||
|
||||
class MIDIManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(unsigned int NoteOn READ NoteOn)
|
||||
Q_PROPERTY(unsigned int NoteOff READ NoteOff)
|
||||
Q_PROPERTY(unsigned int ModWheel READ ModWheel)
|
||||
Q_PROPERTY(unsigned int PitchWheel READ PitchWheel)
|
||||
|
||||
public:
|
||||
static MIDIManager& getInstance();
|
||||
static void midiCallback(double deltaTime, std::vector<unsigned char>* message, void* userData);
|
||||
|
||||
~MIDIManager();
|
||||
|
||||
void openDefaultPort();
|
||||
bool hasDevice() const { return !!_midiInput; }
|
||||
public slots:
|
||||
unsigned int NoteOn() const { return 144; }
|
||||
unsigned int NoteOff() const { return 128; }
|
||||
unsigned int ModWheel() const { return 176; }
|
||||
unsigned int PitchWheel() const { return 224; }
|
||||
signals:
|
||||
void midiEvent(const MIDIEvent& event);
|
||||
|
||||
private:
|
||||
RtMidiIn* _midiInput;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // hifi_MIDIManager_h
|
||||
|
|
@ -266,7 +266,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
// We only need to render the overlays to a texture once, then we just render the texture on the hemisphere
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays);
|
||||
|
|
|
@ -71,6 +71,16 @@ void SixenseManager::setFilter(bool filter) {
|
|||
|
||||
void SixenseManager::update(float deltaTime) {
|
||||
#ifdef HAVE_SIXENSE
|
||||
// if the controllers haven't been moved in a while, disable
|
||||
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
||||
Hand* hand = Application::getInstance()->getAvatar()->getHand();
|
||||
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
|
||||
it->setActive(false);
|
||||
}
|
||||
_lastMovement = usecTimestampNow();
|
||||
}
|
||||
|
||||
if (sixenseGetNumActiveControllers() == 0) {
|
||||
_hydrasConnected = false;
|
||||
return;
|
||||
|
@ -154,6 +164,11 @@ void SixenseManager::update(float deltaTime) {
|
|||
// no latency.
|
||||
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
|
||||
palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter));
|
||||
|
||||
// adjustment for hydra controllers fit into hands
|
||||
float sign = (i == 0) ? -1.0f : 1.0f;
|
||||
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter));
|
||||
|
||||
// use the velocity to determine whether there's any movement (if the hand isn't new)
|
||||
|
@ -180,14 +195,6 @@ void SixenseManager::update(float deltaTime) {
|
|||
if (numActiveControllers == 2) {
|
||||
updateCalibration(controllers);
|
||||
}
|
||||
|
||||
// if the controllers haven't been moved in a while, disable
|
||||
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
||||
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
|
||||
it->setActive(false);
|
||||
}
|
||||
}
|
||||
#endif // HAVE_SIXENSE
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,18 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
int portalW = Application::getInstance()->getGLWidget()->width() / 2;
|
||||
int portalH = Application::getInstance()->getGLWidget()->height();
|
||||
|
||||
const bool glowEnabled = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->prepare();
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
@ -102,13 +114,21 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
|
||||
glPushMatrix();
|
||||
{
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum
|
||||
GLfloat p[4][4];
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
|
||||
GLfloat cotangent = p[1][1];
|
||||
GLfloat fov = atan(1.0f / cotangent);
|
||||
glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
@ -124,14 +144,25 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum
|
||||
GLfloat p[4][4];
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
|
||||
GLfloat cotangent = p[1][1];
|
||||
GLfloat fov = atan(1.0f / cotangent);
|
||||
glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// reset the viewport to how we started
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height());
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include <AngularConstraint.h>
|
||||
//#include <GeometryUtil.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
@ -18,7 +19,26 @@
|
|||
|
||||
JointState::JointState() :
|
||||
_animationPriority(0.0f),
|
||||
_fbxJoint(NULL) {
|
||||
_fbxJoint(NULL),
|
||||
_constraint(NULL) {
|
||||
}
|
||||
|
||||
JointState::JointState(const JointState& other) : _constraint(NULL) {
|
||||
_transform = other._transform;
|
||||
_rotation = other._rotation;
|
||||
_rotationInParentFrame = other._rotationInParentFrame;
|
||||
_animationPriority = other._animationPriority;
|
||||
_fbxJoint = other._fbxJoint;
|
||||
// DO NOT copy _constraint
|
||||
}
|
||||
|
||||
JointState::~JointState() {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
|
@ -26,14 +46,34 @@ void JointState::setFBXJoint(const FBXJoint* joint) {
|
|||
_rotationInParentFrame = joint->rotation;
|
||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||
_fbxJoint = joint;
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::updateConstraint() {
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
if (glm::distance2(glm::vec3(-PI), _fbxJoint->rotationMin) > EPSILON ||
|
||||
glm::distance2(glm::vec3(PI), _fbxJoint->rotationMax) > EPSILON ) {
|
||||
// this joint has rotation constraints
|
||||
_constraint = AngularConstraint::newAngularConstraint(_fbxJoint->rotationMin, _fbxJoint->rotationMax);
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::copyState(const JointState& state) {
|
||||
_rotationInParentFrame = state._rotationInParentFrame;
|
||||
_animationPriority = state._animationPriority;
|
||||
_transform = state._transform;
|
||||
_rotation = extractRotation(_transform);
|
||||
_animationPriority = state._animationPriority;
|
||||
// DO NOT copy _fbxJoint
|
||||
_rotationInParentFrame = state._rotationInParentFrame;
|
||||
|
||||
_visibleTransform = state._visibleTransform;
|
||||
_visibleRotation = extractRotation(_visibleTransform);
|
||||
_visibleRotationInParentFrame = state._visibleRotationInParentFrame;
|
||||
// DO NOT copy _fbxJoint or _constraint
|
||||
}
|
||||
|
||||
void JointState::computeTransform(const glm::mat4& parentTransform) {
|
||||
|
@ -43,6 +83,13 @@ void JointState::computeTransform(const glm::mat4& parentTransform) {
|
|||
_rotation = extractRotation(_transform);
|
||||
}
|
||||
|
||||
void JointState::computeVisibleTransform(const glm::mat4& parentTransform) {
|
||||
glm::quat modifiedRotation = _fbxJoint->preRotation * _visibleRotationInParentFrame * _fbxJoint->postRotation;
|
||||
glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(modifiedRotation) * _fbxJoint->postTransform;
|
||||
_visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform;
|
||||
_visibleRotation = extractRotation(_visibleTransform);
|
||||
}
|
||||
|
||||
glm::quat JointState::getRotationFromBindToModelFrame() const {
|
||||
return _rotation * _fbxJoint->inverseBindRotation;
|
||||
}
|
||||
|
@ -50,16 +97,20 @@ glm::quat JointState::getRotationFromBindToModelFrame() const {
|
|||
void JointState::restoreRotation(float fraction, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
||||
_rotationInParentFrame = safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction);
|
||||
setRotationInParentFrame(safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction));
|
||||
_animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) {
|
||||
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain) {
|
||||
// rotation is from bind- to model-frame
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority >= _animationPriority) {
|
||||
// rotation is from bind- to model-frame
|
||||
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
|
||||
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
|
||||
}
|
||||
setRotationInParentFrame(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +119,9 @@ void JointState::clearTransformTranslation() {
|
|||
_transform[3][0] = 0.0f;
|
||||
_transform[3][1] = 0.0f;
|
||||
_transform[3][2] = 0.0f;
|
||||
_visibleTransform[3][0] = 0.0f;
|
||||
_visibleTransform[3][1] = 0.0f;
|
||||
_visibleTransform[3][2] = 0.0f;
|
||||
}
|
||||
|
||||
void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) {
|
||||
|
@ -75,27 +129,61 @@ void JointState::setRotation(const glm::quat& rotation, bool constrain, float pr
|
|||
}
|
||||
|
||||
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
|
||||
// NOTE: delta is in jointParent-frame
|
||||
// NOTE: delta is in model-frame
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority < _animationPriority) {
|
||||
return;
|
||||
}
|
||||
_animationPriority = priority;
|
||||
if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) &&
|
||||
_fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) {
|
||||
if (!constrain || _constraint == NULL) {
|
||||
// no constraints
|
||||
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||
_rotation = delta * _rotation;
|
||||
return;
|
||||
}
|
||||
glm::quat targetRotation = delta * _rotation;
|
||||
glm::vec3 eulers = safeEulerAngles(_rotationInParentFrame * glm::inverse(_rotation) * targetRotation);
|
||||
glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax));
|
||||
_rotation = _rotation * glm::inverse(_rotationInParentFrame) * newRotation;
|
||||
_rotationInParentFrame = newRotation;
|
||||
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||
setRotationInParentFrame(targetRotation);
|
||||
}
|
||||
|
||||
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||
/// This helps keep an IK solution stable.
|
||||
void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) {
|
||||
// NOTE: delta is in model-frame
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority < _animationPriority) {
|
||||
return;
|
||||
}
|
||||
_animationPriority = priority;
|
||||
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||
if (mixFactor > 0.0f && mixFactor <= 1.0f) {
|
||||
targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor);
|
||||
}
|
||||
if (_constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
|
||||
}
|
||||
setRotationInParentFrame(targetRotation);
|
||||
}
|
||||
|
||||
glm::quat JointState::computeParentRotation() const {
|
||||
// R = Rp * Rpre * r * Rpost
|
||||
// Rp = R * (Rpre * r * Rpost)^
|
||||
return _rotation * glm::inverse(_fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation);
|
||||
}
|
||||
|
||||
void JointState::setRotationInParentFrame(const glm::quat& targetRotation) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
_rotationInParentFrame = targetRotation;
|
||||
// R' = Rp * Rpre * r' * Rpost
|
||||
_rotation = parentRotation * _fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation;
|
||||
}
|
||||
|
||||
const glm::vec3& JointState::getDefaultTranslationInParentFrame() const {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _fbxJoint->translation;
|
||||
}
|
||||
|
||||
void JointState::slaveVisibleTransform() {
|
||||
_visibleTransform = _transform;
|
||||
_visibleRotation = _rotation;
|
||||
_visibleRotationInParentFrame = _rotationInParentFrame;
|
||||
}
|
||||
|
|
|
@ -18,16 +18,27 @@
|
|||
|
||||
#include <FBXReader.h>
|
||||
|
||||
class AngularConstraint;
|
||||
|
||||
class JointState {
|
||||
public:
|
||||
JointState();
|
||||
JointState(const JointState& other);
|
||||
~JointState();
|
||||
|
||||
void setFBXJoint(const FBXJoint* joint);
|
||||
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
|
||||
|
||||
void updateConstraint();
|
||||
void copyState(const JointState& state);
|
||||
|
||||
void computeTransform(const glm::mat4& parentTransform);
|
||||
|
||||
void computeVisibleTransform(const glm::mat4& parentTransform);
|
||||
const glm::mat4& getVisibleTransform() const { return _visibleTransform; }
|
||||
glm::quat getVisibleRotation() const { return _visibleRotation; }
|
||||
glm::vec3 getVisiblePosition() const { return extractTranslation(_visibleTransform); }
|
||||
|
||||
const glm::mat4& getTransform() const { return _transform; }
|
||||
|
||||
glm::quat getRotation() const { return _rotation; }
|
||||
|
@ -39,28 +50,56 @@ public:
|
|||
/// \param rotation rotation of joint in model-frame
|
||||
void setRotation(const glm::quat& rotation, bool constrain, float priority);
|
||||
|
||||
/// \param delta is in the jointParent-frame
|
||||
/// \param delta is in the model-frame
|
||||
void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||
|
||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||
/// This helps keep an IK solution stable.
|
||||
/// \param delta rotation change in model-frame
|
||||
/// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
|
||||
/// \param priority priority level of this animation blend
|
||||
void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f);
|
||||
|
||||
/// Blends a fraciton of default pose into joint rotation.
|
||||
/// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
|
||||
/// \param priority priority level of this animation blend
|
||||
void restoreRotation(float fraction, float priority);
|
||||
|
||||
/// \param rotation is from bind- to model-frame
|
||||
/// computes and sets new _rotationInParentFrame
|
||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationFromBindFrame(const glm::quat& rotation, float priority);
|
||||
void setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
void setRotationInParentFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; }
|
||||
|
||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||
|
||||
|
||||
void clearTransformTranslation();
|
||||
|
||||
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame
|
||||
void slaveVisibleTransform();
|
||||
|
||||
float _animationPriority; // the priority of the animation affecting this joint
|
||||
|
||||
private:
|
||||
/// \return parent model-frame rotation
|
||||
// (used to keep _rotation consistent when modifying _rotationInWorldFrame directly)
|
||||
glm::quat computeParentRotation() const;
|
||||
|
||||
/// debug helper function
|
||||
void loadBindRotation();
|
||||
|
||||
glm::mat4 _transform; // joint- to model-frame
|
||||
glm::quat _rotation; // joint- to model-frame
|
||||
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame
|
||||
|
||||
glm::mat4 _visibleTransform;
|
||||
glm::quat _visibleRotation;
|
||||
glm::quat _visibleRotationInParentFrame;
|
||||
|
||||
const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
|
||||
AngularConstraint* _constraint; // JointState owns its AngularConstraint
|
||||
};
|
||||
|
||||
#endif // hifi_JointState_h
|
||||
|
|
|
@ -39,8 +39,8 @@ Model::Model(QObject* parent) :
|
|||
_scaledToFit(false),
|
||||
_snapModelToCenter(false),
|
||||
_snappedToCenter(false),
|
||||
_showTrueJointTransforms(false),
|
||||
_rootIndex(-1),
|
||||
//_enableCollisionShapes(false),
|
||||
_lodDistance(0.0f),
|
||||
_pupilDilation(0.0f),
|
||||
_url("http://invalid.com") {
|
||||
|
@ -460,7 +460,7 @@ void Model::reset() {
|
|||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i]._rotationInParentFrame = geometry.joints.at(i).rotation;
|
||||
_jointStates[i].setRotationInParentFrame(geometry.joints.at(i).rotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,8 +561,6 @@ bool Model::updateGeometry() {
|
|||
void Model::setJointStates(QVector<JointState> states) {
|
||||
_jointStates = states;
|
||||
|
||||
// compute an approximate bounding radius for broadphase collision queries
|
||||
// against PhysicsSimulation boundaries
|
||||
int numJoints = _jointStates.size();
|
||||
float radius = 0.0f;
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
|
@ -570,6 +568,10 @@ void Model::setJointStates(QVector<JointState> states) {
|
|||
if (distance > radius) {
|
||||
radius = distance;
|
||||
}
|
||||
_jointStates[i].updateConstraint();
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
}
|
||||
_boundingRadius = radius;
|
||||
}
|
||||
|
@ -686,7 +688,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
|
|||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates.at(index)._rotationInParentFrame;
|
||||
rotation = _jointStates.at(index).getRotationInParentFrame();
|
||||
const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation;
|
||||
return glm::abs(rotation.x - defaultRotation.x) >= EPSILON ||
|
||||
glm::abs(rotation.y - defaultRotation.y) >= EPSILON ||
|
||||
|
@ -699,7 +701,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
|
|||
JointState& state = _jointStates[index];
|
||||
if (priority >= state._animationPriority) {
|
||||
if (valid) {
|
||||
state._rotationInParentFrame = rotation;
|
||||
state.setRotationInParentFrame(rotation);
|
||||
state._animationPriority = priority;
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
|
@ -765,6 +767,23 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
// position is in world-frame
|
||||
position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _rotation * _jointStates[jointIndex].getVisibleRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList Model::getJointNames() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
|
@ -918,6 +937,8 @@ void Model::simulateInternal(float deltaTime) {
|
|||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i);
|
||||
}
|
||||
updateVisibleJointStates();
|
||||
|
||||
_shapesAreDirty = ! _shapes.isEmpty();
|
||||
|
||||
// update the attachment transforms and simulate them
|
||||
|
@ -928,8 +949,13 @@ void Model::simulateInternal(float deltaTime) {
|
|||
|
||||
glm::vec3 jointTranslation = _translation;
|
||||
glm::quat jointRotation = _rotation;
|
||||
getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
||||
getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
||||
if (_showTrueJointTransforms) {
|
||||
getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
||||
getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
||||
} else {
|
||||
getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
||||
getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
||||
}
|
||||
|
||||
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
|
@ -944,9 +970,16 @@ void Model::simulateInternal(float deltaTime) {
|
|||
for (int i = 0; i < _meshStates.size(); i++) {
|
||||
MeshState& state = _meshStates[i];
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
|
||||
if (_showTrueJointTransforms) {
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,6 +1005,14 @@ void Model::updateJointState(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::updateVisibleJointStates() {
|
||||
if (!_showTrueJointTransforms) {
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
|
@ -1058,6 +1099,121 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
|
|||
return true;
|
||||
}
|
||||
|
||||
void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {
|
||||
// NOTE: targetRotation is from bind- to model-frame
|
||||
|
||||
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
|
||||
if (freeLineage.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int numFree = freeLineage.size();
|
||||
|
||||
// store and remember topmost parent transform
|
||||
glm::mat4 topParentTransform;
|
||||
{
|
||||
int index = freeLineage.last();
|
||||
const JointState& state = _jointStates.at(index);
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
} else {
|
||||
topParentTransform = _jointStates[parentIndex].getTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
|
||||
// keep track of the position of the end-effector
|
||||
JointState& endState = _jointStates[endIndex];
|
||||
glm::vec3 endPosition = endState.getPosition();
|
||||
float distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
|
||||
const int MAX_ITERATION_COUNT = 2;
|
||||
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
|
||||
int numIterations = 0;
|
||||
do {
|
||||
++numIterations;
|
||||
// moving up, rotate each free joint to get endPosition closer to target
|
||||
for (int j = 1; j < numFree; j++) {
|
||||
int nextIndex = freeLineage.at(j);
|
||||
JointState& nextState = _jointStates[nextIndex];
|
||||
FBXJoint nextJoint = nextState.getFBXJoint();
|
||||
if (! nextJoint.isFree) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 pivot = nextState.getPosition();
|
||||
glm::vec3 leverArm = endPosition - pivot;
|
||||
float leverLength = glm::length(leverArm);
|
||||
if (leverLength < EPSILON) {
|
||||
continue;
|
||||
}
|
||||
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
|
||||
|
||||
// We want to mix the shortest rotation with one that will pull the system down with gravity
|
||||
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
|
||||
// where each joint has unit mass and we don't bother averaging it because we only need direction.
|
||||
if (j > 1) {
|
||||
|
||||
glm::vec3 centerOfMass(0.0f);
|
||||
for (int k = 0; k < j; ++k) {
|
||||
int massIndex = freeLineage.at(k);
|
||||
centerOfMass += _jointStates[massIndex].getPosition() - pivot;
|
||||
}
|
||||
// the gravitational effect is a rotation that tends to align the two cross products
|
||||
const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.f, 0.0f);
|
||||
glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
|
||||
glm::cross(worldAlignment, leverArm));
|
||||
|
||||
float gravityAngle = glm::angle(gravityDelta);
|
||||
const float MIN_GRAVITY_ANGLE = 0.1f;
|
||||
float mixFactor = 0.5f;
|
||||
if (gravityAngle < MIN_GRAVITY_ANGLE) {
|
||||
// the final rotation is a mix of the two
|
||||
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
|
||||
}
|
||||
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
|
||||
}
|
||||
|
||||
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// at in the process. This provides stability to the IK solution for most models.
|
||||
glm::quat oldNextRotation = nextState.getRotation();
|
||||
float mixFactor = 0.03f;
|
||||
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||
|
||||
// measure the result of the rotation which may have been modified by
|
||||
// blending and constraints
|
||||
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
|
||||
endPosition = pivot + actualDelta * leverArm;
|
||||
}
|
||||
|
||||
// recompute transforms from the top down
|
||||
glm::mat4 parentTransform = topParentTransform;
|
||||
for (int j = numFree - 1; j >= 0; --j) {
|
||||
JointState& freeState = _jointStates[freeLineage.at(j)];
|
||||
freeState.computeTransform(parentTransform);
|
||||
parentTransform = freeState.getTransform();
|
||||
}
|
||||
|
||||
// measure our success
|
||||
endPosition = endState.getPosition();
|
||||
distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||
|
||||
// set final rotation of the end joint
|
||||
endState.setRotationFromBindFrame(targetRotation, priority, true);
|
||||
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
}
|
||||
|
||||
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
|
@ -1545,6 +1701,13 @@ AnimationHandle::AnimationHandle(Model* model) :
|
|||
_running(false) {
|
||||
}
|
||||
|
||||
AnimationDetails AnimationHandle::getAnimationDetails() const {
|
||||
AnimationDetails details(_role, _url, _fps, _priority, _loop, _hold,
|
||||
_startAutomatically, _firstFrame, _lastFrame, _running, _frameIndex);
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
void AnimationHandle::simulate(float deltaTime) {
|
||||
_frameIndex += deltaTime * _fps;
|
||||
|
||||
|
@ -1605,7 +1768,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
if (_priority >= state._animationPriority) {
|
||||
state._rotationInParentFrame = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
state.setRotationInParentFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction));
|
||||
state._animationPriority = _priority;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,9 @@ public:
|
|||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
||||
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
|
||||
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
/// \param jointIndex index of joint in model structure
|
||||
/// \param position[out] position of joint in model-frame
|
||||
/// \return true if joint exists
|
||||
|
@ -152,6 +155,7 @@ protected:
|
|||
|
||||
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
|
||||
bool _snappedToCenter; /// are we currently snapped to center
|
||||
bool _showTrueJointTransforms;
|
||||
int _rootIndex;
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
@ -176,6 +180,8 @@ protected:
|
|||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
virtual void updateVisibleJointStates();
|
||||
|
||||
/// \param jointIndex index of joint in model structure
|
||||
/// \param position position of joint in model-frame
|
||||
|
@ -188,6 +194,8 @@ protected:
|
|||
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(),
|
||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
|
||||
|
||||
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
|
||||
|
||||
/// Restores the indexed joint to its default position.
|
||||
/// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
||||
|
@ -346,6 +354,11 @@ public:
|
|||
void setRunning(bool running);
|
||||
bool isRunning() const { return _running; }
|
||||
|
||||
void setFrameIndex(float frameIndex) { _frameIndex = glm::clamp(_frameIndex, _firstFrame, _lastFrame); }
|
||||
float getFrameIndex() const { return _frameIndex; }
|
||||
|
||||
AnimationDetails getAnimationDetails() const;
|
||||
|
||||
signals:
|
||||
|
||||
void runningChanged(bool running);
|
||||
|
|
|
@ -85,6 +85,33 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) {
|
|||
}
|
||||
}
|
||||
|
||||
// use fixed table of permutations. Could also make ordered list programmatically
|
||||
// and then shuffle algorithm. For testing, this ensures consistent behavior in each run.
|
||||
// this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at
|
||||
// http://mrl.nyu.edu/~perlin/noise/
|
||||
|
||||
const int permutation[256] =
|
||||
{
|
||||
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
|
||||
140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148,
|
||||
247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32,
|
||||
57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
|
||||
74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122,
|
||||
60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,
|
||||
65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
|
||||
200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64,
|
||||
52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
|
||||
207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
|
||||
119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
|
||||
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
|
||||
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241,
|
||||
81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
|
||||
184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
|
||||
222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
|
||||
};
|
||||
|
||||
#define USE_CHRIS_NOISE 1
|
||||
|
||||
GLuint TextureCache::getPermutationNormalTextureID() {
|
||||
if (_permutationNormalTextureID == 0) {
|
||||
glGenTextures(1, &_permutationNormalTextureID);
|
||||
|
@ -92,10 +119,17 @@ GLuint TextureCache::getPermutationNormalTextureID() {
|
|||
|
||||
// the first line consists of random permutation offsets
|
||||
unsigned char data[256 * 2 * 3];
|
||||
#if (USE_CHRIS_NOISE==1)
|
||||
for (int i = 0; i < 256; i++) {
|
||||
data[3*i+0] = permutation[i];
|
||||
data[3*i+1] = permutation[i];
|
||||
data[3*i+2] = permutation[i];
|
||||
#else
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
data[i] = rand() % 256;
|
||||
#endif
|
||||
}
|
||||
// the next, random unit normals
|
||||
|
||||
for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {
|
||||
glm::vec3 randvec = glm::sphericalRand(1.0f);
|
||||
data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f;
|
||||
|
@ -105,7 +139,6 @@ GLuint TextureCache::getPermutationNormalTextureID() {
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return _permutationNormalTextureID;
|
||||
|
|
|
@ -41,8 +41,7 @@ ApplicationOverlay::ApplicationOverlay() :
|
|||
_textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE),
|
||||
_alpha(1.0f),
|
||||
_active(true),
|
||||
_crosshairTexture(0)
|
||||
{
|
||||
_crosshairTexture(0) {
|
||||
|
||||
memset(_reticleActive, 0, sizeof(_reticleActive));
|
||||
memset(_magActive, 0, sizeof(_reticleActive));
|
||||
|
@ -206,7 +205,7 @@ void ApplicationOverlay::getClickLocation(int &x, int &y) const {
|
|||
}
|
||||
}
|
||||
|
||||
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane.
|
||||
// Draws the FBO texture for Oculus rift.
|
||||
void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
||||
|
||||
if (_alpha == 0.0f) {
|
||||
|
@ -293,6 +292,107 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
|||
|
||||
}
|
||||
|
||||
// Draws the FBO texture for 3DTV.
|
||||
void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov) {
|
||||
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
Application* application = Application::getInstance();
|
||||
|
||||
MyAvatar* myAvatar = application->getAvatar();
|
||||
const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
// Transform to world space
|
||||
glm::quat rotation = whichCamera.getRotation();
|
||||
glm::vec3 axis2 = glm::axis(rotation);
|
||||
glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
|
||||
glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
|
||||
|
||||
// Translate to the front of the camera
|
||||
glm::vec3 pos = whichCamera.getPosition();
|
||||
glm::quat rot = myAvatar->getOrientation();
|
||||
glm::vec3 axis = glm::axis(rot);
|
||||
|
||||
glTranslatef(pos.x, pos.y, pos.z);
|
||||
glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, _alpha);
|
||||
|
||||
//Render
|
||||
// fov -= RADIANS_PER_DEGREE * 2.5f; //reduce by 5 degrees so it fits in the view
|
||||
const GLfloat distance = 1.0f;
|
||||
|
||||
const GLfloat halfQuadHeight = distance * tan(fov);
|
||||
const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;
|
||||
const GLfloat quadWidth = halfQuadWidth * 2.0f;
|
||||
const GLfloat quadHeight = halfQuadHeight * 2.0f;
|
||||
|
||||
GLfloat x = -halfQuadWidth;
|
||||
GLfloat y = -halfQuadHeight;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + quadHeight, -distance);
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f(x + quadWidth, y + quadHeight, -distance);
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f(x + quadWidth, y, -distance);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, -distance);
|
||||
|
||||
glEnd();
|
||||
|
||||
if (_crosshairTexture == 0) {
|
||||
_crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png"));
|
||||
}
|
||||
|
||||
//draw the mouse pointer
|
||||
glBindTexture(GL_TEXTURE_2D, _crosshairTexture);
|
||||
|
||||
const float reticleSize = 40.0f / application->getGLWidget()->width() * quadWidth;
|
||||
x -= reticleSize / 2.0f;
|
||||
y += reticleSize / 2.0f;
|
||||
const float mouseX = (application->getMouseX() / (float)application->getGLWidget()->width()) * quadWidth;
|
||||
const float mouseY = (1.0 - (application->getMouseY() / (float)application->getGLWidget()->height())) * quadHeight;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]);
|
||||
|
||||
glTexCoord2d(0.0f, 0.0f); glVertex3f(x + mouseX, y + mouseY, -distance);
|
||||
glTexCoord2d(1.0f, 0.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY, -distance);
|
||||
glTexCoord2d(1.0f, 1.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance);
|
||||
glTexCoord2d(0.0f, 1.0f); glVertex3f(x + mouseX, y + mouseY - reticleSize, -distance);
|
||||
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
//Renders optional pointers
|
||||
void ApplicationOverlay::renderPointers() {
|
||||
Application* application = Application::getInstance();
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
void renderOverlay(bool renderToTexture = false);
|
||||
void displayOverlayTexture();
|
||||
void displayOverlayTextureOculus(Camera& whichCamera);
|
||||
void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov);
|
||||
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
|
||||
void getClickLocation(int &x, int &y) const;
|
||||
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
#include <QGridLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QUrl>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "ModelsBrowser.h"
|
||||
|
@ -210,10 +211,10 @@ void ModelHandler::update() {
|
|||
}
|
||||
for (int i = 0; i < _model.rowCount(); ++i) {
|
||||
QUrl url(_model.item(i,0)->data(Qt::UserRole).toString());
|
||||
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this);
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
accessManager->head(request);
|
||||
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
|
||||
QNetworkReply* reply = networkAccessManager.head(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
}
|
||||
_lock.unlock();
|
||||
}
|
||||
|
@ -233,7 +234,8 @@ void ModelHandler::exit() {
|
|||
_lock.unlock();
|
||||
}
|
||||
|
||||
void ModelHandler::downloadFinished(QNetworkReply* reply) {
|
||||
void ModelHandler::downloadFinished() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
if (!data.isEmpty()) {
|
||||
|
@ -261,10 +263,10 @@ void ModelHandler::queryNewFiles(QString marker) {
|
|||
|
||||
// Download
|
||||
url.setQuery(query);
|
||||
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this);
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
accessManager->get(request);
|
||||
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public slots:
|
|||
void exit();
|
||||
|
||||
private slots:
|
||||
void downloadFinished(QNetworkReply* reply);
|
||||
void downloadFinished();
|
||||
|
||||
private:
|
||||
bool _initiateExit;
|
||||
|
|
|
@ -148,8 +148,8 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << scriptPath;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
|
|
@ -40,8 +40,7 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
|||
|
||||
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
_fileName(fileName),
|
||||
_networkAccessManager(NULL)
|
||||
_fileName(fileName)
|
||||
{
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
@ -92,10 +91,6 @@ void SnapshotShareDialog::uploadSnapshot() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
|
||||
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart apiKeyPart;
|
||||
|
@ -118,9 +113,7 @@ void SnapshotShareDialog::uploadSnapshot() {
|
|||
QUrl url(FORUM_UPLOADS_URL);
|
||||
QNetworkRequest request(url);
|
||||
|
||||
QNetworkReply* reply = _networkAccessManager->post(request, multiPart);
|
||||
|
||||
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().post(request, multiPart);
|
||||
connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished);
|
||||
|
||||
QEventLoop loop;
|
||||
|
@ -129,11 +122,6 @@ void SnapshotShareDialog::uploadSnapshot() {
|
|||
}
|
||||
|
||||
void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
|
||||
// post to Discourse forum
|
||||
QNetworkRequest request;
|
||||
QUrl forumUrl(FORUM_POST_URL);
|
||||
|
@ -148,7 +136,7 @@ void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
|
|||
request.setUrl(forumUrl);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
|
||||
QNetworkReply* requestReply = NetworkAccessManager::getInstance().post(request, postData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::postRequestFinished);
|
||||
|
||||
QEventLoop loop;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include "ui_shareSnapshot.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
|
@ -26,7 +25,6 @@ public:
|
|||
|
||||
private:
|
||||
QString _fileName;
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
Ui_SnapshotShareDialog _ui;
|
||||
|
||||
void uploadSnapshot();
|
||||
|
|
77
interface/src/ui/UserLocationsDialog.cpp
Normal file
77
interface/src/ui/UserLocationsDialog.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// UserLocationsDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 06/24/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <QInputDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "Menu.h"
|
||||
#include "UserLocationsDialog.h"
|
||||
|
||||
UserLocationsDialog::UserLocationsDialog(QWidget* parent) :
|
||||
QDialog(parent),
|
||||
_ui(),
|
||||
_proxyModel(this),
|
||||
_userLocationsModel(this) {
|
||||
|
||||
_ui.setupUi(this);
|
||||
|
||||
_proxyModel.setSourceModel(&_userLocationsModel);
|
||||
_proxyModel.setDynamicSortFilter(true);
|
||||
|
||||
_ui.locationsTreeView->setModel(&_proxyModel);
|
||||
_ui.locationsTreeView->setSortingEnabled(true);
|
||||
_ui.locationsTreeView->sortByColumn(UserLocationsModel::NameColumn, Qt::AscendingOrder);
|
||||
|
||||
connect(_ui.locationsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &UserLocationsDialog::updateEnabled);
|
||||
connect(&_userLocationsModel, &UserLocationsModel::modelReset, this, &UserLocationsDialog::updateEnabled);
|
||||
connect(&_userLocationsModel, &UserLocationsModel::modelReset, &_proxyModel, &QSortFilterProxyModel::invalidate);
|
||||
connect(_ui.locationsTreeView, &QTreeView::doubleClicked, this, &UserLocationsDialog::goToModelIndex);
|
||||
|
||||
connect(_ui.deleteButton, &QPushButton::clicked, this, &UserLocationsDialog::deleteSelection);
|
||||
connect(_ui.renameButton, &QPushButton::clicked, this, &UserLocationsDialog::renameSelection);
|
||||
connect(_ui.refreshButton, &QPushButton::clicked, &_userLocationsModel, &UserLocationsModel::refresh);
|
||||
|
||||
this->setWindowTitle("My Locations");
|
||||
}
|
||||
|
||||
void UserLocationsDialog::updateEnabled() {
|
||||
bool enabled = _ui.locationsTreeView->selectionModel()->hasSelection();
|
||||
_ui.renameButton->setEnabled(enabled);
|
||||
_ui.deleteButton->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void UserLocationsDialog::goToModelIndex(const QModelIndex& index) {
|
||||
QVariant location = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::LocationColumn));
|
||||
Menu::getInstance()->goToURL(location.toString());
|
||||
}
|
||||
|
||||
void UserLocationsDialog::deleteSelection() {
|
||||
QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex();
|
||||
selection = _proxyModel.mapToSource(selection);
|
||||
if (selection.isValid()) {
|
||||
_userLocationsModel.deleteLocation(selection);
|
||||
}
|
||||
}
|
||||
|
||||
void UserLocationsDialog::renameSelection() {
|
||||
QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex();
|
||||
selection = _proxyModel.mapToSource(selection);
|
||||
if (selection.isValid()) {
|
||||
bool ok;
|
||||
QString name = _userLocationsModel.data(selection.sibling(selection.row(), UserLocationsModel::NameColumn)).toString();
|
||||
QString newName = QInputDialog::getText(this, "Rename '" + name + "'", "Set name to:", QLineEdit::Normal, name, &ok);
|
||||
if (ok && !newName.isEmpty()) {
|
||||
_userLocationsModel.renameLocation(selection, newName);
|
||||
}
|
||||
}
|
||||
}
|
35
interface/src/ui/UserLocationsDialog.h
Normal file
35
interface/src/ui/UserLocationsDialog.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// UserLocationsDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 06/24/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_UserLocationsDialog_h
|
||||
#define hifi_UserLocationsDialog_h
|
||||
|
||||
#include "ui_userLocationsDialog.h"
|
||||
#include "UserLocationsModel.h"
|
||||
|
||||
class UserLocationsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
UserLocationsDialog(QWidget* parent = NULL);
|
||||
|
||||
protected slots:
|
||||
void updateEnabled();
|
||||
void goToModelIndex(const QModelIndex& index);
|
||||
void deleteSelection();
|
||||
void renameSelection();
|
||||
|
||||
private:
|
||||
Ui::UserLocationsDialog _ui;
|
||||
QSortFilterProxyModel _proxyModel;
|
||||
UserLocationsModel _userLocationsModel;
|
||||
};
|
||||
|
||||
#endif // hifi_UserLocationsDialog_h
|
|
@ -14,8 +14,7 @@
|
|||
#include "BillboardOverlay.h"
|
||||
|
||||
BillboardOverlay::BillboardOverlay()
|
||||
: _manager(NULL),
|
||||
_scale(1.0f),
|
||||
: _scale(1.0f),
|
||||
_isFacingAvatar(true) {
|
||||
}
|
||||
|
||||
|
@ -119,18 +118,13 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
|
||||
void BillboardOverlay::setBillboardURL(const QUrl url) {
|
||||
// TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made?
|
||||
_manager->deleteLater();
|
||||
_manager = new QNetworkAccessManager();
|
||||
connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
||||
_manager->get(QNetworkRequest(url));
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url));
|
||||
connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished);
|
||||
}
|
||||
|
||||
void BillboardOverlay::replyFinished(QNetworkReply* reply) {
|
||||
void BillboardOverlay::replyFinished() {
|
||||
// replace our byte array with the downloaded data
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
_billboard = reply->readAll();
|
||||
_manager->deleteLater();
|
||||
_manager = NULL;
|
||||
}
|
||||
|
|
|
@ -27,12 +27,11 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
|
||||
private slots:
|
||||
void replyFinished(QNetworkReply* reply);
|
||||
void replyFinished();
|
||||
|
||||
private:
|
||||
void setBillboardURL(const QUrl url);
|
||||
|
||||
QNetworkAccessManager* _manager;
|
||||
QUrl _url;
|
||||
QByteArray _billboard;
|
||||
QSize _size;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "ImageOverlay.h"
|
||||
|
||||
ImageOverlay::ImageOverlay() :
|
||||
_manager(NULL),
|
||||
_textureID(0),
|
||||
_renderImage(false),
|
||||
_textureBound(false),
|
||||
|
@ -36,21 +35,18 @@ ImageOverlay::~ImageOverlay() {
|
|||
|
||||
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
|
||||
void ImageOverlay::setImageURL(const QUrl& url) {
|
||||
// TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made?
|
||||
_manager->deleteLater();
|
||||
_manager = new QNetworkAccessManager();
|
||||
connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
||||
_manager->get(QNetworkRequest(url));
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished);
|
||||
}
|
||||
|
||||
void ImageOverlay::replyFinished(QNetworkReply* reply) {
|
||||
|
||||
void ImageOverlay::replyFinished() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
|
||||
// replace our byte array with the downloaded data
|
||||
QByteArray rawData = reply->readAll();
|
||||
_textureImage.loadFromData(rawData);
|
||||
_renderImage = true;
|
||||
_manager->deleteLater();
|
||||
_manager = NULL;
|
||||
}
|
||||
|
||||
void ImageOverlay::render() {
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
|
||||
#include <QGLWidget>
|
||||
#include <QImage>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QRect>
|
||||
#include <QScriptValue>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Overlay.h"
|
||||
|
@ -46,13 +46,12 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
|
||||
private slots:
|
||||
void replyFinished(QNetworkReply* reply); // we actually want to hide this...
|
||||
void replyFinished(); // we actually want to hide this...
|
||||
|
||||
private:
|
||||
|
||||
QUrl _imageURL;
|
||||
QImage _textureImage;
|
||||
QNetworkAccessManager* _manager;
|
||||
|
||||
GLuint _textureID;
|
||||
QRect _fromImage; // where from in the image to sample
|
||||
|
|
|
@ -84,13 +84,13 @@ void Overlays::render3D() {
|
|||
return;
|
||||
}
|
||||
bool myAvatarComputed = false;
|
||||
MyAvatar* avatar;
|
||||
MyAvatar* avatar = NULL;
|
||||
glm::quat myAvatarRotation;
|
||||
glm::vec3 myAvatarPosition;
|
||||
float angle;
|
||||
glm::vec3 axis;
|
||||
float myAvatarScale;
|
||||
|
||||
glm::vec3 myAvatarPosition(0.0f);
|
||||
float angle = 0.0f;
|
||||
glm::vec3 axis(0.0f, 1.0f, 0.0f);
|
||||
float myAvatarScale = 1.0f;
|
||||
|
||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
||||
glPushMatrix();
|
||||
switch (thisOverlay->getAnchor()) {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <QGLWidget>
|
||||
#include <QImage>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QRect>
|
||||
#include <QScriptValue>
|
||||
|
|
|
@ -137,7 +137,7 @@ background: transparent;</string>
|
|||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>75</weight>
|
||||
<italic>false</italic>
|
||||
<bold>true</bold>
|
||||
|
@ -326,9 +326,6 @@ padding-top: 3px;</string>
|
|||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin: 0;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
|
@ -336,7 +333,7 @@ padding-top: 3px;</string>
|
|||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
|
@ -352,7 +349,7 @@ padding-top: 3px;</string>
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>269</width>
|
||||
<width>284</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -460,7 +457,6 @@ font: bold 16px;</string>
|
|||
<string notr="true">background: transparent;
|
||||
font-size: 14px;</string>
|
||||
</property>
|
||||
<zorder>runningScriptsList</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -627,10 +623,10 @@ QListView::item {
|
|||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
130
interface/ui/userLocationsDialog.ui
Normal file
130
interface/ui/userLocationsDialog.ui
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UserLocationsDialog</class>
|
||||
<widget class="QWidget" name="UserLocationsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>929</width>
|
||||
<height>633</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-size: 16px</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>My Locations</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="refreshButton">
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="locationsTreeView">
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="renameButton">
|
||||
<property name="text">
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -102,3 +102,39 @@ void Animation::downloadFinished(QNetworkReply* reply) {
|
|||
QThreadPool::globalInstance()->start(new AnimationReader(_self, reply));
|
||||
}
|
||||
|
||||
|
||||
AnimationDetails::AnimationDetails() :
|
||||
role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false),
|
||||
startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), frameIndex(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
|
||||
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float frameIndex) :
|
||||
role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold),
|
||||
startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame),
|
||||
running(running), frameIndex(frameIndex)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("role", details.role);
|
||||
obj.setProperty("url", details.url.toString());
|
||||
obj.setProperty("fps", details.fps);
|
||||
obj.setProperty("priority", details.priority);
|
||||
obj.setProperty("loop", details.loop);
|
||||
obj.setProperty("hold", details.hold);
|
||||
obj.setProperty("startAutomatically", details.startAutomatically);
|
||||
obj.setProperty("firstFrame", details.firstFrame);
|
||||
obj.setProperty("lastFrame", details.lastFrame);
|
||||
obj.setProperty("running", details.running);
|
||||
obj.setProperty("frameIndex", details.frameIndex);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& details) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#ifndef hifi_AnimationCache_h
|
||||
#define hifi_AnimationCache_h
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValue>
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
#include <FBXReader.h>
|
||||
|
@ -68,4 +71,28 @@ private:
|
|||
bool _isValid;
|
||||
};
|
||||
|
||||
|
||||
class AnimationDetails {
|
||||
public:
|
||||
AnimationDetails();
|
||||
AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
|
||||
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float frameIndex);
|
||||
|
||||
QString role;
|
||||
QUrl url;
|
||||
float fps;
|
||||
float priority;
|
||||
bool loop;
|
||||
bool hold;
|
||||
bool startAutomatically;
|
||||
float firstFrame;
|
||||
float lastFrame;
|
||||
bool running;
|
||||
float frameIndex;
|
||||
};
|
||||
Q_DECLARE_METATYPE(AnimationDetails);
|
||||
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);
|
||||
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event);
|
||||
|
||||
|
||||
#endif // hifi_AnimationCache_h
|
||||
|
|
|
@ -12,6 +12,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
|
|||
|
||||
set(TARGET_NAME audio)
|
||||
|
||||
find_package(Qt5 COMPONENTS Script)
|
||||
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||
|
||||
# set up the external glm library
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
@ -26,4 +29,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
|||
# add a definition for ssize_t so that windows doesn't bail
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
|
@ -19,10 +19,11 @@
|
|||
#include "AudioRingBuffer.h"
|
||||
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) :
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
||||
NodeData(),
|
||||
_overflowCount(0),
|
||||
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
|
||||
_frameCapacity(numFramesCapacity),
|
||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||
_isFull(false),
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_isStarved(true),
|
||||
|
@ -48,20 +49,22 @@ AudioRingBuffer::~AudioRingBuffer() {
|
|||
}
|
||||
|
||||
void AudioRingBuffer::reset() {
|
||||
_overflowCount = 0;
|
||||
_isFull = false;
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
_isStarved = true;
|
||||
}
|
||||
|
||||
void AudioRingBuffer::resizeForFrameSize(qint64 numFrameSamples) {
|
||||
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
|
||||
delete[] _buffer;
|
||||
_sampleCapacity = numFrameSamples * RING_BUFFER_LENGTH_FRAMES;
|
||||
_sampleCapacity = numFrameSamples * _frameCapacity;
|
||||
_numFrameSamples = numFrameSamples;
|
||||
_buffer = new int16_t[_sampleCapacity];
|
||||
if (_randomAccessMode) {
|
||||
memset(_buffer, 0, _sampleCapacity * sizeof(int16_t));
|
||||
}
|
||||
_nextOutput = _buffer;
|
||||
_endOfLastWrite = _buffer;
|
||||
reset();
|
||||
}
|
||||
|
||||
int AudioRingBuffer::parseData(const QByteArray& packet) {
|
||||
|
@ -70,14 +73,14 @@ int AudioRingBuffer::parseData(const QByteArray& packet) {
|
|||
return writeData(packet.data() + numBytesBeforeAudioData, packet.size() - numBytesBeforeAudioData);
|
||||
}
|
||||
|
||||
qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) {
|
||||
int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) {
|
||||
return readData((char*) destination, maxSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) {
|
||||
int AudioRingBuffer::readData(char *data, int maxSize) {
|
||||
|
||||
// only copy up to the number of samples we have available
|
||||
int numReadSamples = std::min((unsigned) (maxSize / sizeof(int16_t)), samplesAvailable());
|
||||
int numReadSamples = std::min((int) (maxSize / sizeof(int16_t)), samplesAvailable());
|
||||
|
||||
// If we're in random access mode, then we consider our number of available read samples slightly
|
||||
// differently. Namely, if anything has been written, we say we have as many samples as they ask for
|
||||
|
@ -118,14 +121,14 @@ qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) {
|
|||
return numReadSamples * sizeof(int16_t);
|
||||
}
|
||||
|
||||
qint64 AudioRingBuffer::writeSamples(const int16_t* source, qint64 maxSamples) {
|
||||
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
|
||||
return writeData((const char*) source, maxSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) {
|
||||
int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
||||
// make sure we have enough bytes left for this to be the right amount of audio
|
||||
// otherwise we should not copy that data, and leave the buffer pointers where they are
|
||||
int samplesToCopy = std::min((quint64)(maxSize / sizeof(int16_t)), (quint64)_sampleCapacity);
|
||||
int samplesToCopy = std::min((int)(maxSize / sizeof(int16_t)), _sampleCapacity);
|
||||
|
||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||
if (samplesToCopy > samplesRoomFor) {
|
||||
|
@ -167,7 +170,7 @@ void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int AudioRingBuffer::samplesAvailable() const {
|
||||
int AudioRingBuffer::samplesAvailable() const {
|
||||
if (!_endOfLastWrite) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -208,7 +211,7 @@ int AudioRingBuffer::addSilentFrame(int numSilentSamples) {
|
|||
return numSilentSamples * sizeof(int16_t);
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const {
|
||||
bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const {
|
||||
if (!_isStarved) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -31,19 +31,19 @@ const int NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL = NETWORK_BUFFER_LENGTH_BYTE
|
|||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
/ (float) SAMPLE_RATE) * 1000 * 1000);
|
||||
|
||||
const short RING_BUFFER_LENGTH_FRAMES = 10;
|
||||
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
||||
const int DEFAULT_RING_BUFFER_FRAME_CAPACITY = 10;
|
||||
|
||||
class AudioRingBuffer : public NodeData {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false);
|
||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
||||
~AudioRingBuffer();
|
||||
|
||||
void reset();
|
||||
void resizeForFrameSize(qint64 numFrameSamples);
|
||||
void resizeForFrameSize(int numFrameSamples);
|
||||
|
||||
int getSampleCapacity() const { return _sampleCapacity; }
|
||||
|
||||
|
@ -53,20 +53,20 @@ public:
|
|||
const int16_t* getNextOutput() const { return _nextOutput; }
|
||||
const int16_t* getBuffer() const { return _buffer; }
|
||||
|
||||
qint64 readSamples(int16_t* destination, qint64 maxSamples);
|
||||
qint64 writeSamples(const int16_t* source, qint64 maxSamples);
|
||||
int readSamples(int16_t* destination, int maxSamples);
|
||||
int writeSamples(const int16_t* source, int maxSamples);
|
||||
|
||||
qint64 readData(char* data, qint64 maxSize);
|
||||
qint64 writeData(const char* data, qint64 maxSize);
|
||||
int readData(char* data, int maxSize);
|
||||
int writeData(const char* data, int maxSize);
|
||||
|
||||
int16_t& operator[](const int index);
|
||||
const int16_t& operator[] (const int index) const;
|
||||
|
||||
void shiftReadPosition(unsigned int numSamples);
|
||||
|
||||
unsigned int samplesAvailable() const;
|
||||
int samplesAvailable() const;
|
||||
|
||||
bool isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const;
|
||||
bool isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const;
|
||||
|
||||
bool isStarved() const { return _isStarved; }
|
||||
void setIsStarved(bool isStarved) { _isStarved = isStarved; }
|
||||
|
@ -84,6 +84,7 @@ protected:
|
|||
|
||||
int _overflowCount; /// how many times has the ring buffer has overwritten old data
|
||||
|
||||
int _frameCapacity;
|
||||
int _sampleCapacity;
|
||||
bool _isFull;
|
||||
int _numFrameSamples;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "InjectedAudioRingBuffer.h"
|
||||
|
||||
InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier, bool dynamicJitterBuffer) :
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, /* isStereo=*/ false , dynamicJitterBuffer),
|
||||
PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, false, dynamicJitterBuffer),
|
||||
_streamIdentifier(streamIdentifier),
|
||||
_radius(0.0f),
|
||||
_attenuationRatio(0)
|
||||
|
|
|
@ -85,10 +85,10 @@ quint64 InterframeTimeGapStats::getWindowMaxGap() {
|
|||
}
|
||||
|
||||
|
||||
PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type,
|
||||
bool isStereo, bool dynamicJitterBuffers) :
|
||||
PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo, bool dynamicJitterBuffers) :
|
||||
|
||||
AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL),
|
||||
AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
||||
false, AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY),
|
||||
_type(type),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
|
@ -98,7 +98,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::
|
|||
_isStereo(isStereo),
|
||||
_listenerUnattenuatedZone(NULL),
|
||||
_desiredJitterBufferFrames(1),
|
||||
_currentJitterBufferFrames(0),
|
||||
_currentJitterBufferFrames(-1),
|
||||
_dynamicJitterBuffers(dynamicJitterBuffers)
|
||||
{
|
||||
}
|
||||
|
@ -212,12 +212,12 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() {
|
|||
}
|
||||
|
||||
return false;
|
||||
} else if (samplesAvailable() < (unsigned int)samplesPerFrame) {
|
||||
} else if (samplesAvailable() < samplesPerFrame) {
|
||||
// if the buffer doesn't have a full frame of samples to take for mixing, it is starved
|
||||
_isStarved = true;
|
||||
|
||||
// set to 0 to indicate the jitter buffer is starved
|
||||
_currentJitterBufferFrames = 0;
|
||||
// set to -1 to indicate the jitter buffer is starved
|
||||
_currentJitterBufferFrames = -1;
|
||||
|
||||
// reset our _shouldOutputStarveDebug to true so the next is printed
|
||||
_shouldOutputStarveDebug = true;
|
||||
|
@ -261,7 +261,7 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() {
|
|||
if (_desiredJitterBufferFrames < 1) {
|
||||
_desiredJitterBufferFrames = 1;
|
||||
}
|
||||
const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1;
|
||||
const int maxDesired = _frameCapacity - 1;
|
||||
if (_desiredJitterBufferFrames > maxDesired) {
|
||||
_desiredJitterBufferFrames = maxDesired;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ private:
|
|||
bool _newWindowMaxGapAvailable;
|
||||
};
|
||||
|
||||
const int AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY = 100;
|
||||
|
||||
class PositionalAudioRingBuffer : public AudioRingBuffer {
|
||||
public:
|
||||
enum Type {
|
||||
|
@ -103,6 +105,11 @@ protected:
|
|||
int _desiredJitterBufferFrames;
|
||||
int _currentJitterBufferFrames;
|
||||
bool _dynamicJitterBuffers;
|
||||
|
||||
// extra stats
|
||||
int _starveCount;
|
||||
int _silentFramesDropped;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_PositionalAudioRingBuffer_h
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
|
||||
#include <QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <qendian.h>
|
||||
|
||||
#include <LimitedNodeList.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
@ -73,11 +73,11 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) :
|
|||
// assume we have a QApplication or QCoreApplication instance and use the
|
||||
// QNetworkAccess manager to grab the raw audio file at the given URL
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
qDebug() << "Requesting audio file" << sampleURL.toDisplayString();
|
||||
|
||||
QNetworkReply* soundDownload = manager->get(QNetworkRequest(sampleURL));
|
||||
QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL));
|
||||
connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished);
|
||||
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -33,8 +33,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
|
|||
|
||||
using namespace std;
|
||||
|
||||
QNetworkAccessManager* AvatarData::networkAccessManager = NULL;
|
||||
|
||||
AvatarData::AvatarData() :
|
||||
_sessionUUID(),
|
||||
_handPosition(0,0,0),
|
||||
|
@ -751,18 +749,15 @@ void AvatarData::setBillboard(const QByteArray& billboard) {
|
|||
void AvatarData::setBillboardFromURL(const QString &billboardURL) {
|
||||
_billboardURL = billboardURL;
|
||||
|
||||
if (AvatarData::networkAccessManager) {
|
||||
qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL);
|
||||
|
||||
QNetworkRequest billboardRequest;
|
||||
billboardRequest.setUrl(QUrl(billboardURL));
|
||||
|
||||
QNetworkReply* networkReply = AvatarData::networkAccessManager->get(billboardRequest);
|
||||
connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply()));
|
||||
|
||||
} else {
|
||||
qDebug() << "Billboard PNG download requested but no network access manager is available.";
|
||||
}
|
||||
|
||||
qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL);
|
||||
|
||||
QNetworkRequest billboardRequest;
|
||||
billboardRequest.setUrl(QUrl(billboardURL));
|
||||
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* networkReply = networkAccessManager.get(billboardRequest);
|
||||
connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply()));
|
||||
}
|
||||
|
||||
void AvatarData::setBillboardFromNetworkReply() {
|
||||
|
@ -839,8 +834,9 @@ void AvatarData::updateJointMappings() {
|
|||
_jointIndices.clear();
|
||||
_jointNames.clear();
|
||||
|
||||
if (networkAccessManager && _skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||
QNetworkReply* networkReply = networkAccessManager->get(QNetworkRequest(_skeletonModelURL));
|
||||
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* networkReply = networkAccessManager.get(QNetworkRequest(_skeletonModelURL));
|
||||
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,6 @@ enum KeyState {
|
|||
const glm::vec3 vec3Zero(0.0f);
|
||||
|
||||
class QDataStream;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class AttachmentData;
|
||||
class JointData;
|
||||
|
@ -269,8 +268,6 @@ public:
|
|||
QElapsedTimer& getLastUpdateTimer() { return _lastUpdateTimer; }
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
|
||||
static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; }
|
||||
|
||||
public slots:
|
||||
void sendIdentityPacket();
|
||||
|
@ -323,8 +320,6 @@ protected:
|
|||
|
||||
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _jointNames; ///< in order of depth-first traversal
|
||||
|
||||
static QNetworkAccessManager* networkAccessManager;
|
||||
|
||||
quint64 _errorLogExpiry; ///< time in future when to log an error
|
||||
|
||||
|
|
|
@ -37,19 +37,20 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
|
|||
|
||||
const QString ACCOUNTS_GROUP = "accounts";
|
||||
|
||||
JSONCallbackParameters::JSONCallbackParameters() :
|
||||
jsonCallbackReceiver(NULL),
|
||||
jsonCallbackMethod(),
|
||||
errorCallbackReceiver(NULL),
|
||||
errorCallbackMethod(),
|
||||
updateReciever(NULL),
|
||||
updateSlot()
|
||||
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
|
||||
QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
|
||||
QObject* updateReceiver, const QString& updateSlot) :
|
||||
jsonCallbackReceiver(jsonCallbackReceiver),
|
||||
jsonCallbackMethod(jsonCallbackMethod),
|
||||
errorCallbackReceiver(errorCallbackReceiver),
|
||||
errorCallbackMethod(errorCallbackMethod),
|
||||
updateReciever(updateReceiver),
|
||||
updateSlot(updateSlot)
|
||||
{
|
||||
}
|
||||
|
||||
AccountManager::AccountManager() :
|
||||
_authURL(),
|
||||
_networkAccessManager(NULL),
|
||||
_pendingCallbackMap(),
|
||||
_accountInfo()
|
||||
{
|
||||
|
@ -153,9 +154,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
|||
const JSONCallbackParameters& callbackParams,
|
||||
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
if (hasValidAccessToken()) {
|
||||
QNetworkRequest authenticatedRequest;
|
||||
|
@ -184,26 +183,29 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
|||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
networkReply = _networkAccessManager->get(authenticatedRequest);
|
||||
networkReply = networkAccessManager.get(authenticatedRequest);
|
||||
break;
|
||||
case QNetworkAccessManager::PostOperation:
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
if (dataMultiPart) {
|
||||
if (operation == QNetworkAccessManager::PostOperation) {
|
||||
networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart);
|
||||
networkReply = networkAccessManager.post(authenticatedRequest, dataMultiPart);
|
||||
} else {
|
||||
networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart);
|
||||
networkReply = networkAccessManager.put(authenticatedRequest, dataMultiPart);
|
||||
}
|
||||
dataMultiPart->setParent(networkReply);
|
||||
} else {
|
||||
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
if (operation == QNetworkAccessManager::PostOperation) {
|
||||
networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray);
|
||||
networkReply = networkAccessManager.post(authenticatedRequest, dataByteArray);
|
||||
} else {
|
||||
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray);
|
||||
networkReply = networkAccessManager.put(authenticatedRequest, dataByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case QNetworkAccessManager::DeleteOperation:
|
||||
networkReply = networkAccessManager.sendCustomRequest(authenticatedRequest, "DELETE");
|
||||
break;
|
||||
default:
|
||||
// other methods not yet handled
|
||||
|
@ -304,9 +306,7 @@ bool AccountManager::checkAndSignalForAccessToken() {
|
|||
|
||||
void AccountManager::requestAccessToken(const QString& login, const QString& password) {
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
|
@ -324,7 +324,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
|
|||
request.setUrl(grantURL);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
|
||||
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
|
||||
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
@ -376,15 +376,13 @@ void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error)
|
|||
}
|
||||
|
||||
void AccountManager::requestProfile() {
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl profileURL = _authURL;
|
||||
profileURL.setPath("/api/v1/users/profile");
|
||||
profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
|
||||
|
||||
QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL));
|
||||
QNetworkReply* profileReply = networkAccessManager.get(QNetworkRequest(profileURL));
|
||||
connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished);
|
||||
connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
|
|
@ -15,14 +15,17 @@
|
|||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
||||
class JSONCallbackParameters {
|
||||
public:
|
||||
JSONCallbackParameters();
|
||||
JSONCallbackParameters(QObject* jsonCallbackReceiver = NULL, const QString& jsonCallbackMethod = QString(),
|
||||
QObject* errorCallbackReceiver = NULL, const QString& errorCallbackMethod = QString(),
|
||||
QObject* updateReceiver = NULL, const QString& updateSlot = QString());
|
||||
|
||||
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
||||
|
||||
|
@ -58,8 +61,6 @@ public:
|
|||
|
||||
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
|
||||
|
||||
void destroy() { delete _networkAccessManager; }
|
||||
|
||||
public slots:
|
||||
void requestAccessTokenFinished();
|
||||
void requestProfileFinished();
|
||||
|
@ -93,7 +94,6 @@ private:
|
|||
QHttpMultiPart* dataMultiPart);
|
||||
|
||||
QUrl _authURL;
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||
|
||||
DataServerAccountInfo _accountInfo;
|
||||
|
|
161
libraries/networking/src/NetworkAccessManager.cpp
Normal file
161
libraries/networking/src/NetworkAccessManager.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// NetworkAccessManager.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/1/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMetaObject>
|
||||
#include <QAbstractNetworkCache>
|
||||
#include <QThread>
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
NetworkAccessManager& NetworkAccessManager::getInstance() {
|
||||
static NetworkAccessManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
NetworkAccessManager::NetworkAccessManager() {
|
||||
qRegisterMetaType<QAbstractNetworkCache*>();
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::get(const QNetworkRequest& request) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"get",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::get(request);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::head(const QNetworkRequest& request) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"head",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::head(request);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QIODevice* data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"post",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(QIODevice*, data));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::post(request, data);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, const QByteArray& data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"post",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(const QByteArray, data));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::post(request, data);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QHttpMultiPart* multiPart) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"post",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(QHttpMultiPart*, multiPart));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::post(request, multiPart);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QIODevice* data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"put",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(QIODevice*, data));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::put(request, data);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QHttpMultiPart* multiPart) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"put",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(QHttpMultiPart*, multiPart));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::put(request, multiPart);
|
||||
}
|
||||
|
||||
QNetworkReply* NetworkAccessManager::put(const QNetworkRequest & request, const QByteArray & data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"put",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(const QByteArray, data));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::put(request, data);
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply* NetworkAccessManager::sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QNetworkReply* result;
|
||||
QMetaObject::invokeMethod(this,
|
||||
"sendCustomRequest",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QNetworkReply*, result),
|
||||
Q_ARG(const QNetworkRequest, request),
|
||||
Q_ARG(const QByteArray, verb),
|
||||
Q_ARG(QIODevice*, data));
|
||||
return result;
|
||||
}
|
||||
return QNetworkAccessManager::sendCustomRequest(request, verb, data);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::setCache(QAbstractNetworkCache* cache) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this,
|
||||
"setCache",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QAbstractNetworkCache*, cache));
|
||||
}
|
||||
QNetworkAccessManager::setCache(cache);
|
||||
}
|
44
libraries/networking/src/NetworkAccessManager.h
Normal file
44
libraries/networking/src/NetworkAccessManager.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// NetworkAccessManager.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/1/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_NetworkAccessManager_h
|
||||
#define hifi_NetworkAccessManager_h
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkConfiguration>
|
||||
#include <QNetworkProxy>
|
||||
|
||||
/// Wrapper around QNetworkAccessManager wo that we only use one instance
|
||||
/// For any other method you should need, make sure to be on the right thread
|
||||
/// or if it is not but is a slot, use QMetaObject::invokeMethod()
|
||||
/// In the case what you want to call isn't a slot and you aren't on the same thread,
|
||||
/// then add then method to the method to the wrapper with the Q_INVKABLE flag
|
||||
class NetworkAccessManager : public QNetworkAccessManager {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static NetworkAccessManager& getInstance();
|
||||
|
||||
Q_INVOKABLE QNetworkReply* get(const QNetworkRequest& request);
|
||||
Q_INVOKABLE QNetworkReply* head(const QNetworkRequest& request);
|
||||
Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QIODevice* data);
|
||||
Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, const QByteArray& data);
|
||||
Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QHttpMultiPart* multiPart);
|
||||
Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QIODevice* data);
|
||||
Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QHttpMultiPart* multiPart);
|
||||
Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, const QByteArray& data);
|
||||
Q_INVOKABLE QNetworkReply* sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data = 0);
|
||||
Q_INVOKABLE void setCache(QAbstractNetworkCache* cache);
|
||||
|
||||
private:
|
||||
NetworkAccessManager();
|
||||
};
|
||||
|
||||
#endif // hifi_NetworkAccessManager_h
|
|
@ -16,6 +16,8 @@
|
|||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
#include "ResourceCache.h"
|
||||
|
||||
ResourceCache::ResourceCache(QObject* parent) :
|
||||
|
@ -103,8 +105,6 @@ void ResourceCache::requestCompleted(Resource* resource) {
|
|||
}
|
||||
}
|
||||
|
||||
QNetworkAccessManager* ResourceCache::_networkAccessManager = NULL;
|
||||
|
||||
const int DEFAULT_REQUEST_LIMIT = 10;
|
||||
int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
|
||||
|
||||
|
@ -219,7 +219,7 @@ void Resource::init() {
|
|||
if (_url.isEmpty()) {
|
||||
_startedLoading = _loaded = true;
|
||||
|
||||
} else if (!(_url.isValid() && ResourceCache::getNetworkAccessManager())) {
|
||||
} else if (!(_url.isValid())) {
|
||||
_startedLoading = _failedToLoad = true;
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ void Resource::handleReplyTimeout() {
|
|||
}
|
||||
|
||||
void Resource::makeRequest() {
|
||||
_reply = ResourceCache::getNetworkAccessManager()->get(_request);
|
||||
_reply = NetworkAccessManager::getInstance().get(_request);
|
||||
|
||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
|
@ -22,7 +22,6 @@
|
|||
#include <QUrl>
|
||||
#include <QWeakPointer>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QTimer;
|
||||
|
||||
|
@ -33,10 +32,6 @@ class ResourceCache : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; }
|
||||
static QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
|
||||
static void setRequestLimit(int limit) { _requestLimit = limit; }
|
||||
static int getRequestLimit() { return _requestLimit; }
|
||||
|
||||
|
@ -76,7 +71,6 @@ private:
|
|||
QHash<QUrl, QWeakPointer<Resource> > _resources;
|
||||
int _lastLRUKey;
|
||||
|
||||
static QNetworkAccessManager* _networkAccessManager;
|
||||
static int _requestLimit;
|
||||
static QList<QPointer<Resource> > _pendingRequests;
|
||||
static QList<Resource*> _loadingRequests;
|
|
@ -15,10 +15,12 @@
|
|||
#include <qbytearray.h>
|
||||
#include <qvector.h>
|
||||
|
||||
#include "SequenceNumberStats.h"
|
||||
|
||||
class SentPacketHistory {
|
||||
|
||||
public:
|
||||
SentPacketHistory(int size = 1000);
|
||||
SentPacketHistory(int size = MAX_REASONABLE_SEQUENCE_GAP);
|
||||
|
||||
void packetSent(uint16_t sequenceNumber, const QByteArray& packet);
|
||||
const QByteArray* getPacket(uint16_t sequenceNumber) const;
|
||||
|
|
|
@ -39,7 +39,6 @@ void SequenceNumberStats::reset() {
|
|||
}
|
||||
|
||||
static const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||
static const int MAX_REASONABLE_SEQUENCE_GAP = 1000; // this must be less than UINT16_RANGE / 2 for rollover handling to work
|
||||
|
||||
void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderUUID, const bool wantExtraDebugging) {
|
||||
|
||||
|
@ -95,6 +94,7 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
|
||||
_numEarly++;
|
||||
_numLost += (incomingInt - expectedInt);
|
||||
_lastReceived = incoming;
|
||||
|
||||
// add all sequence numbers that were skipped to the missing sequence numbers list
|
||||
for (int missingInt = expectedInt; missingInt < incomingInt; missingInt++) {
|
||||
|
@ -106,14 +106,14 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
if (_missingSet.size() > MAX_REASONABLE_SEQUENCE_GAP) {
|
||||
pruneMissingSet(wantExtraDebugging);
|
||||
}
|
||||
|
||||
_lastReceived = incoming;
|
||||
} else { // late
|
||||
if (wantExtraDebugging) {
|
||||
qDebug() << "this packet is later than expected...";
|
||||
}
|
||||
_numLate++;
|
||||
|
||||
// do not update _lastReceived; it shouldn't become smaller
|
||||
|
||||
// remove this from missing sequence number if it's in there
|
||||
if (_missingSet.remove(incoming)) {
|
||||
if (wantExtraDebugging) {
|
||||
|
@ -127,8 +127,6 @@ void SequenceNumberStats::sequenceNumberReceived(quint16 incoming, QUuid senderU
|
|||
}
|
||||
_numDuplicate++;
|
||||
}
|
||||
|
||||
// do not update _incomingLastSequence; it shouldn't become smaller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
#include "SharedUtil.h"
|
||||
#include <quuid.h>
|
||||
|
||||
const int MAX_REASONABLE_SEQUENCE_GAP = 1000;
|
||||
|
||||
class SequenceNumberStats {
|
||||
public:
|
||||
SequenceNumberStats();
|
||||
|
||||
void reset();
|
||||
|
||||
void sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false);
|
||||
void pruneMissingSet(const bool wantExtraDebugging = false);
|
||||
|
||||
quint32 getNumReceived() const { return _numReceived; }
|
||||
quint32 getNumUnreasonable() const { return _numUnreasonable; }
|
||||
|
@ -34,8 +36,6 @@ public:
|
|||
const QSet<quint16>& getMissingSet() const { return _missingSet; }
|
||||
|
||||
private:
|
||||
void pruneMissingSet(const bool wantExtraDebugging);
|
||||
|
||||
quint16 _lastReceived;
|
||||
QSet<quint16> _missingSet;
|
||||
|
||||
|
|
|
@ -83,17 +83,10 @@ void UserActivityLogger::close(int delayTime) {
|
|||
|
||||
// In order to get the end of the session, we need to give the account manager enough time to send the packet.
|
||||
QEventLoop loop;
|
||||
// Here we connect the callbacks to stop the event loop
|
||||
JSONCallbackParameters params;
|
||||
params.jsonCallbackReceiver = &loop;
|
||||
params.errorCallbackReceiver = &loop;
|
||||
params.jsonCallbackMethod = "quit";
|
||||
params.errorCallbackMethod = "quit";
|
||||
// In case something goes wrong, we also setup a timer so that the delai is not greater than delayTime
|
||||
QTimer timer;
|
||||
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
// Now we can log it
|
||||
logAction(ACTION_NAME, QJsonObject(), params);
|
||||
logAction(ACTION_NAME, QJsonObject());
|
||||
timer.start(delayTime);
|
||||
loop.exec();
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
float getIncomingFlightTimeAverage() { return _incomingFlightTimeAverage.getAverage(); }
|
||||
|
||||
const SequenceNumberStats& getIncomingOctreeSequenceNumberStats() const { return _incomingOctreeSequenceNumberStats; }
|
||||
SequenceNumberStats& getIncomingOctreeSequenceNumberStats() { return _incomingOctreeSequenceNumberStats; }
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -183,7 +183,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
|
||||
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame
|
||||
const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second
|
||||
const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
||||
// particles that are in hand, don't collide with avatars
|
||||
|
|
37
libraries/script-engine/src/MIDIEvent.cpp
Normal file
37
libraries/script-engine/src/MIDIEvent.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// MIDIEvent.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MIDIEvent.h"
|
||||
|
||||
void registerMIDIMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, midiEventToScriptValue, midiEventFromScriptValue);
|
||||
}
|
||||
|
||||
const QString MIDI_DELTA_TIME_PROP_NAME = "deltaTime";
|
||||
const QString MIDI_EVENT_TYPE_PROP_NAME = "type";
|
||||
const QString MIDI_DATA_1_PROP_NAME = "data1";
|
||||
const QString MIDI_DATA_2_PROP_NAME = "data2";
|
||||
|
||||
QScriptValue midiEventToScriptValue(QScriptEngine* engine, const MIDIEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty(MIDI_DELTA_TIME_PROP_NAME, event.deltaTime);
|
||||
obj.setProperty(MIDI_EVENT_TYPE_PROP_NAME, event.type);
|
||||
obj.setProperty(MIDI_DATA_1_PROP_NAME, event.data1);
|
||||
obj.setProperty(MIDI_DATA_2_PROP_NAME, event.data2);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void midiEventFromScriptValue(const QScriptValue &object, MIDIEvent& event) {
|
||||
event.deltaTime = object.property(MIDI_DELTA_TIME_PROP_NAME).toVariant().toDouble();
|
||||
event.type = object.property(MIDI_EVENT_TYPE_PROP_NAME).toVariant().toUInt();
|
||||
event.data1 = object.property(MIDI_DATA_1_PROP_NAME).toVariant().toUInt();
|
||||
event.data2 = object.property(MIDI_DATA_2_PROP_NAME).toVariant().toUInt();
|
||||
}
|
32
libraries/script-engine/src/MIDIEvent.h
Normal file
32
libraries/script-engine/src/MIDIEvent.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// MIDIEvent.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#ifndef hifi_MIDIEvent_h
|
||||
#define hifi_MIDIEvent_h
|
||||
|
||||
class MIDIEvent {
|
||||
public:
|
||||
double deltaTime;
|
||||
unsigned int type;
|
||||
unsigned int data1;
|
||||
unsigned int data2;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MIDIEvent)
|
||||
|
||||
void registerMIDIMetaTypes(QScriptEngine* engine);
|
||||
|
||||
QScriptValue midiEventToScriptValue(QScriptEngine* engine, const MIDIEvent& event);
|
||||
void midiEventFromScriptValue(const QScriptValue &object, MIDIEvent& event);
|
||||
|
||||
#endif // hifi_MIDIEvent_h
|
|
@ -13,7 +13,6 @@
|
|||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QScriptEngine>
|
||||
|
@ -23,6 +22,7 @@
|
|||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <ModelsScriptingInterface.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "AnimationObject.h"
|
||||
#include "MenuItemProperties.h"
|
||||
#include "MIDIEvent.h"
|
||||
#include "LocalVoxels.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
|
@ -149,8 +150,8 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
|
|||
emit errorMessage("ERROR Loading file:" + fileName);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << url;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
@ -225,6 +226,7 @@ void ScriptEngine::init() {
|
|||
|
||||
// register various meta-types
|
||||
registerMetaTypes(&_engine);
|
||||
registerMIDIMetaTypes(&_engine);
|
||||
registerVoxelMetaTypes(&_engine);
|
||||
registerEventTypes(&_engine);
|
||||
registerMenuItemProperties(&_engine);
|
||||
|
@ -263,6 +265,8 @@ void ScriptEngine::init() {
|
|||
qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue);
|
||||
qScriptRegisterMetaType( &_engine, injectorToScriptValue_InputController, injectorFromScriptValue_InputController);
|
||||
|
||||
qScriptRegisterMetaType(&_engine, animationDetailsToScriptValue, animationDetailsFromScriptValue);
|
||||
|
||||
registerGlobalObject("Script", this);
|
||||
registerGlobalObject("Audio", &_audioScriptingInterface);
|
||||
registerGlobalObject("Controller", _controllerScriptingInterface);
|
||||
|
@ -478,7 +482,10 @@ void ScriptEngine::run() {
|
|||
|
||||
// pack a placeholder value for sequence number for now, will be packed when destination node is known
|
||||
int numPreSequenceNumberBytes = audioPacket.size();
|
||||
packetStream << (quint16)0;
|
||||
packetStream << (quint16) 0;
|
||||
|
||||
// assume scripted avatar audio is mono and set channel flag to zero
|
||||
packetStream << (quint8) 0;
|
||||
|
||||
// use the orientation and position of this avatar for the source of this audio
|
||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3));
|
||||
|
@ -655,8 +662,8 @@ void ScriptEngine::include(const QString& includeFile) {
|
|||
QString includeContents;
|
||||
|
||||
if (url.scheme() == "http" || url.scheme() == "ftp") {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << includeFile;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <QEventLoop>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "XMLHttpRequestClass.h"
|
||||
|
||||
XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
|
||||
|
@ -22,7 +24,6 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
|
|||
_url(),
|
||||
_method(""),
|
||||
_responseType(""),
|
||||
_manager(this),
|
||||
_request(),
|
||||
_reply(NULL),
|
||||
_sendData(NULL),
|
||||
|
@ -161,7 +162,7 @@ void XMLHttpRequestClass::send(const QString& data) {
|
|||
}
|
||||
|
||||
void XMLHttpRequestClass::doSend() {
|
||||
_reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData);
|
||||
_reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData);
|
||||
|
||||
connectToReply(_reply);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_XMLHttpRequestClass_h
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QObject>
|
||||
|
@ -104,7 +103,6 @@ private:
|
|||
QUrl _url;
|
||||
QString _method;
|
||||
QString _responseType;
|
||||
QNetworkAccessManager _manager;
|
||||
QNetworkRequest _request;
|
||||
QNetworkReply* _reply;
|
||||
QBuffer* _sendData;
|
||||
|
|
|
@ -10,7 +10,7 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
|||
set(TARGET_NAME shared)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
find_package(Qt5 COMPONENTS Network Widgets Xml)
|
||||
find_package(Qt5 COMPONENTS Network Widgets Xml Script)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
@ -32,4 +32,8 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
||||
# There is something special (bug) about Qt5Scripts, that we have to explicitly add its include
|
||||
# directory when Qt5 (5.2.1) is compiled from source and is not in a standard place.
|
||||
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
||||
|
|
201
libraries/shared/src/AngularConstraint.cpp
Normal file
201
libraries/shared/src/AngularConstraint.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// AngularConstraint.cpp
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "AngularConstraint.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
// helper function
|
||||
/// \param angle radian angle to be clamped within angleMin and angleMax
|
||||
/// \param angleMin minimum value
|
||||
/// \param angleMax maximum value
|
||||
/// \return value between minAngle and maxAngle closest to angle
|
||||
float clampAngle(float angle, float angleMin, float angleMax) {
|
||||
float minDistance = angle - angleMin;
|
||||
float maxDistance = angle - angleMax;
|
||||
if (maxDistance > 0.0f) {
|
||||
minDistance = glm::min(minDistance, angleMin + TWO_PI - angle);
|
||||
angle = (minDistance < maxDistance) ? angleMin : angleMax;
|
||||
} else if (minDistance < 0.0f) {
|
||||
maxDistance = glm::max(maxDistance, angleMax - TWO_PI - angle);
|
||||
angle = (minDistance > maxDistance) ? angleMin : angleMax;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
// static
|
||||
AngularConstraint* AngularConstraint::newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles) {
|
||||
float minDistance2 = glm::distance2(minAngles, glm::vec3(-PI, -PI, -PI));
|
||||
float maxDistance2 = glm::distance2(maxAngles, glm::vec3(PI, PI, PI));
|
||||
if (minDistance2 < EPSILON && maxDistance2 < EPSILON) {
|
||||
// no constraint
|
||||
return NULL;
|
||||
}
|
||||
// count the zero length elements
|
||||
glm::vec3 rangeAngles = maxAngles - minAngles;
|
||||
int pivotIndex = -1;
|
||||
int numZeroes = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (rangeAngles[i] < EPSILON) {
|
||||
++numZeroes;
|
||||
} else {
|
||||
pivotIndex = i;
|
||||
}
|
||||
}
|
||||
if (numZeroes == 2) {
|
||||
// this is a hinge
|
||||
int forwardIndex = (pivotIndex + 1) % 3;
|
||||
glm::vec3 forwardAxis(0.0f);
|
||||
forwardAxis[forwardIndex] = 1.0f;
|
||||
glm::vec3 rotationAxis(0.0f);
|
||||
rotationAxis[pivotIndex] = 1.0f;
|
||||
return new HingeConstraint(forwardAxis, rotationAxis, minAngles[pivotIndex], maxAngles[pivotIndex]);
|
||||
} else if (numZeroes == 0) {
|
||||
// approximate the angular limits with a cone roller
|
||||
// we assume the roll is about z
|
||||
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
|
||||
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::vec3 coneAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
// the coneAngle is half the average range of the two non-roll rotations
|
||||
glm::vec3 range = maxAngles - minAngles;
|
||||
float coneAngle = 0.25f * (range[0] + range[1]);
|
||||
return new ConeRollerConstraint(coneAngle, coneAxis, minAngles.z, maxAngles.z);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AngularConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
|
||||
glm::quat clampedTarget = targetRotation;
|
||||
bool clamped = clamp(clampedTarget);
|
||||
if (clamped) {
|
||||
// check if oldRotation is also clamped
|
||||
glm::quat clampedOld = oldRotation;
|
||||
bool clamped2 = clamp(clampedOld);
|
||||
if (clamped2) {
|
||||
// oldRotation is already beyond the constraint
|
||||
// we clamp again midway between targetRotation and clamped oldPosition
|
||||
clampedTarget = glm::shortMix(clampedOld, targetRotation, mixFraction);
|
||||
// and then clamp that
|
||||
clamp(clampedTarget);
|
||||
}
|
||||
// finally we mix targetRotation with the clampedTarget
|
||||
targetRotation = glm::shortMix(clampedTarget, targetRotation, mixFraction);
|
||||
}
|
||||
return clamped;
|
||||
}
|
||||
|
||||
HingeConstraint::HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle)
|
||||
: _minAngle(minAngle), _maxAngle(maxAngle) {
|
||||
assert(_minAngle < _maxAngle);
|
||||
// we accept the rotationAxis direction
|
||||
assert(glm::length(rotationAxis) > EPSILON);
|
||||
_rotationAxis = glm::normalize(rotationAxis);
|
||||
// but we compute the final _forwardAxis
|
||||
glm::vec3 otherAxis = glm::cross(_rotationAxis, forwardAxis);
|
||||
assert(glm::length(otherAxis) > EPSILON);
|
||||
_forwardAxis = glm::normalize(glm::cross(otherAxis, _rotationAxis));
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool HingeConstraint::clamp(glm::quat& rotation) const {
|
||||
glm::vec3 forward = rotation * _forwardAxis;
|
||||
forward -= glm::dot(forward, _rotationAxis) * _rotationAxis;
|
||||
float length = glm::length(forward);
|
||||
if (length < EPSILON) {
|
||||
// infinite number of solutions ==> choose the middle of the contrained range
|
||||
rotation = glm::angleAxis(0.5f * (_minAngle + _maxAngle), _rotationAxis);
|
||||
return true;
|
||||
}
|
||||
forward /= length;
|
||||
float sign = (glm::dot(glm::cross(_forwardAxis, forward), _rotationAxis) > 0.0f ? 1.0f : -1.0f);
|
||||
//float angle = sign * acos(glm::dot(forward, _forwardAxis) / length);
|
||||
float angle = sign * acos(glm::dot(forward, _forwardAxis));
|
||||
glm::quat newRotation = glm::angleAxis(clampAngle(angle, _minAngle, _maxAngle), _rotationAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, rotation)) > EPSILON * EPSILON) {
|
||||
rotation = newRotation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HingeConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
|
||||
// the hinge works best without a soft clamp
|
||||
return clamp(targetRotation);
|
||||
}
|
||||
|
||||
ConeRollerConstraint::ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll)
|
||||
: _coneAngle(coneAngle), _minRoll(minRoll), _maxRoll(maxRoll) {
|
||||
assert(_maxRoll >= _minRoll);
|
||||
float axisLength = glm::length(coneAxis);
|
||||
assert(axisLength > EPSILON);
|
||||
_coneAxis = coneAxis / axisLength;
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool ConeRollerConstraint::clamp(glm::quat& rotation) const {
|
||||
bool applied = false;
|
||||
glm::vec3 rotatedAxis = rotation * _coneAxis;
|
||||
glm::vec3 perpAxis = glm::cross(rotatedAxis, _coneAxis);
|
||||
float perpAxisLength = glm::length(perpAxis);
|
||||
if (perpAxisLength > EPSILON) {
|
||||
perpAxis /= perpAxisLength;
|
||||
// enforce the cone
|
||||
float angle = acosf(glm::dot(rotatedAxis, _coneAxis));
|
||||
if (angle > _coneAngle) {
|
||||
rotation = glm::angleAxis(angle - _coneAngle, perpAxis) * rotation;
|
||||
rotatedAxis = rotation * _coneAxis;
|
||||
applied = true;
|
||||
}
|
||||
} else {
|
||||
// the rotation is 100% roll
|
||||
// there is no obvious perp axis so we must pick one
|
||||
perpAxis = rotatedAxis;
|
||||
// find the first non-zero element:
|
||||
float iValue = 0.0f;
|
||||
int i = 0;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (fabsf(perpAxis[i]) > EPSILON) {
|
||||
iValue = perpAxis[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(i != 3);
|
||||
// swap or negate the next element
|
||||
int j = (i + 1) % 3;
|
||||
float jValue = perpAxis[j];
|
||||
if (fabsf(jValue - iValue) > EPSILON) {
|
||||
perpAxis[i] = jValue;
|
||||
perpAxis[j] = iValue;
|
||||
} else {
|
||||
perpAxis[i] = -iValue;
|
||||
}
|
||||
perpAxis = glm::cross(perpAxis, rotatedAxis);
|
||||
perpAxisLength = glm::length(perpAxis);
|
||||
assert(perpAxisLength > EPSILON);
|
||||
perpAxis /= perpAxisLength;
|
||||
}
|
||||
// measure the roll
|
||||
// NOTE: perpAxis is perpendicular to both _coneAxis and rotatedConeAxis, so we can
|
||||
// rotate it again and we'll end up with an something that has only been rolled.
|
||||
glm::vec3 rolledPerpAxis = rotation * perpAxis;
|
||||
float sign = glm::dot(rotatedAxis, glm::cross(perpAxis, rolledPerpAxis)) > 0.0f ? 1.0f : -1.0f;
|
||||
float roll = sign * angleBetween(rolledPerpAxis, perpAxis);
|
||||
if (roll < _minRoll || roll > _maxRoll) {
|
||||
float clampedRoll = clampAngle(roll, _minRoll, _maxRoll);
|
||||
rotation = glm::normalize(glm::angleAxis(clampedRoll - roll, rotatedAxis) * rotation);
|
||||
applied = true;
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
|
55
libraries/shared/src/AngularConstraint.h
Normal file
55
libraries/shared/src/AngularConstraint.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// AngularConstraint.h
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2013 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_AngularConstraint_h
|
||||
#define hifi_AngularConstraint_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
class AngularConstraint {
|
||||
public:
|
||||
/// \param minAngles minumum euler angles for the constraint
|
||||
/// \param maxAngles minumum euler angles for the constraint
|
||||
/// \return pointer to new AngularConstraint of the right type or NULL if none could be made
|
||||
static AngularConstraint* newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles);
|
||||
|
||||
AngularConstraint() {}
|
||||
virtual ~AngularConstraint() {}
|
||||
virtual bool clamp(glm::quat& rotation) const = 0;
|
||||
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
|
||||
protected:
|
||||
};
|
||||
|
||||
class HingeConstraint : public AngularConstraint {
|
||||
public:
|
||||
HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle);
|
||||
virtual bool clamp(glm::quat& rotation) const;
|
||||
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
|
||||
protected:
|
||||
glm::vec3 _forwardAxis;
|
||||
glm::vec3 _rotationAxis;
|
||||
float _minAngle;
|
||||
float _maxAngle;
|
||||
};
|
||||
|
||||
class ConeRollerConstraint : public AngularConstraint {
|
||||
public:
|
||||
ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll);
|
||||
virtual bool clamp(glm::quat& rotation) const;
|
||||
private:
|
||||
float _coneAngle;
|
||||
glm::vec3 _coneAxis;
|
||||
float _minRoll;
|
||||
float _maxRoll;
|
||||
};
|
||||
|
||||
#endif // hifi_AngularConstraint_h
|
|
@ -24,9 +24,6 @@ int MAX_ENTITIES_PER_SIMULATION = 64;
|
|||
int MAX_COLLISIONS_PER_SIMULATION = 256;
|
||||
|
||||
|
||||
const int NUM_SHAPE_BITS = 6;
|
||||
const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1;
|
||||
|
||||
PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION),
|
||||
_numIterations(0), _numCollisions(0), _constraintError(0.0f), _stepTime(0) {
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ void AudioRingBufferTests::runAllTests() {
|
|||
int readIndexAt;
|
||||
|
||||
|
||||
AudioRingBuffer ringBuffer(10); // makes buffer of 100 int16_t samples
|
||||
AudioRingBuffer ringBuffer(10, false, 10); // makes buffer of 100 int16_t samples
|
||||
for (int T = 0; T < 300; T++) {
|
||||
|
||||
writeIndexAt = 0;
|
||||
|
|
|
@ -23,7 +23,7 @@ void SequenceNumberStatsTests::runAllTests() {
|
|||
pruneTest();
|
||||
}
|
||||
|
||||
const int UINT16_RANGE = std::numeric_limits<quint16>::max() + 1;
|
||||
const quint32 UINT16_RANGE = std::numeric_limits<quint16>::max() + 1;
|
||||
|
||||
|
||||
void SequenceNumberStatsTests::rolloverTest() {
|
||||
|
@ -34,7 +34,7 @@ void SequenceNumberStatsTests::rolloverTest() {
|
|||
quint16 seq = 79; // start on some random number
|
||||
|
||||
for (int R = 0; R < 2; R++) {
|
||||
for (int i = 0; i < 3 * UINT16_RANGE; i++) {
|
||||
for (quint32 i = 0; i < 3 * UINT16_RANGE; i++) {
|
||||
stats.sequenceNumberReceived(seq);
|
||||
seq = seq + (quint16)1;
|
||||
|
||||
|
@ -53,12 +53,12 @@ void SequenceNumberStatsTests::earlyLateTest() {
|
|||
|
||||
SequenceNumberStats stats;
|
||||
quint16 seq = 65530;
|
||||
int numSent = 0;
|
||||
quint32 numSent = 0;
|
||||
|
||||
int numEarly = 0;
|
||||
int numLate = 0;
|
||||
int numLost = 0;
|
||||
int numRecovered = 0;
|
||||
quint32 numEarly = 0;
|
||||
quint32 numLate = 0;
|
||||
quint32 numLost = 0;
|
||||
quint32 numRecovered = 0;
|
||||
|
||||
for (int R = 0; R < 2; R++) {
|
||||
for (int T = 0; T < 10000; T++) {
|
||||
|
@ -115,6 +115,11 @@ void SequenceNumberStatsTests::earlyLateTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numEarly = 0;
|
||||
numLate = 0;
|
||||
numLost = 0;
|
||||
numRecovered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,12 +127,12 @@ void SequenceNumberStatsTests::duplicateTest() {
|
|||
|
||||
SequenceNumberStats stats;
|
||||
quint16 seq = 12345;
|
||||
int numSent = 0;
|
||||
quint32 numSent = 0;
|
||||
|
||||
int numDuplicate = 0;
|
||||
int numEarly = 0;
|
||||
int numLate = 0;
|
||||
int numLost = 0;
|
||||
quint32 numDuplicate = 0;
|
||||
quint32 numEarly = 0;
|
||||
quint32 numLate = 0;
|
||||
quint32 numLost = 0;
|
||||
|
||||
for (int R = 0; R < 2; R++) {
|
||||
for (int T = 0; T < 10000; T++) {
|
||||
|
@ -203,6 +208,11 @@ void SequenceNumberStatsTests::duplicateTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numDuplicate = 0;
|
||||
numEarly = 0;
|
||||
numLate = 0;
|
||||
numLost = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,10 +220,10 @@ void SequenceNumberStatsTests::pruneTest() {
|
|||
|
||||
SequenceNumberStats stats;
|
||||
quint16 seq = 54321;
|
||||
int numSent = 0;
|
||||
quint32 numSent = 0;
|
||||
|
||||
int numEarly = 0;
|
||||
int numLost = 0;
|
||||
quint32 numEarly = 0;
|
||||
quint32 numLost = 0;
|
||||
|
||||
for (int R = 0; R < 2; R++) {
|
||||
for (int T = 0; T < 1000; T++) {
|
||||
|
@ -263,5 +273,8 @@ void SequenceNumberStatsTests::pruneTest() {
|
|||
}
|
||||
}
|
||||
stats.reset();
|
||||
numSent = 0;
|
||||
numEarly = 0;
|
||||
numLost = 0;
|
||||
}
|
||||
}
|
||||
|
|
476
tests/shared/src/AngularConstraintTests.cpp
Normal file
476
tests/shared/src/AngularConstraintTests.cpp
Normal file
|
@ -0,0 +1,476 @@
|
|||
//
|
||||
// AngularConstraintTests.cpp
|
||||
// tests/physics/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <AngularConstraint.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "AngularConstraintTests.h"
|
||||
|
||||
|
||||
void AngularConstraintTests::testHingeConstraint() {
|
||||
float minAngle = -PI;
|
||||
float maxAngle = 0.0f;
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 minAngles(0.0f, -PI, 0.0f);
|
||||
glm::vec3 maxAngles(0.0f, 0.0f, 0.0f);
|
||||
|
||||
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
|
||||
if (!c) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
|
||||
}
|
||||
|
||||
{ // test in middle of constraint
|
||||
float angle = 0.5f * (minAngle + maxAngle);
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside min edge of constraint
|
||||
float angle = minAngle + 10.f * EPSILON;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside max edge of constraint
|
||||
float angle = maxAngle - 10.f * EPSILON;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside min edge of constraint
|
||||
float angle = minAngle - 0.001f;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside max edge of constraint
|
||||
float angle = maxAngle + 0.001f;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside min edge of constraint (wraps around to max)
|
||||
float angle = minAngle - 0.75f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside max edge of constraint (wraps around to min)
|
||||
float angle = maxAngle + 0.75f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
float ACCEPTABLE_ERROR = 1.0e-4f;
|
||||
{ // test nearby but off-axis rotation
|
||||
float offAngle = 0.1f;
|
||||
glm::quat offRotation(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = 0.5f * (maxAngle + minAngle);
|
||||
glm::quat rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(angle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation > maxAngle
|
||||
float offAngle = 0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = maxAngle + 0.2f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation < minAngle
|
||||
float offAngle = 0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = minAngle - 0.2f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation > maxAngle with wrap over to minAngle
|
||||
float offAngle = -0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = maxAngle + 0.6f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation < minAngle with wrap over to maxAngle
|
||||
float offAngle = -0.6f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = minAngle - 0.7f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
delete c;
|
||||
}
|
||||
|
||||
void AngularConstraintTests::testConeRollerConstraint() {
|
||||
float minAngleX = -PI / 5.0f;
|
||||
float minAngleY = -PI / 5.0f;
|
||||
float minAngleZ = -PI / 8.0f;
|
||||
|
||||
float maxAngleX = PI / 4.0f;
|
||||
float maxAngleY = PI / 3.0f;
|
||||
float maxAngleZ = PI / 4.0f;
|
||||
|
||||
glm::vec3 minAngles(minAngleX, minAngleY, minAngleZ);
|
||||
glm::vec3 maxAngles(maxAngleX, maxAngleY, maxAngleZ);
|
||||
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
|
||||
|
||||
float expectedConeAngle = 0.25 * (maxAngleX - minAngleX + maxAngleY - minAngleY);
|
||||
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
|
||||
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::vec3 expectedConeAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 perpAxis = glm::normalize(xAxis - glm::dot(xAxis, expectedConeAxis) * expectedConeAxis);
|
||||
|
||||
if (!c) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
|
||||
}
|
||||
{ // test in middle of constraint
|
||||
glm::vec3 angles(PI/20.0f, 0.0f, PI/10.0f);
|
||||
glm::quat rotation(angles);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
float deltaAngle = 0.001f;
|
||||
{ // test just inside edge of cone
|
||||
glm::quat rotation = glm::angleAxis(expectedConeAngle - deltaAngle, perpAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside edge of cone
|
||||
glm::quat rotation = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside min edge of roll
|
||||
glm::quat rotation = glm::angleAxis(minAngleZ + deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside max edge of roll
|
||||
glm::quat rotation = glm::angleAxis(maxAngleZ - deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside min edge of roll
|
||||
glm::quat rotation = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngleZ, expectedConeAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside max edge of roll
|
||||
glm::quat rotation = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngleZ, expectedConeAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
deltaAngle = 0.25f * expectedConeAngle;
|
||||
{ // test far outside cone and min roll
|
||||
glm::quat roll = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
|
||||
glm::quat pitchYaw = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
|
||||
glm::quat rotation = pitchYaw * roll;
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRoll = glm::angleAxis(minAngleZ, expectedConeAxis);
|
||||
glm::quat expectedPitchYaw = glm::angleAxis(expectedConeAngle, perpAxis);
|
||||
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside cone and max roll
|
||||
glm::quat roll = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
|
||||
glm::quat pitchYaw = glm::angleAxis(- expectedConeAngle - deltaAngle, perpAxis);
|
||||
glm::quat rotation = pitchYaw * roll;
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRoll = glm::angleAxis(maxAngleZ, expectedConeAxis);
|
||||
glm::quat expectedPitchYaw = glm::angleAxis(- expectedConeAngle, perpAxis);
|
||||
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
delete c;
|
||||
}
|
||||
|
||||
void AngularConstraintTests::runAllTests() {
|
||||
testHingeConstraint();
|
||||
testConeRollerConstraint();
|
||||
}
|
21
tests/shared/src/AngularConstraintTests.h
Normal file
21
tests/shared/src/AngularConstraintTests.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// AngularConstraintTests.h
|
||||
// tests/physics/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AngularConstraintTests_h
|
||||
#define hifi_AngularConstraintTests_h
|
||||
|
||||
namespace AngularConstraintTests {
|
||||
void testHingeConstraint();
|
||||
void testConeRollerConstraint();
|
||||
void runAllTests();
|
||||
}
|
||||
|
||||
#endif // hifi_AngularConstraintTests_h
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue