mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 18:23:22 +02:00
Merge branch 'master' of github.com:highfidelity/hifi
This commit is contained in:
commit
87e5133a06
251 changed files with 6741 additions and 5663 deletions
|
@ -133,6 +133,9 @@ if (APPLE)
|
|||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX10.9.sdk)
|
||||
endif ()
|
||||
|
||||
# Hide automoc folders (for IDEs)
|
||||
set(AUTOGEN_TARGETS_FOLDER "hidden/generated")
|
||||
|
||||
# Find includes in corresponding build directories
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
|
|
83
assignment-client/src/AssignmentAction.cpp
Normal file
83
assignment-client/src/AssignmentAction.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// AssignmentAction.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
AssignmentAction::AssignmentAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
|
||||
_id(id),
|
||||
_type(type),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentAction::~AssignmentAction() {
|
||||
}
|
||||
|
||||
void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const {
|
||||
simulation->removeAction(_id);
|
||||
}
|
||||
|
||||
QByteArray AssignmentAction::serialize() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
void AssignmentAction::deserialize(QByteArray serializedArguments) {
|
||||
_data = serializedArguments;
|
||||
}
|
||||
|
||||
bool AssignmentAction::updateArguments(QVariantMap arguments) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap AssignmentAction::getArguments() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client.";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getPosition() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setPosition(glm::vec3 position) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::quat AssignmentAction::getRotation() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client.";
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
void AssignmentAction::setRotation(glm::quat rotation) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getLinearVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getAngularVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client.";
|
||||
}
|
57
assignment-client/src/AssignmentAction.h
Normal file
57
assignment-client/src/AssignmentAction.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// AssignmentAction.h
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
|
||||
|
||||
#ifndef hifi_AssignmentAction_h
|
||||
#define hifi_AssignmentAction_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <EntityItem.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
class AssignmentAction : public EntityActionInterface {
|
||||
public:
|
||||
AssignmentAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
|
||||
const QUuid& getID() const { return _id; }
|
||||
virtual EntityActionType getType() { return _type; }
|
||||
virtual void removeFromSimulation(EntitySimulation* simulation) const;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual QVariantMap getArguments();
|
||||
|
||||
virtual QByteArray serialize();
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
|
||||
private:
|
||||
QUuid _id;
|
||||
EntityActionType _type;
|
||||
QByteArray _data;
|
||||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition();
|
||||
virtual void setPosition(glm::vec3 position);
|
||||
virtual glm::quat getRotation();
|
||||
virtual void setRotation(glm::quat rotation);
|
||||
virtual glm::vec3 getLinearVelocity();
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity);
|
||||
virtual glm::vec3 getAngularVelocity();
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity);
|
||||
|
||||
bool _active;
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentAction_h
|
52
assignment-client/src/AssignmentActionFactory.cpp
Normal file
52
assignment-client/src/AssignmentActionFactory.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer assignmentActionFactory(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) {
|
||||
return (EntityActionPointer) new AssignmentAction(type, id, ownerEntity);
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factoryBA(EntitySimulation* simulation,
|
||||
EntityItemPointer ownerEntity,
|
||||
QByteArray data) {
|
||||
QDataStream serializedActionDataStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedActionDataStream >> type;
|
||||
serializedActionDataStream >> id;
|
||||
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
}
|
||||
return action;
|
||||
}
|
32
assignment-client/src/AssignmentActionFactory.h
Normal file
32
assignment-client/src/AssignmentActionFactory.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentActionFactory_h
|
||||
#define hifi_AssignmentActionFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
class AssignmentActionFactory : public EntityActionFactoryInterface {
|
||||
public:
|
||||
AssignmentActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~AssignmentActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
||||
EntityItemPointer ownerEntity,
|
||||
QByteArray data);
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentActionFactory_h
|
|
@ -32,6 +32,7 @@
|
|||
#include <SoundCache.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
|
||||
|
@ -58,6 +59,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
|
||||
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
|
||||
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
|
||||
|
||||
// make up a uuid for this child so the parent can tell us apart. This id will be changed
|
||||
// when the domain server hands over an assignment.
|
||||
QUuid nodeUUID = QUuid::createUuid();
|
||||
|
|
|
@ -327,6 +327,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
showStats = true;
|
||||
} else if (url.path() == "/resetStats") {
|
||||
_octreeInboundPacketProcessor->resetStats();
|
||||
_tree->resetEditStats();
|
||||
resetSendingStats();
|
||||
showStats = true;
|
||||
}
|
||||
|
@ -627,6 +628,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
// display inbound packet stats
|
||||
statsString += QString().sprintf("<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
|
||||
getMyServerName());
|
||||
quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount();
|
||||
quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
|
||||
quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
|
||||
quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
|
||||
|
@ -635,8 +637,18 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
quint64 totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed();
|
||||
quint64 totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||
|
||||
quint64 averageDecodeTime = _tree->getAverageDecodeTime();
|
||||
quint64 averageLookupTime = _tree->getAverageLookupTime();
|
||||
quint64 averageUpdateTime = _tree->getAverageUpdateTime();
|
||||
quint64 averageCreateTime = _tree->getAverageCreateTime();
|
||||
quint64 averageLoggingTime = _tree->getAverageLoggingTime();
|
||||
|
||||
|
||||
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
|
@ -654,6 +666,17 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString(" Average Decode Time: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageDecodeTime).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Lookup Time: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLookupTime).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Update Time: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageUpdateTime).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Create Time: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageCreateTime).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Logging Time: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLoggingTime).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
int senderNumber = 0;
|
||||
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||
|
@ -737,47 +760,6 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString(" Total: %1 nodes\r\n")
|
||||
.arg(locale.toString((uint)checkSum).rightJustified(16, ' '));
|
||||
|
||||
#ifdef BLENDED_UNION_CHILDREN
|
||||
statsString += "\r\n";
|
||||
statsString += "OctreeElement Children Encoding Statistics...\r\n";
|
||||
|
||||
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getSingleChildrenCount(),
|
||||
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
|
||||
statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getTwoChildrenOffsetCount(),
|
||||
((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT));
|
||||
statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getTwoChildrenExternalCount(),
|
||||
((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||
statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getThreeChildrenOffsetCount(),
|
||||
((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
|
||||
statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getThreeChildrenExternalCount(),
|
||||
((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getExternalChildrenCount(),
|
||||
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||
|
||||
checkSum = OctreeElement::getSingleChildrenCount() +
|
||||
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
|
||||
OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() +
|
||||
OctreeElement::getExternalChildrenCount();
|
||||
|
||||
statsString += " ----------------\r\n";
|
||||
statsString += QString().sprintf(" Total: %10.llu nodes\r\n", checkSum);
|
||||
statsString += QString().sprintf(" Expected: %10.lu nodes\r\n", nodeCount);
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "In other news....\r\n";
|
||||
|
||||
statsString += QString().sprintf("could store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldStoreFourChildrenInternally());
|
||||
statsString += QString().sprintf("could NOT store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldNotStoreFourChildrenInternally());
|
||||
#endif
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
statsString += "</pre>\r\n";
|
||||
statsString += "</doc></html>";
|
||||
|
@ -1411,6 +1393,8 @@ void OctreeServer::sendStatsPacket() {
|
|||
|
||||
static QJsonObject statsObject3;
|
||||
|
||||
statsObject3[baseName + QString(".3.inbound.data.1.packetQueue")] =
|
||||
(double)_octreeInboundPacketProcessor->packetsToProcessCount();
|
||||
statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] =
|
||||
(double)_octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||
statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] =
|
||||
|
|
3
cmake/externals/LibOVR/CMakeLists.txt
vendored
3
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -86,4 +86,7 @@ elseif(NOT ANDROID)
|
|||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARY} ${${EXTERNAL_NAME_UPPER}_LIBRARY_EXTRAS} CACHE TYPE INTERNAL)
|
||||
endif()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
|
||||
|
|
3
cmake/externals/bullet/CMakeLists.txt
vendored
3
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -38,6 +38,9 @@ else ()
|
|||
)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/glew/CMakeLists.txt
vendored
3
cmake/externals/glew/CMakeLists.txt
vendored
|
@ -12,6 +12,9 @@ if (WIN32)
|
|||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/glm/CMakeLists.txt
vendored
3
cmake/externals/glm/CMakeLists.txt
vendored
|
@ -12,6 +12,9 @@ ExternalProject_Add(
|
|||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/gverb/CMakeLists.txt
vendored
3
cmake/externals/gverb/CMakeLists.txt
vendored
|
@ -16,6 +16,9 @@ ExternalProject_Add(
|
|||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/oglplus/CMakeLists.txt
vendored
3
cmake/externals/oglplus/CMakeLists.txt
vendored
|
@ -12,6 +12,9 @@ ExternalProject_Add(
|
|||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include ${SOURCE_DIR}/implement CACHE TYPE INTERNAL)
|
||||
|
|
3
cmake/externals/openvr/CMakeLists.txt
vendored
3
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -15,6 +15,9 @@ ExternalProject_Add(
|
|||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNAL)
|
||||
|
|
2
cmake/externals/polyvox/CMakeLists.txt
vendored
2
cmake/externals/polyvox/CMakeLists.txt
vendored
|
@ -12,6 +12,8 @@ ExternalProject_Add(
|
|||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
|
|
3
cmake/externals/qxmpp/CMakeLists.txt
vendored
3
cmake/externals/qxmpp/CMakeLists.txt
vendored
|
@ -36,6 +36,9 @@ ExternalProject_Add(
|
|||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL Xcode)
|
||||
|
|
3
cmake/externals/sdl2/CMakeLists.txt
vendored
3
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -38,6 +38,9 @@ else ()
|
|||
)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (APPLE)
|
||||
|
|
3
cmake/externals/soxr/CMakeLists.txt
vendored
3
cmake/externals/soxr/CMakeLists.txt
vendored
|
@ -16,6 +16,9 @@ ExternalProject_Add(
|
|||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/tbb/CMakeLists.txt
vendored
3
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -53,6 +53,9 @@ else ()
|
|||
)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
3
cmake/externals/vhacd/CMakeLists.txt
vendored
3
cmake/externals/vhacd/CMakeLists.txt
vendored
|
@ -16,6 +16,9 @@ ExternalProject_Add(
|
|||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
|
139
cmake/macros/SetupHifiTestCase.cmake
Normal file
139
cmake/macros/SetupHifiTestCase.cmake
Normal file
|
@ -0,0 +1,139 @@
|
|||
#
|
||||
# SetupHifiTestCase.cmake
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# Sets up a hifi testcase using QtTest.
|
||||
# Can be called with arguments; like setup_hifi_project, the arguments correspond to qt modules, so call it
|
||||
# via setup_hifi_testcase(Script Network Qml) to build the testcase with Qt5Script, Qt5Network, and Qt5Qml linked,
|
||||
# for example.
|
||||
|
||||
# One special quirk of this is that because we are creating multiple testcase targets (instead of just one),
|
||||
# any dependencies and other setup that the testcase has must be declared in a macro, and setup_hifi_testcase()
|
||||
# must be called *after* this declaration, not before it.
|
||||
|
||||
# Here's a full example:
|
||||
# tests/my-foo-test/CMakeLists.txt:
|
||||
#
|
||||
# # Declare testcase dependencies
|
||||
# macro (setup_hifi_testcase)
|
||||
# bunch
|
||||
# of
|
||||
# custom
|
||||
# dependencies...
|
||||
#
|
||||
# link_hifi_libraries(shared networking etc)
|
||||
#
|
||||
# copy_dlls_beside_windows_executable()
|
||||
# endmacro()
|
||||
#
|
||||
# setup_hifi_testcase(Network etc)
|
||||
#
|
||||
# Additionally, all .cpp files in the src dir (eg tests/my-foo-test/src) must:
|
||||
# - Contain exactly one test class (any supporting code must be either external or inline in a .hpp file)
|
||||
# - Be built against QtTestLib (test class should be a QObject, with test methods defined as private slots)
|
||||
# - Contain a QTEST_MAIN declaration at the end of the file (or at least be a standalone executable)
|
||||
#
|
||||
# All other testing infrastructure is generated automatically.
|
||||
#
|
||||
|
||||
macro(SETUP_HIFI_TESTCASE)
|
||||
if (NOT DEFINED TEST_PROJ_NAME)
|
||||
message(SEND_ERROR "Missing TEST_PROJ_NAME (setup_hifi_testcase was called incorrectly?)")
|
||||
elseif (NOT COMMAND SETUP_TESTCASE_DEPENDENCIES)
|
||||
message(SEND_ERROR "Missing testcase dependencies declaration (SETUP_TESTCASE_DEPENDENCIES)")
|
||||
elseif (DEFINED ${TEST_PROJ_NAME}_BUILT)
|
||||
message(WARNING "testcase \"" ${TEST_PROJ_NAME} "\" was already built")
|
||||
else ()
|
||||
set(${TEST_PROJ_NAME}_BUILT 1)
|
||||
|
||||
file(GLOB TEST_PROJ_SRC_FILES src/*)
|
||||
file(GLOB TEST_PROJ_SRC_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/*)
|
||||
|
||||
foreach (DIR ${TEST_PROJ_SRC_SUBDIRS})
|
||||
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/${DIR}")
|
||||
file(GLOB DIR_CONTENTS "src/${DIR}/*")
|
||||
set(TEST_PROJ_SRC_FILES ${TEST_PROJ_SRC_FILES} "${DIR_CONTENTS}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Find test classes to build into test executables.
|
||||
# Warn about any .cpp files that are *not* test classes (*Test[s].cpp), since those files will not be used.
|
||||
foreach (SRC_FILE ${TEST_PROJ_SRC_FILES})
|
||||
string(REGEX MATCH ".+Tests?\\.cpp$" TEST_CPP_FILE ${SRC_FILE})
|
||||
string(REGEX MATCH ".+\\.cpp$" NON_TEST_CPP_FILE ${SRC_FILE})
|
||||
if (TEST_CPP_FILE)
|
||||
list(APPEND TEST_CASE_FILES ${TEST_CPP_FILE})
|
||||
elseif (NON_TEST_CPP_FILE)
|
||||
message(WARNING "ignoring .cpp file (not a test class -- this will not be linked or compiled!): " ${NON_TEST_CPP_FILE})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
if (TEST_CASE_FILES)
|
||||
set(TEST_PROJ_TARGETS "")
|
||||
|
||||
# Add each test class executable (duplicates functionality in SetupHifiProject.cmake)
|
||||
# The resulting targets will be buried inside of hidden/test-executables so that they don't clutter up
|
||||
# the project view in Xcode, Visual Studio, etc.
|
||||
foreach (TEST_FILE ${TEST_CASE_FILES})
|
||||
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
|
||||
set(TARGET_NAME ${TEST_PROJ_NAME}-${TEST_NAME})
|
||||
|
||||
project(${TARGET_NAME})
|
||||
|
||||
# grab the implemenation and header files
|
||||
set(TARGET_SRCS ${TEST_FILE}) # only one source / .cpp file (the test class)
|
||||
|
||||
add_executable(${TARGET_NAME} ${TEST_FILE})
|
||||
add_test(${TARGET_NAME}-test ${TARGET_NAME})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
list (APPEND ${TEST_PROJ_NAME}_TARGETS ${TARGET_NAME})
|
||||
#list (APPEND ALL_TEST_TARGETS ${TARGET_NAME})
|
||||
|
||||
set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN})
|
||||
|
||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core Test)
|
||||
|
||||
# find these Qt modules and link them to our own target
|
||||
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
|
||||
|
||||
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
|
||||
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
|
||||
endforeach()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "hidden/test-executables")
|
||||
|
||||
# handle testcase-specific dependencies (this a macro that should be defined in the cmakelists.txt file in each tests subdir)
|
||||
|
||||
SETUP_TESTCASE_DEPENDENCIES ()
|
||||
endforeach ()
|
||||
|
||||
set(TEST_TARGET ${TEST_PROJ_NAME}-tests)
|
||||
|
||||
# Add a dummy target so that the project files are visible.
|
||||
# This target will also build + run the other test targets using ctest when built.
|
||||
|
||||
add_custom_target(${TEST_TARGET}
|
||||
COMMAND ctest .
|
||||
SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target
|
||||
DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
|
||||
set_target_properties(${TEST_TARGET} PROPERTIES
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests")
|
||||
|
||||
list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
|
||||
set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
|
||||
else ()
|
||||
message(WARNING "No testcases in " ${TEST_PROJ_NAME})
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro(SETUP_HIFI_TESTCASE)
|
|
@ -484,7 +484,7 @@
|
|||
"type": "checkbox",
|
||||
"label": "Edit Logging",
|
||||
"help": "Logging of all edits to entities",
|
||||
"default": true,
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
|
|
91
examples/animationPerfTest.js
Normal file
91
examples/animationPerfTest.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/07/01
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var NUM_MOONS = 20;
|
||||
// 1 = 60Hz, 2 = 30Hz, 3 = 20Hz, etc
|
||||
var UPDATE_FREQUENCY_DIVISOR = 2;
|
||||
|
||||
var MAX_RANGE = 75.0;
|
||||
var LIFETIME = 600;
|
||||
var SCALE = 0.1;
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position,
|
||||
Vec3.multiply(MAX_RANGE * SCALE, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
var PARTICLE_MIN_SIZE = 2.50;
|
||||
var PARTICLE_MAX_SIZE = 2.50;
|
||||
|
||||
|
||||
var planet = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
position: center,
|
||||
dimensions: { x: 10 * SCALE, y: 10 * SCALE, z: 10 * SCALE },
|
||||
color: { red: 0, green: 0, blue: 255 },
|
||||
ignoreCollisions: true,
|
||||
collisionsWillMove: false,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
var moons = [];
|
||||
|
||||
// Create initial test particles that will move according to gravity from the planets
|
||||
for (var i = 0; i < NUM_MOONS; i++) {
|
||||
var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE;
|
||||
radius *= SCALE;
|
||||
var gray = Math.random() * 155;
|
||||
var position = { x: 10 , y: i * 3, z: 0 };
|
||||
var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
|
||||
if (i == 0) {
|
||||
color = { red: 255, green: 0, blue: 0 };
|
||||
radius = 6 * SCALE
|
||||
}
|
||||
moons.push(Entities.addEntity({
|
||||
type: "Sphere",
|
||||
position: Vec3.sum(center, position),
|
||||
dimensions: { x: radius, y: radius, z: radius },
|
||||
color: color,
|
||||
ignoreCollisions: true,
|
||||
lifetime: LIFETIME,
|
||||
collisionsWillMove: false
|
||||
}));
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
|
||||
function scriptEnding() {
|
||||
Entities.deleteEntity(planet);
|
||||
for (var i = 0; i < moons.length; i++) {
|
||||
Entities.deleteEntity(moons[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var totalTime = 0.0;
|
||||
var updateCount = 0;
|
||||
function update(deltaTime) {
|
||||
// Apply gravitational force from planets
|
||||
totalTime += deltaTime;
|
||||
updateCount++;
|
||||
if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
var planetProperties = Entities.getEntityProperties(planet);
|
||||
var center = planetProperties.position;
|
||||
var particlePos = Entities.getEntityProperties(moons[0]).position;
|
||||
var relativePos = Vec3.subtract(particlePos.position, center);
|
||||
for (var t = 0; t < moons.length; t++) {
|
||||
var thetaDelta = (Math.PI * 2.0 / NUM_MOONS) * t;
|
||||
var y = Math.sin(totalTime + thetaDelta) * 10.0 * SCALE;
|
||||
var x = Math.cos(totalTime + thetaDelta) * 10.0 * SCALE;
|
||||
var newBasePos = Vec3.sum({ x: 0, y: y, z: x }, center);
|
||||
Entities.editEntity(moons[t], { position: newBasePos});
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
42
examples/debug-actions.js
Normal file
42
examples/debug-actions.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
function printData(data) {
|
||||
var str = '';
|
||||
for (var key in data) {
|
||||
if (typeof data[key] == 'object') str += key + printData(data[key]) + ' ';
|
||||
else str += key + ':' + data[key] + ' ';
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
function printActions(deltaTime) {
|
||||
var printed = false;
|
||||
var ids = Entities.findEntities(MyAvatar.position, 10);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var entityID = ids[i];
|
||||
var actionIDs = Entities.getActionIDs(entityID);
|
||||
if (actionIDs.length > 0) {
|
||||
var props = Entities.getEntityProperties(entityID);
|
||||
var output = props.name + " ";
|
||||
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
|
||||
actionID = actionIDs[actionIndex];
|
||||
actionArguments = Entities.getActionArguments(entityID, actionID);
|
||||
// output += actionArguments['type'];
|
||||
output += "(" + printData(actionArguments) + ") ";
|
||||
}
|
||||
if (!printed) {
|
||||
print("---------");
|
||||
printed = true;
|
||||
}
|
||||
print(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Script.setInterval(printActions, 5000);
|
||||
|
||||
// Script.update.connect(printActions);
|
|
@ -1321,6 +1321,15 @@ PropertiesTool = function(opts) {
|
|||
pushCommandForSelections();
|
||||
selectionManager._update();
|
||||
}
|
||||
} else if (data.action == "reloadScript") {
|
||||
if (selectionManager.hasSelection()) {
|
||||
var timestamp = Date.now();
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
Entities.editEntity(selectionManager.selections[i], {
|
||||
scriptTimestamp: timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (data.action == "centerAtmosphereToZone") {
|
||||
if (selectionManager.hasSelection()) {
|
||||
selectionManager.saveProperties();
|
||||
|
|
|
@ -250,6 +250,8 @@
|
|||
var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
|
||||
var elLifetime = document.getElementById("property-lifetime");
|
||||
var elScriptURL = document.getElementById("property-script-url");
|
||||
var elScriptTimestamp = document.getElementById("property-script-timestamp");
|
||||
var elReloadScriptButton = document.getElementById("reload-script-button");
|
||||
var elUserData = document.getElementById("property-user-data");
|
||||
|
||||
var elColorSection = document.getElementById("color-section");
|
||||
|
@ -470,6 +472,7 @@
|
|||
elCollisionSoundURL.value = properties.collisionSoundURL;
|
||||
elLifetime.value = properties.lifetime;
|
||||
elScriptURL.value = properties.script;
|
||||
elScriptTimestamp.value = properties.scriptTimestamp;
|
||||
elUserData.value = properties.userData;
|
||||
|
||||
elHyperlinkHref.value = properties.href;
|
||||
|
@ -688,6 +691,7 @@
|
|||
|
||||
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
|
||||
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
|
||||
elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
|
||||
elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
|
||||
|
||||
var colorChangeFunction = createEmitColorPropertyUpdateFunction(
|
||||
|
@ -889,6 +893,12 @@
|
|||
percentage: parseInt(elRescaleDimensionsPct.value),
|
||||
}));
|
||||
});
|
||||
elReloadScriptButton.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
action: "reloadScript"
|
||||
}));
|
||||
});
|
||||
elCenterAtmosphereToZone.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
|
@ -1181,7 +1191,10 @@
|
|||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Script URL</div>
|
||||
<div class="label">Script URL
|
||||
<input type="hidden" id="property-script-timestamp" class="value"></input>
|
||||
<input type="button" id="reload-script-button" value="Reload"></input>
|
||||
</div>
|
||||
<div class="value">
|
||||
<input id="property-script-url" class="url"></input>
|
||||
</div>
|
||||
|
|
4
examples/html/jquery-2.1.4.min.js
vendored
Normal file
4
examples/html/jquery-2.1.4.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -260,6 +260,11 @@ table#properties-list {
|
|||
height: 1.2em;
|
||||
}
|
||||
|
||||
#properties-list .label input[type=button] {
|
||||
float: right;
|
||||
padding: 0 5px 1px;
|
||||
}
|
||||
|
||||
#properties-list .value > div{
|
||||
padding: 3pt 0;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
test = function(name, func) {
|
||||
var test = function(name, func) {
|
||||
print("Running test: " + name);
|
||||
|
||||
var unitTest = new UnitTest(name, func);
|
||||
|
|
|
@ -40,7 +40,7 @@ Avatar = function() {
|
|||
// settings
|
||||
this.headFree = true;
|
||||
this.armsFree = this.hydraCheck(); // automatically sets true to enable Hydra support - temporary fix
|
||||
this.makesFootStepSounds = true;
|
||||
this.makesFootStepSounds = false;
|
||||
this.blenderPreRotations = false; // temporary fix
|
||||
this.animationSet = undefined; // currently just one animation set
|
||||
this.setAnimationSet = function(animationSet) {
|
||||
|
|
|
@ -38,7 +38,7 @@ var mouseLook = (function () {
|
|||
keyboardID = 0;
|
||||
|
||||
function onKeyPressEvent(event) {
|
||||
if (event.text == 'm' && event.isMeta) {
|
||||
if (event.text == 'M') {
|
||||
active = !active;
|
||||
updateMapping();
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ var mouseLook = (function () {
|
|||
}
|
||||
|
||||
function setupMenu() {
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: "Mouselook Mode", shortcutKey: "META+M",
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: "Mouselook Mode", shortcutKey: "SHIFT+M",
|
||||
afterItem: "Mirror", isCheckable: true, isChecked: false });
|
||||
}
|
||||
|
||||
|
|
|
@ -16,41 +16,60 @@ var controllerID;
|
|||
var controllerActive;
|
||||
var stickID = null;
|
||||
var actionID = nullActionID;
|
||||
// sometimes if this is run immediately the stick doesn't get created? use a timer.
|
||||
Script.setTimeout(function() {
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
|
||||
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||
dimensions: {x: .11, y: .11, z: 1.0},
|
||||
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: .1,
|
||||
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
|
||||
hand: hand,
|
||||
timeScale: 0.15});
|
||||
}, 3000);
|
||||
var makingNewStick = false;
|
||||
|
||||
function makeNewStick() {
|
||||
if (makingNewStick) {
|
||||
return;
|
||||
}
|
||||
makingNewStick = true;
|
||||
cleanUp();
|
||||
// sometimes if this is run immediately the stick doesn't get created? use a timer.
|
||||
Script.setTimeout(function() {
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "stick",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
|
||||
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||
dimensions: {x: .11, y: .11, z: 1.0},
|
||||
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: .1,
|
||||
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
|
||||
hand: hand,
|
||||
timeScale: 0.15});
|
||||
if (actionID == nullActionID) {
|
||||
cleanUp();
|
||||
}
|
||||
makingNewStick = false;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
|
||||
function cleanUp() {
|
||||
Entities.deleteEntity(stickID);
|
||||
if (stickID) {
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function positionStick(stickOrientation) {
|
||||
var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
|
||||
var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
|
||||
Entities.updateAction(stickID, actionID, {relativePosition: offset,
|
||||
relativeRotation: stickOrientation});
|
||||
if (!Entities.updateAction(stickID, actionID, {relativePosition: offset, relativeRotation: stickOrientation})) {
|
||||
makeNewStick();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (!stickID || actionID == nullActionID) {
|
||||
makeNewStick();
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
|
@ -89,6 +108,8 @@ function update(deltaTime){
|
|||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Script.update.connect(update);
|
||||
makeNewStick();
|
||||
|
|
1316
examples/utilities/tools/cookies.js
Executable file → Normal file
1316
examples/utilities/tools/cookies.js
Executable file → Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@
|
|||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var panel = new Panel(10, 800);
|
||||
var panel = new Panel(10, 100);
|
||||
|
||||
panel.newSlider("Num Feed Opaques", 0, 1000,
|
||||
function(value) { },
|
||||
|
@ -66,6 +66,12 @@ panel.newSlider("Max Drawn Overlay3Ds", -1, 100,
|
|||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value); },
|
||||
function() { return Scene.doEngineDisplayItemStatus(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
|
|
|
@ -16,6 +16,8 @@ find_package(Qt5LinguistToolsMacros)
|
|||
|
||||
if (DEFINED ENV{JOB_ID})
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
elseif (DEFINED ENV{ghprbPullId})
|
||||
set(BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}")
|
||||
else ()
|
||||
set(BUILD_SEQ "dev")
|
||||
endif ()
|
||||
|
|
BIN
interface/resources/images/link.png
Normal file
BIN
interface/resources/images/link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
50
interface/resources/images/reload-script.svg
Normal file
50
interface/resources/images/reload-script.svg
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 100 100"
|
||||
enable-background="new 0 0 100 100"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="reload-script-43339.svg"><metadata
|
||||
id="metadata14"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs12" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1184"
|
||||
inkscape:window-height="655"
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.36"
|
||||
inkscape:cx="-12.5"
|
||||
inkscape:cy="56.355932"
|
||||
inkscape:window-x="424"
|
||||
inkscape:window-y="79"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" /><g
|
||||
id="g4"
|
||||
style="fill:#c4c4c4;fill-opacity:1"
|
||||
transform="matrix(1.0520186,0,0,1.0809642,-4.9561957,-7.0141066)"><g
|
||||
id="g6"
|
||||
style="fill:#c4c4c4;fill-opacity:1"><path
|
||||
d="m 8.139,47.902 14.505,0 c 1.51,0 2.838,-0.987 3.28,-2.429 1.222,-4.042 3.425,-7.649 6.357,-10.583 4.643,-4.627 10.918,-7.442 17.982,-7.45 7.056,0.008 13.331,2.823 17.979,7.45 0.144,0.147 0.271,0.31 0.411,0.457 L 59.46,46.155 c -0.257,0.299 -0.313,0.724 -0.157,1.078 0.173,0.365 0.53,0.59 0.923,0.584 l 38.526,0.111 c 0.294,0.005 0.581,-0.129 0.771,-0.352 0.197,-0.231 0.278,-0.527 0.23,-0.818 L 93.483,8.741 C 93.421,8.353 93.137,8.038 92.753,7.933 92.375,7.828 91.968,7.952 91.714,8.248 L 82.256,19.366 C 73.983,11.441 62.706,6.512 50.337,6.491 50.279,6.488 50.206,6.488 50.122,6.491 37.396,6.527 25.801,11.73 17.469,20.08 11.119,26.417 6.604,34.634 4.776,43.81 c -0.198,1.013 0.062,2.046 0.715,2.843 0.655,0.793 1.617,1.249 2.648,1.249 z m 90.845,10.939 c -0.652,-0.798 -1.618,-1.259 -2.646,-1.259 l -14.506,0 c -1.507,0 -2.836,0.991 -3.279,2.435 -1.223,4.039 -3.427,7.649 -6.357,10.587 -4.64,4.622 -10.915,7.439 -17.982,7.45 -7.056,-0.011 -13.331,-2.828 -17.979,-7.45 -0.144,-0.146 -0.269,-0.32 -0.41,-0.464 l 9.196,-10.811 c 0.256,-0.294 0.311,-0.717 0.154,-1.076 -0.173,-0.356 -0.527,-0.587 -0.923,-0.585 L 5.727,57.561 c -0.294,0 -0.581,0.126 -0.773,0.352 -0.195,0.229 -0.277,0.524 -0.229,0.824 l 6.271,38.012 c 0.062,0.388 0.345,0.701 0.729,0.812 0.378,0.104 0.784,-0.025 1.039,-0.322 L 22.22,86.125 c 8.278,7.922 19.553,12.859 31.921,12.87 0.06,0.005 0.131,0.005 0.215,0 12.729,-0.032 24.323,-5.237 32.655,-13.583 6.348,-6.338 10.865,-14.561 12.69,-23.728 0.199,-1.012 -0.062,-2.045 -0.717,-2.843 z"
|
||||
id="path8"
|
||||
style="fill:#c4c4c4;fill-opacity:1"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -18,11 +18,11 @@ Hifi.Tooltip {
|
|||
offsetX = (lastMousePosition.x > surfaceSize.width/2) ? -root.width : 0
|
||||
offsetY = (lastMousePosition.y > surfaceSize.height/2) ? -root.height : 0
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: border
|
||||
color: "#7f000000"
|
||||
width: 322
|
||||
color: "#BF000000"
|
||||
width: 330
|
||||
height: col.height + hifi.layout.spacing * 2
|
||||
|
||||
Column {
|
||||
|
@ -38,14 +38,14 @@ Hifi.Tooltip {
|
|||
Text {
|
||||
id: textPlace
|
||||
color: "white"
|
||||
font.underline: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: hifi.fonts.pixelSize / 2
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: hifi.fonts.pixelSize * 2
|
||||
text: root.title
|
||||
wrapMode: Text.WrapAnywhere
|
||||
|
||||
/* Uncomment for debugging to see the extent of the
|
||||
|
||||
/* Uncomment for debugging to see the extent of the
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#7fff00ff"
|
||||
|
@ -53,12 +53,34 @@ Hifi.Tooltip {
|
|||
*/
|
||||
}
|
||||
|
||||
Image {
|
||||
id: tooltipPic
|
||||
source: "../images/NoPictureProvided.svg"
|
||||
Rectangle {
|
||||
id: seperator
|
||||
color: "white"
|
||||
width: col.width
|
||||
height: hifi.layout.spacing / 3
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
verticalAlignment: Image.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
id: firstSpacer
|
||||
width: col.width
|
||||
height: 5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: tooltipPic
|
||||
source: root.imageURL
|
||||
height: 180
|
||||
width: 320
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Item {
|
||||
id: secondSpacer
|
||||
width: col.width
|
||||
height: 5
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -67,10 +89,10 @@ Hifi.Tooltip {
|
|||
width: border.implicitWidth
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: hifi.fonts.pixelSize / 2
|
||||
text: root.text
|
||||
wrapMode: Text.WrapAnywhere
|
||||
text: root.description
|
||||
font.pixelSize: 16
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
#include <SceneScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SimpleAverage.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TextRenderer.h>
|
||||
#include <Tooltip.h>
|
||||
|
@ -178,6 +179,7 @@ using namespace std;
|
|||
// Starfield information
|
||||
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||
static unsigned STARFIELD_SEED = 1;
|
||||
static uint8_t THROTTLED_IDLE_TIMER_DELAY = 10;
|
||||
|
||||
const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB
|
||||
|
||||
|
@ -604,6 +606,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_settingsTimer.setSingleShot(false);
|
||||
_settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL);
|
||||
_settingsThread.start();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
_trayIcon->show();
|
||||
|
||||
|
@ -1032,7 +1039,7 @@ void Application::showEditEntitiesHelp() {
|
|||
InfoView::show(INFO_EDIT_ENTITIES_PATH);
|
||||
}
|
||||
|
||||
void Application::resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size) {
|
||||
void Application::resetCameras(Camera& camera, const glm::uvec2& size) {
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::configureCamera(camera);
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
|
@ -1055,7 +1062,6 @@ void Application::resizeGL() {
|
|||
if (_renderResolution != toGlm(renderSize)) {
|
||||
_renderResolution = toGlm(renderSize);
|
||||
DependencyManager::get<TextureCache>()->setFrameBufferSize(renderSize);
|
||||
resetCamerasOnResizeGL(_myCamera, _renderResolution);
|
||||
|
||||
glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu???
|
||||
|
||||
|
@ -1063,6 +1069,8 @@ void Application::resizeGL() {
|
|||
glLoadIdentity();
|
||||
}
|
||||
|
||||
resetCameras(_myCamera, _renderResolution);
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
auto canvasSize = _glWidget->size();
|
||||
|
@ -1779,8 +1787,24 @@ void Application::checkFPS() {
|
|||
}
|
||||
|
||||
void Application::idle() {
|
||||
PerformanceTimer perfTimer("idle");
|
||||
static SimpleAverage<float> interIdleDurations;
|
||||
static uint64_t lastIdleEnd{ 0 };
|
||||
|
||||
if (lastIdleEnd != 0) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
interIdleDurations.update(now - lastIdleEnd);
|
||||
static uint64_t lastReportTime = now;
|
||||
if ((now - lastReportTime) >= (USECS_PER_SECOND)) {
|
||||
static QString LOGLINE("Average inter-idle time: %1 us for %2 samples");
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::LogExtraTimings)) {
|
||||
qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount());
|
||||
}
|
||||
interIdleDurations.reset();
|
||||
lastReportTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("idle");
|
||||
if (_aboutToQuit) {
|
||||
return; // bail early, nothing to do here.
|
||||
}
|
||||
|
@ -1823,13 +1847,15 @@ void Application::idle() {
|
|||
_idleLoopStdev.reset();
|
||||
}
|
||||
|
||||
// After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
|
||||
idleTimer->start(2);
|
||||
}
|
||||
}
|
||||
// After finishing all of the above work, ensure the idle timer is set to the proper interval,
|
||||
// depending on whether we're throttling or not
|
||||
idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 0);
|
||||
}
|
||||
|
||||
// check for any requested background downloads.
|
||||
emit checkBackgroundDownloads();
|
||||
lastIdleEnd = usecTimestampNow();
|
||||
}
|
||||
|
||||
void Application::setFullscreen(bool fullscreen) {
|
||||
|
@ -2202,6 +2228,7 @@ void Application::init() {
|
|||
|
||||
// Make sure any new sounds are loaded as soon as know about them.
|
||||
connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
}
|
||||
|
||||
void Application::closeMirrorView() {
|
||||
|
@ -3521,6 +3548,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems();
|
||||
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
|
||||
|
||||
renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus();
|
||||
|
||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||
|
||||
renderContext.args = renderArgs;
|
||||
|
@ -3542,7 +3571,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
}
|
||||
//Render the sixense lasers
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
|
||||
_myAvatar->renderLaserPointers();
|
||||
_myAvatar->renderLaserPointers(*renderArgs->_batch);
|
||||
}
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
|
@ -4048,6 +4077,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&)));
|
||||
|
||||
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
|
||||
connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool)));
|
||||
|
||||
scriptEngine->registerGlobalObject("Overlays", &_overlays);
|
||||
qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);
|
||||
|
@ -4263,7 +4293,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
}
|
||||
|
||||
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
||||
bool loadScriptFromEditor, bool activateMainWindow) {
|
||||
bool loadScriptFromEditor, bool activateMainWindow, bool reload) {
|
||||
|
||||
if (isAboutToQuit()) {
|
||||
return NULL;
|
||||
|
@ -4292,7 +4322,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError);
|
||||
|
||||
// get the script engine object to load the script at the designated script URL
|
||||
scriptEngine->loadURL(scriptUrl);
|
||||
scriptEngine->loadURL(scriptUrl, reload);
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
|
@ -4303,6 +4333,10 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
return scriptEngine;
|
||||
}
|
||||
|
||||
void Application::reloadScript(const QString& scriptName, bool isUserLoaded) {
|
||||
loadScript(scriptName, isUserLoaded, false, false, true);
|
||||
}
|
||||
|
||||
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
|
||||
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
|
||||
|
||||
|
@ -4330,14 +4364,25 @@ void Application::scriptFinished(const QString& scriptName) {
|
|||
}
|
||||
|
||||
void Application::stopAllScripts(bool restart) {
|
||||
// stops all current running scripts
|
||||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&)));
|
||||
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
||||
}
|
||||
it.value()->stop();
|
||||
qCDebug(interfaceapp) << "stopping script..." << it.key();
|
||||
|
@ -4345,13 +4390,20 @@ void Application::stopAllScripts(bool restart) {
|
|||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
// TODO: expose animation priorities and provide a layered animation control system.
|
||||
_myAvatar->clearJointAnimationPriorities();
|
||||
_myAvatar->clearScriptableSettings();
|
||||
}
|
||||
|
||||
void Application::stopScript(const QString &scriptName) {
|
||||
void Application::stopScript(const QString &scriptName, bool restart) {
|
||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
||||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
||||
_scriptEnginesHash.value(scriptURLString)->stop();
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(scriptName);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
||||
}
|
||||
scriptEngine->stop();
|
||||
qCDebug(interfaceapp) << "stopping script..." << scriptName;
|
||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
|
@ -4367,6 +4419,10 @@ void Application::reloadAllScripts() {
|
|||
stopAllScripts(true);
|
||||
}
|
||||
|
||||
void Application::reloadOneScript(const QString& scriptName) {
|
||||
stopScript(scriptName, true);
|
||||
}
|
||||
|
||||
void Application::loadDefaultScripts() {
|
||||
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
|
|
|
@ -409,15 +409,19 @@ public slots:
|
|||
bool acceptSnapshot(const QString& urlString);
|
||||
bool askToSetAvatarUrl(const QString& url);
|
||||
bool askToLoadScript(const QString& scriptFilenameOrURL);
|
||||
|
||||
ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true,
|
||||
bool loadScriptFromEditor = false, bool activateMainWindow = false);
|
||||
bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
|
||||
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
|
||||
void scriptFinished(const QString& scriptName);
|
||||
void stopAllScripts(bool restart = false);
|
||||
void stopScript(const QString& scriptName);
|
||||
void stopScript(const QString& scriptName, bool restart = false);
|
||||
void reloadAllScripts();
|
||||
void reloadOneScript(const QString& scriptName);
|
||||
void loadDefaultScripts();
|
||||
void toggleRunningScriptsWidget();
|
||||
void saveScripts();
|
||||
|
||||
void showFriendsWindow();
|
||||
void friendsWindowClosed();
|
||||
|
||||
|
@ -481,7 +485,7 @@ private slots:
|
|||
void setCursorVisible(bool visible);
|
||||
|
||||
private:
|
||||
void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size);
|
||||
void resetCameras(Camera& camera, const glm::uvec2& size);
|
||||
void updateProjectionMatrix();
|
||||
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true);
|
||||
|
||||
|
|
|
@ -18,32 +18,53 @@
|
|||
#include "InterfaceActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer interfaceActionFactory(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_OFFSET:
|
||||
return (EntityActionPointer) new ObjectActionOffset(type, id, ownerEntity);
|
||||
case ACTION_TYPE_SPRING:
|
||||
return (EntityActionPointer) new ObjectActionSpring(type, id, ownerEntity);
|
||||
case ACTION_TYPE_HOLD:
|
||||
return (EntityActionPointer) new AvatarActionHold(type, id, ownerEntity);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = nullptr;
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_OFFSET:
|
||||
action = (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
|
||||
break;
|
||||
case ACTION_TYPE_SPRING:
|
||||
action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
break;
|
||||
case ACTION_TYPE_HOLD:
|
||||
action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
|
||||
break;
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
ownerEntity->addAction(simulation, action);
|
||||
return action;
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factoryBA(EntitySimulation* simulation,
|
||||
EntityItemPointer ownerEntity,
|
||||
QByteArray data) {
|
||||
QDataStream serializedArgumentStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedArgumentStream >> type;
|
||||
serializedArgumentStream >> id;
|
||||
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
}
|
||||
|
||||
action = nullptr;
|
||||
return action;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ public:
|
|||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
||||
EntityItemPointer ownerEntity,
|
||||
QByteArray data);
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
||||
|
|
|
@ -12,3 +12,4 @@
|
|||
#include "InterfaceLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(interfaceapp, "hifi.interface")
|
||||
Q_LOGGING_CATEGORY(interfaceapp_timing, "hifi.interface.timing")
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(interfaceapp)
|
||||
Q_DECLARE_LOGGING_CATEGORY(interfaceapp_timing)
|
||||
|
||||
#endif // hifi_InterfaceLogging_h
|
||||
|
|
|
@ -529,7 +529,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests()));
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace MenuOption {
|
|||
const QString LodTools = "LOD Tools";
|
||||
const QString Login = "Login";
|
||||
const QString Log = "Log";
|
||||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
|
@ -268,6 +269,7 @@ namespace MenuOption {
|
|||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowBordersEntityNodes = "Show Entity Nodes";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
|
||||
const QString SimpleShadows = "Simple";
|
||||
const QString SixenseEnabled = "Enable Hydra Support";
|
||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||
|
|
|
@ -449,26 +449,26 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
if (renderBounding && shouldRenderHead(renderArgs)) {
|
||||
_skeletonModel.renderBoundingCollisionShapes(0.7f);
|
||||
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the avatar being looked at, render a little ball above their head
|
||||
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
|
||||
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||
const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
|
||||
const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
|
||||
glm::vec3 position;
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z);
|
||||
} else {
|
||||
position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
|
||||
}
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
|
||||
, 15, 15, LOOK_AT_INDICATOR_COLOR);
|
||||
// If this is the avatar being looked at, render a little ball above their head
|
||||
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
|
||||
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||
const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
|
||||
const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
|
||||
glm::vec3 position;
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z);
|
||||
} else {
|
||||
position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
|
||||
}
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
|
||||
, 15, 15, LOOK_AT_INDICATOR_COLOR);
|
||||
}
|
||||
|
||||
// quick check before falling into the code below:
|
||||
|
@ -694,9 +694,9 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas)
|
||||
|
||||
// Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY
|
||||
double pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
float pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
// Handles pixel density (especially for macs retina displays)
|
||||
double devicePixelRatio = qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
float devicePixelRatio = (float)qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
|
||||
// Compute correct scale to apply
|
||||
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
|
||||
|
@ -708,9 +708,10 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight;
|
||||
|
||||
// Compute orientation
|
||||
glm::vec3 eulerAngles = ::safeEulerAngles(frustum.getOrientation());
|
||||
eulerAngles.z = 0.0f; // Cancel roll
|
||||
glm::quat orientation(eulerAngles); // back to quaternions
|
||||
glm::vec3 dPosition = frustum.getPosition() - getPosition();
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
|
||||
// Set transform (The order IS important)
|
||||
result.setTranslation(textPosition);
|
||||
|
@ -749,7 +750,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
|
|||
const int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// Compute background position/size
|
||||
static const float SLIGHTLY_BEHIND = -0.05f;
|
||||
static const float SLIGHTLY_IN_FRONT = 0.1f;
|
||||
const int border = 0.1f * nameDynamicRect.height();
|
||||
const int left = text_x - border;
|
||||
const int bottom = text_y - border;
|
||||
|
@ -764,16 +765,16 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
|
|||
|
||||
// Compute display name transform
|
||||
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
|
||||
|
||||
// Render background slightly behind to avoid z-fighting
|
||||
auto backgroundTransform = textTransform;
|
||||
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
|
||||
batch.setModelTransform(backgroundTransform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
batch.setModelTransform(textTransform);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor);
|
||||
// Render actual name
|
||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
// Render text slightly in front to avoid z-fighting
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
|
||||
batch.setModelTransform(textTransform);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
|
@ -1010,7 +1011,7 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
int Avatar::_jointConesID = GeometryCache::UNKNOWN_ID;
|
||||
|
||||
// render a makeshift cone section that serves as a body part connecting joint spheres
|
||||
void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
||||
void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
|
||||
float radius1, float radius2, const glm::vec4& color) {
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -1057,7 +1058,7 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
|||
// TODO: this is really inefficient constantly recreating these vertices buffers. It would be
|
||||
// better if the avatars cached these buffers for each of the joints they are rendering
|
||||
geometryCache->updateVertices(_jointConesID, points, color);
|
||||
geometryCache->renderVertices(gpu::TRIANGLES, _jointConesID);
|
||||
geometryCache->renderVertices(batch, gpu::TRIANGLES, _jointConesID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ public:
|
|||
|
||||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
||||
static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
|
||||
float radius1, float radius2, const glm::vec4& color);
|
||||
|
||||
virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { }
|
||||
|
|
|
@ -9,13 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include "AvatarActionHold.h"
|
||||
|
||||
AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity) {
|
||||
const uint16_t AvatarActionHold::holdVersion = 1;
|
||||
|
||||
AvatarActionHold::AvatarActionHold(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(type, id, ownerEntity),
|
||||
_relativePosition(glm::vec3(0.0f)),
|
||||
_relativeRotation(glm::quat()),
|
||||
_hand("right"),
|
||||
_mine(false)
|
||||
{
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::AvatarActionHold";
|
||||
#endif
|
||||
|
@ -28,6 +36,13 @@ AvatarActionHold::~AvatarActionHold() {
|
|||
}
|
||||
|
||||
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||
if (!_mine) {
|
||||
// if a local script isn't updating this, then we are just getting spring-action data over the wire.
|
||||
// let the super-class handle it.
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
if (!tryLockForRead()) {
|
||||
|
@ -51,6 +66,22 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
if (!tryLockForWrite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for NaNs
|
||||
if (position.x != position.x ||
|
||||
position.y != position.y ||
|
||||
position.z != position.z) {
|
||||
qDebug() << "AvatarActionHold::updateActionWorker -- target position includes NaN";
|
||||
return;
|
||||
}
|
||||
if (rotation.x != rotation.x ||
|
||||
rotation.y != rotation.y ||
|
||||
rotation.z != rotation.z ||
|
||||
rotation.w != rotation.w) {
|
||||
qDebug() << "AvatarActionHold::updateActionWorker -- target rotation includes NaN";
|
||||
return;
|
||||
}
|
||||
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
unlock();
|
||||
|
@ -76,22 +107,22 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
lockForWrite();
|
||||
if (rPOk) {
|
||||
_relativePosition = relativePosition;
|
||||
} else if (!_parametersSet) {
|
||||
} else {
|
||||
_relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (rROk) {
|
||||
_relativeRotation = relativeRotation;
|
||||
} else if (!_parametersSet) {
|
||||
} else {
|
||||
_relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (tSOk) {
|
||||
_linearTimeScale = timeScale;
|
||||
_angularTimeScale = timeScale;
|
||||
} else if (!_parametersSet) {
|
||||
_linearTimeScale = 0.2;
|
||||
_angularTimeScale = 0.2;
|
||||
} else {
|
||||
_linearTimeScale = 0.2f;
|
||||
_angularTimeScale = 0.2f;
|
||||
}
|
||||
|
||||
if (hOk) {
|
||||
|
@ -104,14 +135,39 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
qDebug() << "hold action -- invalid hand argument:" << hand;
|
||||
_hand = "right";
|
||||
}
|
||||
} else if (!_parametersSet) {
|
||||
} else {
|
||||
_hand = "right";
|
||||
}
|
||||
|
||||
_parametersSet = true;
|
||||
_mine = true;
|
||||
_positionalTargetSet = true;
|
||||
_rotationalTargetSet = true;
|
||||
_active = true;
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap AvatarActionHold::getArguments() {
|
||||
QVariantMap arguments;
|
||||
lockForRead();
|
||||
if (_mine) {
|
||||
arguments["relativePosition"] = glmToQMap(_relativePosition);
|
||||
arguments["relativeRotation"] = glmToQMap(_relativeRotation);
|
||||
arguments["timeScale"] = _linearTimeScale;
|
||||
arguments["hand"] = _hand;
|
||||
} else {
|
||||
unlock();
|
||||
return ObjectActionSpring::getArguments();
|
||||
}
|
||||
unlock();
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
|
||||
if (_mine) {
|
||||
return;
|
||||
}
|
||||
ObjectActionSpring::deserialize(serializedArguments);
|
||||
}
|
||||
|
|
|
@ -19,17 +19,25 @@
|
|||
|
||||
class AvatarActionHold : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
|
||||
AvatarActionHold(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionHold();
|
||||
|
||||
virtual EntityActionType getType() { return ACTION_TYPE_HOLD; }
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual QVariantMap getArguments();
|
||||
|
||||
virtual void updateActionWorker(float deltaTimeStep);
|
||||
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
|
||||
private:
|
||||
static const uint16_t holdVersion;
|
||||
|
||||
glm::vec3 _relativePosition;
|
||||
glm::quat _relativeRotation;
|
||||
QString _hand;
|
||||
bool _parametersSet = false;
|
||||
bool _mine = false;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionHold_h
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
|
||||
#include <QScriptEngine>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
@ -74,19 +74,19 @@ void AvatarManager::init() {
|
|||
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
|
||||
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
||||
void AvatarManager::updateMyAvatar(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");
|
||||
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 dt = now - _lastSendAvatarDataTime;
|
||||
|
||||
|
||||
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS) {
|
||||
// send head/hand data to the avatar mixer and voxel server
|
||||
PerformanceTimer perfTimer("send");
|
||||
|
@ -103,12 +103,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
|
||||
|
||||
// simulate avatars
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value());
|
||||
|
||||
|
||||
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||
// DO NOT update or fade out uninitialized Avatars
|
||||
|
@ -122,17 +122,17 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
++avatarIterator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// simulate avatar fades
|
||||
simulateAvatarFades(deltaTime);
|
||||
}
|
||||
|
||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
|
||||
|
||||
|
||||
const float SHRINK_RATE = 0.9f;
|
||||
const float MIN_FADE_SCALE = 0.001f;
|
||||
|
||||
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
while (fadingIterator != _avatarFades.end()) {
|
||||
|
@ -153,12 +153,12 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
|||
return AvatarSharedPointer(std::make_shared<Avatar>());
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
auto avatar = std::dynamic_pointer_cast<Avatar>(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer));
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
avatar->addToScene(avatar, scene, pendingChanges);
|
||||
avatar->addToScene(avatar, scene, pendingChanges);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
return avatar;
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.find(sessionUUID);
|
||||
if (avatarIterator != _avatarHash.end()) {
|
||||
|
@ -257,6 +257,37 @@ void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) {
|
|||
|
||||
void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
|
||||
// TODO: expose avatar collision events to JS
|
||||
for (Collision collision : collisionEvents) {
|
||||
// TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR,
|
||||
// and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one
|
||||
// if the ids will be null), and the behavior for other avatars is not specified. This has to be fleshed
|
||||
// out as soon as we use the new motionstates.
|
||||
if (collision.idA.isNull() || collision.idB.isNull()) {
|
||||
MyAvatar* myAvatar = getMyAvatar();
|
||||
const QString& collisionSoundURL = myAvatar->getCollisionSoundURL();
|
||||
if (!collisionSoundURL.isEmpty()) {
|
||||
const float velocityChange = glm::length(collision.velocityChange);
|
||||
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01;
|
||||
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
|
||||
|
||||
if (!isSound) {
|
||||
// TODO: When the new motion states are used, we'll probably break from the whole loop as soon as we hit our own avatar
|
||||
// (regardless of isSound), because other users should inject for their own avatars.
|
||||
continue;
|
||||
}
|
||||
// Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for.
|
||||
const float energy = velocityChange * velocityChange;
|
||||
const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f;
|
||||
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
|
||||
|
||||
// For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object,
|
||||
// but most avatars are roughly the same size, so let's not be so fancy yet.
|
||||
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
||||
|
||||
AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
|
||||
|
|
|
@ -148,10 +148,6 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarMotionState::bump() {
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() {
|
||||
return COLLISION_GROUP_OTHER_AVATAR;
|
||||
|
|
|
@ -55,7 +55,6 @@ public:
|
|||
virtual const QUuid& getObjectID() const;
|
||||
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual void bump();
|
||||
|
||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||
|
||||
|
|
|
@ -103,7 +103,8 @@ void Hand::resolvePenetrations() {
|
|||
}
|
||||
|
||||
void Hand::render(RenderArgs* renderArgs, bool isMine) {
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
// draw a green sphere at hand joint location, which is actually near the wrist)
|
||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
||||
|
@ -112,31 +113,25 @@ void Hand::render(RenderArgs* renderArgs, bool isMine) {
|
|||
continue;
|
||||
}
|
||||
glm::vec3 position = palm.getPosition();
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glPopMatrix();
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(position);
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
|
||||
renderHandTargets(isMine);
|
||||
renderHandTargets(renderArgs, isMine);
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_RESCALE_NORMAL);
|
||||
}
|
||||
|
||||
void Hand::renderHandTargets(bool isMine) {
|
||||
glPushMatrix();
|
||||
}
|
||||
|
||||
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
const float avatarScale = DependencyManager::get<AvatarManager>()->getMyAvatar()->getScale();
|
||||
|
||||
const float alpha = 1.0f;
|
||||
const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
if (isMine && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
|
@ -145,12 +140,12 @@ void Hand::renderHandTargets(bool isMine) {
|
|||
continue;
|
||||
}
|
||||
glm::vec3 targetPosition = palm.getTipPosition();
|
||||
glPushMatrix();
|
||||
glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z);
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(targetPosition);
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
const float collisionRadius = 0.05f;
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false);
|
||||
glPopMatrix();
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,22 +160,19 @@ void Hand::renderHandTargets(bool isMine) {
|
|||
if (palm.isActive()) {
|
||||
glm::vec3 tip = palm.getTipPosition();
|
||||
glm::vec3 root = palm.getPosition();
|
||||
|
||||
Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(glm::vec3());
|
||||
batch.setModelTransform(transform);
|
||||
Avatar::renderJointConnectingCone(batch, root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
|
||||
// Render sphere at palm/finger root
|
||||
glm::vec3 offsetFromPalm = root + palm.getNormal() * PALM_DISK_THICKNESS;
|
||||
Avatar::renderJointConnectingCone(root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
glPushMatrix();
|
||||
glTranslatef(root.x, root.y, root.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
glPopMatrix();
|
||||
Avatar::renderJointConnectingCone(batch, root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
transform = Transform();
|
||||
transform.setTranslation(root);
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha));
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ private:
|
|||
|
||||
Avatar* _owningAvatar;
|
||||
|
||||
void renderHandTargets(bool isMine);
|
||||
void renderHandTargets(RenderArgs* renderArgs, bool isMine);
|
||||
};
|
||||
|
||||
#endif // hifi_Hand_h
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
//
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GlowEffect.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -293,10 +295,8 @@ void Head::relaxLean(float deltaTime) {
|
|||
}
|
||||
|
||||
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
|
||||
if (postLighting) {
|
||||
if (_renderLookatVectors) {
|
||||
if (_renderLookatVectors) {
|
||||
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,17 +380,19 @@ void Head::addLeanDeltas(float sideways, float forward) {
|
|||
}
|
||||
|
||||
void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
||||
auto& batch = *renderArgs->_batch;
|
||||
auto transform = Transform{};
|
||||
batch.setModelTransform(transform);
|
||||
batch._glLineWidth(2.0f);
|
||||
|
||||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
DependencyManager::get<GlowEffect>()->begin(renderArgs);
|
||||
|
||||
glLineWidth(2.0);
|
||||
|
||||
glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
geometryCache->renderLine(leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID);
|
||||
geometryCache->renderLine(rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
|
||||
|
||||
DependencyManager::get<GlowEffect>()->end(renderArgs);
|
||||
geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID);
|
||||
geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,9 +70,10 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
|||
const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
|
||||
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
|
||||
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
|
||||
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
|
||||
|
||||
const float MyAvatar::ZOOM_MIN = 0.5f;
|
||||
const float MyAvatar::ZOOM_MAX = 10.0f;
|
||||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
|
||||
MyAvatar::MyAvatar() :
|
||||
|
@ -90,6 +91,7 @@ MyAvatar::MyAvatar() :
|
|||
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
|
||||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_collisionSoundURL(""),
|
||||
_characterController(this),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
|
@ -664,6 +666,7 @@ void MyAvatar::saveData() {
|
|||
settings.endArray();
|
||||
|
||||
settings.setValue("displayName", _displayName);
|
||||
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
@ -789,6 +792,7 @@ void MyAvatar::loadData() {
|
|||
settings.endArray();
|
||||
|
||||
setDisplayName(settings.value("displayName").toString());
|
||||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
@ -1183,6 +1187,13 @@ void MyAvatar::clearScriptableSettings() {
|
|||
_scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE;
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionSoundURL(const QString& url) {
|
||||
_collisionSoundURL = url;
|
||||
if (!url.isEmpty() && (url != _collisionSoundURL)) {
|
||||
emit newCollisionSoundURL(QUrl(url));
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,
|
||||
const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
@ -1212,9 +1223,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
|
|||
if (shouldRenderHead(renderArgs)) {
|
||||
getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
|
||||
}
|
||||
if (postLighting) {
|
||||
getHand()->render(renderArgs, true);
|
||||
}
|
||||
getHand()->render(renderArgs, true);
|
||||
}
|
||||
|
||||
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
|
||||
|
@ -1394,7 +1403,8 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
}
|
||||
}
|
||||
|
||||
_boomLength += _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
|
||||
float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
|
||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
|
||||
return newLocalVelocity;
|
||||
|
@ -1587,7 +1597,7 @@ void MyAvatar::updateMotionBehavior() {
|
|||
}
|
||||
|
||||
//Renders sixense laser pointers for UI selection with controllers
|
||||
void MyAvatar::renderLaserPointers() {
|
||||
void MyAvatar::renderLaserPointers(gpu::Batch& batch) {
|
||||
const float PALM_TIP_ROD_RADIUS = 0.002f;
|
||||
|
||||
//If the Oculus is enabled, we will draw a blue cursor ray
|
||||
|
@ -1600,8 +1610,10 @@ void MyAvatar::renderLaserPointers() {
|
|||
|
||||
//Scale the root vector with the avatar scale
|
||||
scaleVectorRelativeToPosition(root);
|
||||
|
||||
Avatar::renderJointConnectingCone(root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1));
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(glm::vec3());
|
||||
batch.setModelTransform(transform);
|
||||
Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity)
|
||||
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)
|
||||
Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame)
|
||||
Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL)
|
||||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
public:
|
||||
|
@ -150,6 +151,9 @@ public:
|
|||
void setScriptedMotorTimescale(float timescale);
|
||||
void setScriptedMotorFrame(QString frame);
|
||||
|
||||
const QString& getCollisionSoundURL() {return _collisionSoundURL; }
|
||||
void setCollisionSoundURL(const QString& url);
|
||||
|
||||
void clearScriptableSettings();
|
||||
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
|
@ -157,7 +161,7 @@ public:
|
|||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
||||
/// Renders a laser pointer for UI picking
|
||||
void renderLaserPointers();
|
||||
void renderLaserPointers(gpu::Batch& batch);
|
||||
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
|
||||
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
|
@ -204,6 +208,7 @@ public slots:
|
|||
|
||||
signals:
|
||||
void transformChanged();
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -233,6 +238,7 @@ private:
|
|||
float _scriptedMotorTimescale; // timescale for avatar to achieve its target velocity
|
||||
int _scriptedMotorFrame;
|
||||
quint32 _motionBehaviors;
|
||||
QString _collisionSoundURL;
|
||||
|
||||
DynamicCharacterController _characterController;
|
||||
|
||||
|
|
|
@ -776,24 +776,24 @@ void SkeletonModel::resetShapePositionsToDefaultPose() {
|
|||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderBoundingCollisionShapes(float alpha) {
|
||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, 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);
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(endPoint);
|
||||
batch.setModelTransform(transform);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->renderSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule startpoint
|
||||
glm::vec3 startPoint;
|
||||
|
@ -805,9 +805,7 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) {
|
|||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.0f);
|
||||
Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha));
|
||||
|
||||
glPopMatrix();
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha));
|
||||
}
|
||||
|
||||
bool SkeletonModel::hasSkeleton() {
|
||||
|
|
|
@ -101,7 +101,7 @@ public:
|
|||
const glm::vec3& getStandingOffset() const { return _standingOffset; }
|
||||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
void renderBoundingCollisionShapes(float alpha);
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||
const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; }
|
||||
|
|
|
@ -225,14 +225,14 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
|
||||
|
||||
// Arrow keys mapping
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
|
||||
|
@ -270,8 +270,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
|
||||
|
||||
// Wheel move
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
|
||||
|
||||
|
|
|
@ -249,7 +249,6 @@ static GlWindow* _outputWindow{ nullptr };
|
|||
static bool _isConnected = false;
|
||||
static ovrHmd _ovrHmd;
|
||||
static ovrFovPort _eyeFov[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffset[ovrEye_Count];
|
||||
static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
|
||||
static ovrSizei _renderTargetSize;
|
||||
static glm::mat4 _eyeProjection[ovrEye_Count];
|
||||
|
@ -281,6 +280,7 @@ static ovrPosef _eyeRenderPoses[ovrEye_Count];
|
|||
static ovrRecti _eyeViewports[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffsets[ovrEye_Count];
|
||||
|
||||
|
||||
glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
|
||||
glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
|
||||
|
||||
|
@ -910,4 +910,4 @@ mat4 OculusManager::getEyePose(int eye) {
|
|||
mat4 OculusManager::getHeadPose() {
|
||||
ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
|
||||
return toGlm(ts.HeadPose.ThePose);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
|
|||
glScissor(portalX, portalY, portalW, portalH);
|
||||
|
||||
glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ);
|
||||
float fov = atan(1.0f / projection[1][1]);
|
||||
projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0));
|
||||
eyeCamera.setProjection(projection);
|
||||
|
||||
|
|
|
@ -17,25 +17,21 @@
|
|||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <CursorManager.h>
|
||||
#include <Tooltip.h>
|
||||
|
||||
#include "CursorManager.h"
|
||||
#include "Tooltip.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
// Used to animate the magnification windows
|
||||
static const float MAG_SPEED = 0.08f;
|
||||
|
||||
static const quint64 MSECS_TO_USECS = 1000ULL;
|
||||
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||
|
||||
static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
|
||||
static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
|
||||
static const float reticleSize = TWO_PI / 100.0f;
|
||||
|
||||
static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
|
||||
static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
|
||||
|
||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||
static const float MOUSE_PITCH_RANGE = 1.0f * PI;
|
||||
static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
|
||||
|
@ -117,7 +113,7 @@ ApplicationCompositor::ApplicationCompositor() {
|
|||
memset(_magSizeMult, 0, sizeof(_magSizeMult));
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
|
||||
_reticleQuad = geometryCache->allocateID();
|
||||
_magnifierQuad = geometryCache->allocateID();
|
||||
_magnifierBorder = geometryCache->allocateID();
|
||||
|
@ -128,11 +124,25 @@ ApplicationCompositor::ApplicationCompositor() {
|
|||
_hoverItemId = entityItemID;
|
||||
_hoverItemEnterUsecs = usecTimestampNow();
|
||||
auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);
|
||||
_hoverItemHref = properties.getHref();
|
||||
|
||||
// check the format of this href string before we parse it
|
||||
QString hrefString = properties.getHref();
|
||||
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
if (!_hoverItemHref.isEmpty()) {
|
||||
if (!hrefString.isEmpty()) {
|
||||
if (!hrefString.startsWith("hifi:")) {
|
||||
hrefString.prepend("hifi://");
|
||||
}
|
||||
|
||||
// parse out a QUrl from the hrefString
|
||||
QUrl href = QUrl(hrefString);
|
||||
|
||||
_hoverItemTitle = href.host();
|
||||
_hoverItemDescription = properties.getDescription();
|
||||
cursor->setIcon(Cursor::Icon::LINK);
|
||||
} else {
|
||||
_hoverItemTitle.clear();
|
||||
_hoverItemDescription.clear();
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +151,10 @@ ApplicationCompositor::ApplicationCompositor() {
|
|||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_hoverItemId == entityItemID) {
|
||||
_hoverItemId = _noItemId;
|
||||
_hoverItemHref.clear();
|
||||
|
||||
_hoverItemTitle.clear();
|
||||
_hoverItemDescription.clear();
|
||||
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
if (!_tooltipId.isEmpty()) {
|
||||
|
@ -216,7 +229,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
@ -314,7 +326,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
batch.setModelTransform(reticleXfm);
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
|
||||
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
||||
|
@ -324,7 +336,7 @@ void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& or
|
|||
const glm::vec2 projection = screenToSpherical(cursorPos);
|
||||
// The overlay space orientation of the mouse coordinates
|
||||
const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
|
||||
// FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.
|
||||
// FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.
|
||||
// Now we need to account for the actual camera position relative to the overlay
|
||||
glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT);
|
||||
|
||||
|
@ -368,8 +380,8 @@ QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const {
|
|||
ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;
|
||||
}
|
||||
|
||||
rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x);
|
||||
rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y);
|
||||
rv.setX(((ndcSpacePos.x + 1.0f) / 2.0f) * canvasSize.x);
|
||||
rv.setY((1.0f - ((ndcSpacePos.y + 1.0f) / 2.0f)) * canvasSize.y);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
@ -377,7 +389,7 @@ QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const {
|
|||
//Finds the collision point of a world space ray
|
||||
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
|
||||
glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());
|
||||
|
||||
glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());
|
||||
|
@ -409,7 +421,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
|
|||
renderControllerPointers(batch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
@ -487,7 +499,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
|
|||
|
||||
// Get the angles, scaled between (-0.5,0.5)
|
||||
float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
|
||||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
|
||||
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult();
|
||||
|
@ -517,7 +529,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
|
|||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,10 +539,10 @@ void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2&
|
|||
return;
|
||||
}
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
|
||||
|
||||
const int widgetWidth = canvasSize.x;
|
||||
const int widgetHeight = canvasSize.y;
|
||||
|
||||
|
||||
const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f;
|
||||
const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f;
|
||||
// Magnification Texture Coordinates
|
||||
|
@ -538,7 +550,7 @@ void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2&
|
|||
const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth;
|
||||
const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight;
|
||||
const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight;
|
||||
|
||||
|
||||
const float newHalfWidth = halfWidth * MAGNIFY_MULT;
|
||||
const float newHalfHeight = halfHeight * MAGNIFY_MULT;
|
||||
//Get yaw / pitch value for the corners
|
||||
|
@ -546,7 +558,7 @@ void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2&
|
|||
magPos.y - newHalfHeight));
|
||||
const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth,
|
||||
magPos.y + newHalfHeight));
|
||||
|
||||
|
||||
const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y);
|
||||
const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y);
|
||||
const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y);
|
||||
|
@ -569,7 +581,7 @@ void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2&
|
|||
_previousMagnifierTopLeft = topLeft;
|
||||
_previousMagnifierTopRight = topRight;
|
||||
}
|
||||
|
||||
|
||||
glPushMatrix(); {
|
||||
if (showBorder) {
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
@ -581,12 +593,12 @@ void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2&
|
|||
glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha };
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft,
|
||||
glm::vec2(magnifyULeft, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVTop),
|
||||
glm::vec2(magnifyULeft, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVTop),
|
||||
glm::vec2(magnifyULeft, magnifyVTop),
|
||||
magnifierColor, _magnifierQuad);
|
||||
|
||||
|
||||
} glPopMatrix();
|
||||
}
|
||||
|
||||
|
@ -611,8 +623,8 @@ void ApplicationCompositor::buildHemiVertices(
|
|||
}
|
||||
|
||||
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
|
||||
|
||||
vec3 pos;
|
||||
|
||||
vec3 pos;
|
||||
vec2 uv;
|
||||
// Compute vertices positions and texture UV coordinate
|
||||
// Create and write to buffer
|
||||
|
@ -631,13 +643,13 @@ void ApplicationCompositor::buildHemiVertices(
|
|||
_hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute number of indices needed
|
||||
static const int VERTEX_PER_TRANGLE = 3;
|
||||
static const int TRIANGLE_PER_RECTANGLE = 2;
|
||||
int numberOfRectangles = (slices - 1) * (stacks - 1);
|
||||
_hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
|
||||
|
||||
|
||||
// Compute indices order
|
||||
std::vector<GLushort> indices;
|
||||
for (int i = 0; i < stacks - 1; i++) {
|
||||
|
@ -694,7 +706,7 @@ glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction
|
|||
}
|
||||
// Compute pitch
|
||||
result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -710,22 +722,22 @@ glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) {
|
|||
result.y = (screenPos.y / screenSize.y - 0.5f);
|
||||
result.x *= MOUSE_YAW_RANGE;
|
||||
result.y *= MOUSE_PITCH_RANGE;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0;
|
||||
result.x *= -1.0f;
|
||||
result /= MOUSE_RANGE;
|
||||
result += 0.5f;
|
||||
result *= qApp->getCanvasSize();
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0;
|
||||
result.x *= -1.0f;
|
||||
result /= _textureFov;
|
||||
result.x /= _textureAspectRatio;
|
||||
result += 0.5f;
|
||||
|
@ -737,7 +749,7 @@ glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos
|
|||
glm::vec2 result = overlayPos;
|
||||
result /= qApp->getCanvasSize();
|
||||
result -= 0.5f;
|
||||
result *= _textureFov;
|
||||
result *= _textureFov;
|
||||
result.x *= _textureAspectRatio;
|
||||
result.x *= -1.0f;
|
||||
return result;
|
||||
|
@ -754,10 +766,10 @@ glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) co
|
|||
void ApplicationCompositor::updateTooltips() {
|
||||
if (_hoverItemId != _noItemId) {
|
||||
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||
if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
|
||||
if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemTitle.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
|
||||
// TODO Enable and position the tooltip
|
||||
_hoverItemEnterUsecs = UINT64_MAX;
|
||||
_tooltipId = Tooltip::showTip("URL: " + _hoverItemHref);
|
||||
_tooltipId = Tooltip::showTip(_hoverItemTitle, _hoverItemDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
|
||||
QPoint getPalmClickLocation(const PalmData *palm) const;
|
||||
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
||||
|
||||
|
||||
bool hasMagnifier() const { return _magnifier; }
|
||||
void toggleMagnifier() { _magnifier = !_magnifier; }
|
||||
|
||||
|
@ -68,7 +68,7 @@ public:
|
|||
static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
|
||||
static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
|
||||
static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
|
||||
|
||||
|
||||
private:
|
||||
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||
|
@ -83,9 +83,10 @@ private:
|
|||
|
||||
// Support for hovering and tooltips
|
||||
static EntityItemID _noItemId;
|
||||
EntityItemID _hoverItemId{ _noItemId };
|
||||
QString _hoverItemHref;
|
||||
quint64 _hoverItemEnterUsecs{ 0 };
|
||||
EntityItemID _hoverItemId { _noItemId };
|
||||
QString _hoverItemTitle;
|
||||
QString _hoverItemDescription;
|
||||
quint64 _hoverItemEnterUsecs { 0 };
|
||||
|
||||
float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
|
||||
float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) };
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
#include "ui/Stats.h"
|
||||
#include "ui/AvatarInputs.h"
|
||||
|
||||
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
|
||||
const int AUDIO_METER_GAP = 5;
|
||||
const int MUTE_ICON_PADDING = 10;
|
||||
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
|
||||
const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
|
||||
static const float ORTHO_NEAR_CLIP = -10000;
|
||||
|
|
|
@ -67,7 +67,6 @@ void AvatarInputs::update() {
|
|||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
const float CLIPPING_INDICATOR_TIME = 1.0f;
|
||||
const float AUDIO_METER_AVERAGING = 0.5;
|
||||
const float LOG2 = log(2.0f);
|
||||
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
|
||||
|
@ -85,7 +84,7 @@ void AvatarInputs::update() {
|
|||
} else {
|
||||
audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
|
||||
}
|
||||
if (audioLevel > 1.0) {
|
||||
if (audioLevel > 1.0f) {
|
||||
audioLevel = 1.0;
|
||||
}
|
||||
AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
@ -20,7 +20,7 @@
|
|||
#include <QTextBlock>
|
||||
#include <QtGui>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,11 +21,14 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
#include "../octree/OctreePacketProcessor.h"
|
||||
#include "ui/OctreeStatsDialog.h"
|
||||
|
||||
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
|
||||
_model(model) {
|
||||
_model(model),
|
||||
_averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
{
|
||||
|
||||
_statCount = 0;
|
||||
_octreeServerLabelsCount = 0;
|
||||
|
@ -50,6 +53,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
|
|||
_localElements = AddStatItem("Local Elements");
|
||||
_localElementsMemory = AddStatItem("Elements Memory");
|
||||
_sendingMode = AddStatItem("Sending Mode");
|
||||
|
||||
_processedPackets = AddStatItem("Entity Packets");
|
||||
_processedPacketsElements = AddStatItem("Processed Packets Elements");
|
||||
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
|
||||
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
|
||||
|
||||
_entityUpdateTime = AddStatItem("Entity Update Time");
|
||||
_entityUpdates = AddStatItem("Entity Updates");
|
||||
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
|
@ -119,6 +130,36 @@ OctreeStatsDialog::~OctreeStatsDialog() {
|
|||
|
||||
void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
|
||||
|
||||
// Processed Entities Related stats
|
||||
auto entities = Application::getInstance()->getEntities();
|
||||
auto entitiesTree = entities->getTree();
|
||||
|
||||
// Do this ever paint event... even if we don't update
|
||||
auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits();
|
||||
|
||||
// track our updated per second
|
||||
const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND;
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceLastWindow = now - _lastWindowAt;
|
||||
auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits;
|
||||
float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND;
|
||||
float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds;
|
||||
if (sinceLastWindow > SAMPLING_WINDOW) {
|
||||
_averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond);
|
||||
_lastWindowAt = now;
|
||||
_lastKnownTrackedEdits = totalTrackedEdits;
|
||||
}
|
||||
|
||||
// Only refresh our stats every once in a while, unless asked for realtime
|
||||
quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND;
|
||||
quint64 sinceLastRefresh = now - _lastRefresh;
|
||||
if (sinceLastRefresh < REFRESH_AFTER) {
|
||||
return QDialog::paintEvent(event);
|
||||
}
|
||||
const int FLOATING_POINT_PRECISION = 3;
|
||||
|
||||
_lastRefresh = now;
|
||||
|
||||
// Update labels
|
||||
|
||||
QLabel* label;
|
||||
|
@ -203,9 +244,110 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
|
|||
"Leaves: " << qPrintable(serversLeavesString) << "";
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Processed Packets Elements
|
||||
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
|
||||
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
|
||||
|
||||
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
|
||||
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
|
||||
|
||||
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
|
||||
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
|
||||
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
|
||||
|
||||
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION);
|
||||
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
|
||||
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
|
||||
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
|
||||
|
||||
label = _labels[_processedPackets];
|
||||
const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor();
|
||||
|
||||
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
|
||||
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
|
||||
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
|
||||
|
||||
QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
|
||||
"Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
|
||||
"Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsElements];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(averageElementsPerPacketString) << " per packet / " <<
|
||||
"" << qPrintable(averageElementsPerSecondString) << " per second";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsEntities];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(averageEntitiesPerPacketString) << " per packet / " <<
|
||||
"" << qPrintable(averageEntitiesPerSecondString) << " per second";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsTiming];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " <<
|
||||
"Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " <<
|
||||
"Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Entity Edits update time
|
||||
label = _labels[_entityUpdateTime];
|
||||
auto averageEditDelta = entitiesTree->getAverageEditDeltas();
|
||||
auto maxEditDelta = entitiesTree->getMaxEditDelta();
|
||||
|
||||
QString averageEditDeltaString = locale.toString((uint)averageEditDelta);
|
||||
QString maxEditDeltaString = locale.toString((uint)maxEditDelta);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " <<
|
||||
"Max: " << qPrintable(maxEditDeltaString) << " (usecs)";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Entity Edits
|
||||
label = _labels[_entityUpdates];
|
||||
auto bytesPerEdit = entitiesTree->getAverageEditBytes();
|
||||
|
||||
auto updatesPerSecond = _averageUpdatesPerSecond.getAverage();
|
||||
if (updatesPerSecond < 1) {
|
||||
updatesPerSecond = 0; // we don't really care about small updates per second so suppress those
|
||||
}
|
||||
|
||||
QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits);
|
||||
QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
QString bytesPerEditString = locale.toString(bytesPerEdit);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(updatesPerSecondString) << " updates per second / " <<
|
||||
"" << qPrintable(totalTrackedEditsString) << " total updates / " <<
|
||||
"Average Size: " << qPrintable(bytesPerEditString) << " bytes ";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
showAllOctreeServers();
|
||||
|
||||
this->QDialog::paintEvent(event);
|
||||
QDialog::paintEvent(event);
|
||||
}
|
||||
void OctreeStatsDialog::showAllOctreeServers() {
|
||||
int serverCount = 0;
|
||||
|
|
|
@ -63,6 +63,21 @@ private:
|
|||
int _serverElements;
|
||||
int _localElements;
|
||||
int _localElementsMemory;
|
||||
|
||||
int _entityUpdateTime;
|
||||
int _entityUpdates;
|
||||
int _processedPackets;
|
||||
int _processedPacketsElements;
|
||||
int _processedPacketsEntities;
|
||||
int _processedPacketsTiming;
|
||||
|
||||
const int SAMPLES_PER_SECOND = 10;
|
||||
SimpleMovingAverage _averageUpdatesPerSecond;
|
||||
quint64 _lastWindowAt = 0;
|
||||
quint64 _lastKnownTrackedEdits = 0;
|
||||
|
||||
quint64 _lastRefresh = 0;
|
||||
|
||||
int _octreeServerLables[MAX_VOXEL_SERVERS];
|
||||
int _octreeServerLabelsCount;
|
||||
details _extraServerDetails[MAX_VOXEL_SERVERS];
|
||||
|
|
|
@ -127,6 +127,8 @@ void PreferencesDialog::loadPreferences() {
|
|||
_displayNameString = myAvatar->getDisplayName();
|
||||
ui.displayNameEdit->setText(_displayNameString);
|
||||
|
||||
ui.collisionSoundURLEdit->setText(myAvatar->getCollisionSoundURL());
|
||||
|
||||
ui.sendDataCheckBox->setChecked(!menuInstance->isOptionChecked(MenuOption::DisableActivityLogger));
|
||||
|
||||
ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get());
|
||||
|
@ -204,6 +206,8 @@ void PreferencesDialog::savePreferences() {
|
|||
myAvatar->sendIdentityPacket();
|
||||
}
|
||||
|
||||
myAvatar->setCollisionSoundURL(ui.collisionSoundURLEdit->text());
|
||||
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger)
|
||||
!= ui.sendDataCheckBox->isChecked()) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::DisableActivityLogger);
|
||||
|
@ -221,8 +225,6 @@ void PreferencesDialog::savePreferences() {
|
|||
myAvatar->setLeanScale(ui.leanScaleSpin->value());
|
||||
myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value());
|
||||
|
||||
Application::getInstance()->resizeGL();
|
||||
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value());
|
||||
|
||||
qApp->setFieldOfView(ui.fieldOfViewSpin->value());
|
||||
|
|
|
@ -32,7 +32,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
|||
QWidget(parent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint |
|
||||
Qt::WindowCloseButtonHint),
|
||||
ui(new Ui::RunningScriptsWidget),
|
||||
_signalMapper(this),
|
||||
_reloadSignalMapper(this),
|
||||
_stopSignalMapper(this),
|
||||
_scriptsModelFilter(this),
|
||||
_scriptsModel(this) {
|
||||
ui->setupUi(this);
|
||||
|
@ -64,7 +65,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
|||
Application::getInstance(), &Application::loadDialog);
|
||||
connect(ui->loadScriptFromURLButton, &QPushButton::clicked,
|
||||
Application::getInstance(), &Application::loadScriptURLDialog);
|
||||
connect(&_signalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&)));
|
||||
connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadOneScript(const QString&)));
|
||||
connect(&_stopSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&)));
|
||||
|
||||
UIUtil::scaleWidgetFontSizes(this);
|
||||
}
|
||||
|
@ -115,6 +117,17 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
name->setText(name->text() + "(" + QString::number(hash.find(list.at(i)).value()) + ")");
|
||||
}
|
||||
++hash[list.at(i)];
|
||||
|
||||
QPushButton* reloadButton = new QPushButton(row);
|
||||
reloadButton->setFlat(true);
|
||||
reloadButton->setIcon(
|
||||
QIcon(QPixmap(PathUtils::resourcesPath() + "images/reload-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT)));
|
||||
reloadButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
|
||||
reloadButton->setStyleSheet("border: 0;");
|
||||
reloadButton->setCursor(Qt::PointingHandCursor);
|
||||
connect(reloadButton, SIGNAL(clicked()), &_reloadSignalMapper, SLOT(map()));
|
||||
_reloadSignalMapper.setMapping(reloadButton, url.toString());
|
||||
|
||||
QPushButton* closeButton = new QPushButton(row);
|
||||
closeButton->setFlat(true);
|
||||
closeButton->setIcon(
|
||||
|
@ -122,9 +135,8 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
closeButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
|
||||
closeButton->setStyleSheet("border: 0;");
|
||||
closeButton->setCursor(Qt::PointingHandCursor);
|
||||
|
||||
connect(closeButton, SIGNAL(clicked()), &_signalMapper, SLOT(map()));
|
||||
_signalMapper.setMapping(closeButton, url.toString());
|
||||
connect(closeButton, SIGNAL(clicked()), &_stopSignalMapper, SLOT(map()));
|
||||
_stopSignalMapper.setMapping(closeButton, url.toString());
|
||||
|
||||
row->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
|
||||
|
||||
|
@ -132,6 +144,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
row->layout()->setSpacing(0);
|
||||
|
||||
row->layout()->addWidget(name);
|
||||
row->layout()->addWidget(reloadButton);
|
||||
row->layout()->addWidget(closeButton);
|
||||
|
||||
row->setToolTip(url.toString());
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
const ScriptsModel* getScriptsModel() { return &_scriptsModel; }
|
||||
|
||||
signals:
|
||||
void stopScriptName(const QString& name);
|
||||
void stopScriptName(const QString& name, bool restart = false);
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
|
@ -59,7 +59,8 @@ private slots:
|
|||
|
||||
private:
|
||||
Ui::RunningScriptsWidget* ui;
|
||||
QSignalMapper _signalMapper;
|
||||
QSignalMapper _reloadSignalMapper;
|
||||
QSignalMapper _stopSignalMapper;
|
||||
ScriptsModelFilter _scriptsModelFilter;
|
||||
ScriptsModel _scriptsModel;
|
||||
ScriptsTableWidget* _recentlyLoadedScriptsTable;
|
||||
|
|
|
@ -122,8 +122,8 @@ void Stats::updateStats() {
|
|||
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||
STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond());
|
||||
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
|
||||
// Second column: ping
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
|
@ -136,7 +136,6 @@ void Stats::updateStats() {
|
|||
unsigned long totalPingOctree = 0;
|
||||
int octreeServerCount = 0;
|
||||
int pingOctreeMax = 0;
|
||||
int pingVoxel;
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// TODO: this should also support entities
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
|
@ -147,19 +146,6 @@ void Stats::updateStats() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (octreeServerCount) {
|
||||
pingVoxel = totalPingOctree / octreeServerCount;
|
||||
}
|
||||
|
||||
//STAT_UPDATE(entitiesPing, pingVoxel);
|
||||
//if (_expanded) {
|
||||
// QString voxelMaxPing;
|
||||
// if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
|
||||
// voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
|
||||
// } else {
|
||||
// voxelMaxPing = QString("Voxel max ping: --");
|
||||
// }
|
||||
} else {
|
||||
// -2 causes the QML to hide the ping column
|
||||
STAT_UPDATE(audioPing, -2);
|
||||
|
@ -280,15 +266,15 @@ void Stats::updateStats() {
|
|||
}
|
||||
|
||||
// Server Octree Elements
|
||||
STAT_UPDATE(serverElements, totalNodes);
|
||||
STAT_UPDATE(localElements, OctreeElement::getNodeCount());
|
||||
STAT_UPDATE(serverElements, (int)totalNodes);
|
||||
STAT_UPDATE(localElements, (int)OctreeElement::getNodeCount());
|
||||
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(serverInternal, totalInternal);
|
||||
STAT_UPDATE(serverLeaves, totalLeaves);
|
||||
STAT_UPDATE(serverInternal, (int)totalInternal);
|
||||
STAT_UPDATE(serverLeaves, (int)totalLeaves);
|
||||
// Local Voxels
|
||||
STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount());
|
||||
STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount());
|
||||
STAT_UPDATE(localInternal, (int)OctreeElement::getInternalNodeCount());
|
||||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
}
|
||||
|
|
|
@ -245,8 +245,8 @@ void UserInputMapper::assignDefaulActionScales() {
|
|||
_actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit
|
||||
_actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit
|
||||
_actionScales[PITCH_UP] = 1.0f; // 1 degree per unit
|
||||
_actionScales[BOOM_IN] = 1.0f; // 1m per unit
|
||||
_actionScales[BOOM_OUT] = 1.0f; // 1m per unit
|
||||
_actionScales[BOOM_IN] = 0.5f; // .5m per unit
|
||||
_actionScales[BOOM_OUT] = 0.5f; // .5m per unit
|
||||
_actionStates[SHIFT] = 1.0f; // on
|
||||
_actionStates[ACTION1] = 1.0f; // default
|
||||
_actionStates[ACTION2] = 1.0f; // default
|
||||
|
|
|
@ -93,31 +93,6 @@ void BillboardOverlay::render(RenderArgs* args) {
|
|||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
|
||||
|
||||
batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
} else {
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _texture->getID());
|
||||
|
||||
glPushMatrix(); {
|
||||
glTranslatef(getPosition().x, getPosition().y, getPosition().z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(_dimensions.x, _dimensions.y, 1.0f);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
|
||||
|
||||
} glPopMatrix();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,75 +89,6 @@ void Grid3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
} else {
|
||||
if (!_gridProgram.isLinked()) {
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.link()) {
|
||||
qDebug() << "Failed to link: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Render code largely taken from MetavoxelEditor::render()
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
glLineWidth(1.5f);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
|
||||
_gridProgram.bind();
|
||||
|
||||
// Minor grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z);
|
||||
|
||||
float scale = MINOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
// Major grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glLineWidth(4.0f);
|
||||
spacing *= _majorGridEvery;
|
||||
glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z);
|
||||
|
||||
float scale = MAJOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
_gridProgram.release();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,25 +32,25 @@ void LocalModelsOverlay::update(float deltatime) {
|
|||
|
||||
void LocalModelsOverlay::render(RenderArgs* args) {
|
||||
if (_visible) {
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix(); {
|
||||
Application* app = Application::getInstance();
|
||||
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
|
||||
app->setViewMatrixTranslation(oldTranslation + getPosition());
|
||||
_entityTreeRenderer->render(args);
|
||||
Application::getInstance()->setViewMatrixTranslation(oldTranslation);
|
||||
} glPopMatrix();
|
||||
|
||||
|
||||
auto batch = args ->_batch;
|
||||
Application* app = Application::getInstance();
|
||||
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(oldTranslation + getPosition());
|
||||
batch->setViewTransform(transform);
|
||||
_entityTreeRenderer->render(args);
|
||||
transform.setTranslation(oldTranslation);
|
||||
batch->setViewTransform(transform);
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <Application.h>
|
||||
#include <render/Scene.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
#include "BillboardOverlay.h"
|
||||
#include "Circle3DOverlay.h"
|
||||
|
@ -96,6 +97,10 @@ void Overlays::cleanupOverlaysToDelete() {
|
|||
|
||||
void Overlays::renderHUD(RenderArgs* renderArgs) {
|
||||
QReadLocker lock(&_lock);
|
||||
gpu::Batch batch;
|
||||
renderArgs->_batch = &batch;
|
||||
|
||||
|
||||
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
|
||||
if (thisOverlay->is3D()) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -109,6 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
thisOverlay->render(renderArgs);
|
||||
}
|
||||
}
|
||||
gpu::GLBackend::renderBatch(batch, true);
|
||||
}
|
||||
|
||||
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
|
||||
|
|
|
@ -62,19 +62,19 @@ namespace render {
|
|||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
|
||||
glPushMatrix();
|
||||
auto batch = args->_batch;
|
||||
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::quat myAvatarRotation = avatar->getOrientation();
|
||||
glm::vec3 myAvatarPosition = avatar->getPosition();
|
||||
float angle = glm::degrees(glm::angle(myAvatarRotation));
|
||||
glm::vec3 axis = glm::axis(myAvatarRotation);
|
||||
float myAvatarScale = avatar->getScale();
|
||||
|
||||
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
|
||||
glRotatef(angle, axis.x, axis.y, axis.z);
|
||||
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(myAvatarPosition);
|
||||
transform.setRotation(glm::angleAxis(angle, axis));
|
||||
transform.setScale(myAvatarScale);
|
||||
batch->setModelTransform(transform);
|
||||
overlay->render(args);
|
||||
glPopMatrix();
|
||||
} else {
|
||||
overlay->render(args);
|
||||
}
|
||||
|
|
|
@ -87,70 +87,6 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
|
|||
geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
//glScalef(dimensions.x, dimensions.y, 1.0f);
|
||||
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line...
|
||||
if (getIsSolid()) {
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, rectangleColor);
|
||||
} else {
|
||||
if (getIsDashedLine()) {
|
||||
|
||||
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
|
||||
geometryCache->renderDashedLine(point1, point2, rectangleColor);
|
||||
geometryCache->renderDashedLine(point2, point3, rectangleColor);
|
||||
geometryCache->renderDashedLine(point3, point4, rectangleColor);
|
||||
geometryCache->renderDashedLine(point4, point1, rectangleColor);
|
||||
|
||||
} else {
|
||||
|
||||
if (halfDimensions != _previousHalfDimensions) {
|
||||
QVector<glm::vec3> border;
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
|
||||
|
||||
_previousHalfDimensions = halfDimensions;
|
||||
|
||||
}
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,35 +41,6 @@ void Sphere3DOverlay::render(RenderArgs* args) {
|
|||
transform.postScale(getDimensions());
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Text3DOverlay.h"
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
||||
|
@ -114,6 +115,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, false, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
|
||||
|
||||
// Same font properties as textSize()
|
||||
|
|
|
@ -170,3 +170,11 @@ QSizeF TextOverlay::textSize(const QString& text) const {
|
|||
|
||||
return QSizeF(extents.x, extents.y);
|
||||
}
|
||||
|
||||
void TextOverlay::setFontSize(int fontSize) {
|
||||
_fontSize = fontSize;
|
||||
|
||||
auto oldTextRenderer = _textRenderer;
|
||||
_textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
|
||||
delete oldTextRenderer;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
|
||||
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
|
||||
const int DEFAULT_MARGIN = 10;
|
||||
const int DEFAULT_FONTSIZE = 11;
|
||||
const int DEFAULT_FONTSIZE = 12;
|
||||
const int DEFAULT_FONT_WEIGHT = 50;
|
||||
|
||||
class TextRenderer;
|
||||
|
@ -48,7 +48,7 @@ public:
|
|||
void setText(const QString& text) { _text = text; }
|
||||
void setLeftMargin(int margin) { _leftMargin = margin; }
|
||||
void setTopMargin(int margin) { _topMargin = margin; }
|
||||
void setFontSize(int fontSize) { _fontSize = fontSize; }
|
||||
void setFontSize(int fontSize);
|
||||
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual TextOverlay* createClone() const;
|
||||
|
|
|
@ -189,6 +189,66 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5csu">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="avatarCollisionSoundURLLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Avatar collision sound URL <span style=" color:#909090;">(optional)</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>collisionSoundURLEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_17csu">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="collisionSoundURLEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Enter the URL of a sound to play when you bump into something</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_appearance">
|
||||
|
|
|
@ -104,7 +104,9 @@ void AutoUpdater::parseLatestVersionData() {
|
|||
}
|
||||
|
||||
void AutoUpdater::checkVersionAndNotify() {
|
||||
if (QCoreApplication::applicationVersion() == "dev" || _builds.empty()) {
|
||||
if (QCoreApplication::applicationVersion() == "dev" ||
|
||||
QCoreApplication::applicationVersion().contains("PR") ||
|
||||
_builds.empty()) {
|
||||
// No version checking is required in dev builds or when no build
|
||||
// data was found for the platform
|
||||
return;
|
||||
|
|
|
@ -122,9 +122,9 @@ void EntityTreeRenderer::init() {
|
|||
// first chance, we'll check for enter/leave entity events.
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
|
||||
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
|
||||
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity);
|
||||
connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging);
|
||||
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity, Qt::QueuedConnection);
|
||||
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity, Qt::QueuedConnection);
|
||||
connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::shutdown() {
|
||||
|
@ -148,13 +148,14 @@ void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) {
|
|||
}
|
||||
}
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload) {
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload, bool reload) {
|
||||
EntityItemPointer entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||
return loadEntityScript(entity, isPreload);
|
||||
return loadEntityScript(entity, isPreload, reload);
|
||||
}
|
||||
|
||||
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut) {
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut,
|
||||
bool& reload) {
|
||||
isPending = false;
|
||||
QUrl url(scriptMaybeURLorText);
|
||||
|
||||
|
@ -192,7 +193,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
|
||||
if (!scriptCache->isInBadScriptList(url)) {
|
||||
scriptContents = scriptCache->getScript(url, this, isPending);
|
||||
scriptContents = scriptCache->getScript(url, this, isPending, reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +202,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
}
|
||||
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload) {
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload, bool reload) {
|
||||
if (_shuttingDown) {
|
||||
return QScriptValue(); // since we're shutting down, we don't load any more scripts
|
||||
}
|
||||
|
@ -221,8 +222,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool
|
|||
if (_entityScripts.contains(entityID)) {
|
||||
EntityScriptDetails details = _entityScripts[entityID];
|
||||
|
||||
// check to make sure our script text hasn't changed on us since we last loaded it
|
||||
if (details.scriptText == entityScript) {
|
||||
// check to make sure our script text hasn't changed on us since we last loaded it and we're not redownloading it
|
||||
if (details.scriptText == entityScript && !reload) {
|
||||
return details.scriptObject; // previously loaded
|
||||
}
|
||||
|
||||
|
@ -237,7 +238,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool
|
|||
bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text.
|
||||
bool isPending = false;
|
||||
QUrl url;
|
||||
QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url);
|
||||
QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url, reload);
|
||||
|
||||
if (isPending && isPreload && isURL) {
|
||||
_waitingOnPreload.insert(url, entityID);
|
||||
|
@ -1030,17 +1031,17 @@ void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
|
||||
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
||||
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const bool reload) {
|
||||
if (_tree && !_shuttingDown) {
|
||||
checkAndCallUnload(entityID);
|
||||
checkAndCallPreload(entityID);
|
||||
checkAndCallPreload(entityID, reload);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
|
||||
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) {
|
||||
if (_tree && !_shuttingDown) {
|
||||
// load the entity script if needed...
|
||||
QScriptValue entityScript = loadEntityScript(entityID, true); // is preload!
|
||||
QScriptValue entityScript = loadEntityScript(entityID, true, reload); // is preload!
|
||||
if (entityScript.property("preload").isValid()) {
|
||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||
entityScript.property("preload").call(entityScript, entityArgs);
|
||||
|
|
|
@ -110,7 +110,7 @@ signals:
|
|||
public slots:
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void entitySciptChanging(const EntityItemID& entityID);
|
||||
void entitySciptChanging(const EntityItemID& entityID, const bool reload);
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
// optional slots that can be wired to menu items
|
||||
|
@ -127,7 +127,7 @@ private:
|
|||
|
||||
void applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone);
|
||||
void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args);
|
||||
void checkAndCallPreload(const EntityItemID& entityID);
|
||||
void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false);
|
||||
void checkAndCallUnload(const EntityItemID& entityID);
|
||||
|
||||
QList<Model*> _releasedModels;
|
||||
|
@ -148,10 +148,10 @@ private:
|
|||
ScriptEngine* _entitiesScriptEngine;
|
||||
ScriptEngine* _sandboxScriptEngine;
|
||||
|
||||
QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false);
|
||||
QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false, bool reload = false);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false, bool reload = false);
|
||||
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url, bool& reload);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
|
|||
gpu::Batch& batch = *args->_batch;
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(getPosition());
|
||||
transform.setRotation(getRotation());
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
batch._glLineWidth(getLineWidth());
|
||||
|
|
|
@ -167,6 +167,25 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) {
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED));
|
||||
});
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastBroadcast();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN));
|
||||
});
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
@ -177,7 +196,9 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
|
|||
pendingChanges.resetItem(_myMetaItem, renderPayload);
|
||||
|
||||
if (_model) {
|
||||
return _model->addToScene(scene, pendingChanges);
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(this, statusGetters);
|
||||
return _model->addToScene(scene, pendingChanges, statusGetters);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -214,7 +235,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
render::PendingChanges pendingChanges;
|
||||
if (_model->needsFixupInScene()) {
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(this, statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include <QByteArray>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
@ -19,7 +19,7 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
@ -458,7 +458,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
|
|||
const glm::vec3& direction,
|
||||
bool& keepSearching,
|
||||
OctreeElement*& element,
|
||||
float& distance, BoxFace& face,
|
||||
float& distance, BoxFace& face,
|
||||
void** intersectedObject,
|
||||
bool precisionPicking) const
|
||||
{
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
#include <Transform.h>
|
||||
|
||||
|
||||
|
||||
#include "RenderableTextEntityItem.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
@ -33,19 +36,31 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
|
||||
Transform transformToTopLeft = getTransformToCenter();
|
||||
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
|
||||
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
|
||||
// Render background
|
||||
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
|
||||
|
||||
|
||||
// Batch render calls
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
Transform transformToTopLeft = getTransformToCenter();
|
||||
if (getFaceCamera()) {
|
||||
//rotate about vertical to face the camera
|
||||
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
transformToTopLeft.setRotation(orientation);
|
||||
}
|
||||
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
|
||||
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
|
||||
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
|
||||
// Render background
|
||||
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false, false, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||
|
||||
float scale = _lineHeight / _textRenderer->getFontSize();
|
||||
transformToTopLeft.setScale(scale); // Scale to have the correct line height
|
||||
|
@ -55,6 +70,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
|
||||
dimensions.y - 2.0f * topMargin);
|
||||
_textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -172,22 +172,20 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
Glower glow(0.0f);
|
||||
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Web);
|
||||
static const glm::vec2 texMin(0.0f);
|
||||
static const glm::vec2 texMax(1.0f);
|
||||
glm::vec2 topLeft(-0.5f -0.5f);
|
||||
glm::vec2 bottomRight(0.5f, 0.5f);
|
||||
static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f);
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
bool textured = false, culled = false, emissive = false;
|
||||
if (_texture) {
|
||||
batch._glActiveTexture(GL_TEXTURE0);
|
||||
batch._glBindTexture(GL_TEXTURE_2D, _texture);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
textured = emissive = true;
|
||||
}
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, textured, culled, emissive);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f));
|
||||
DependencyManager::get<DeferredLightingEffect>()->releaseSimpleProgram(batch);
|
||||
}
|
||||
|
||||
void RenderableWebEntityItem::setSourceUrl(const QString& value) {
|
||||
|
|
|
@ -28,6 +28,9 @@ class EntityActionFactoryInterface : public QObject, public Dependency {
|
|||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { assert(false); return nullptr; }
|
||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
||||
EntityItemPointer ownerEntity,
|
||||
QByteArray data) { assert(false); return nullptr; }
|
||||
};
|
||||
|
||||
#endif // hifi_EntityActionFactoryInterface_h
|
||||
|
|
|
@ -9,6 +9,78 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | | |
|
||||
| EntityActionInterface | | btActionInterface | | EntityActionFactoryInterface |
|
||||
| (entities) | | (bullet) | | (entities) |
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | |
|
||||
+----+ +--+ +----------+ | |
|
||||
| | | | |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
| | | | | | | |
|
||||
| AssignmentAction | | ObjectAction | | InterfaceActionFactory | | AssignmentActionFactory |
|
||||
|(assignment client)| | (physics) | | (interface) | | (assignment client) |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
|
|
||||
|
|
||||
|
|
||||
+--------------------+
|
||||
| |
|
||||
| ObjectActionSpring |
|
||||
| (physics) |
|
||||
+--------------------+
|
||||
|
||||
|
||||
|
||||
|
||||
An action is a callback which is registered with bullet. An action is called-back every physics
|
||||
simulation step and can do whatever it wants with the various datastructures it has available. An
|
||||
action, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
|
||||
point by a spring.
|
||||
|
||||
In this system, an action is a property of an EntityItem (rather, an EntityItem has a property which
|
||||
encodes a list of actions). Each action has a type and some arguments. Actions can be created by a
|
||||
script or when receiving information via an EntityTree data-stream (either over the network or from an
|
||||
svo file).
|
||||
|
||||
In the interface, if an EntityItem has actions, this EntityItem will have pointers to ObjectAction
|
||||
subclass (like ObjectActionSpring) instantiations. Code in the entities library affects an action-object
|
||||
via the EntityActionInterface (which knows nothing about bullet). When the ObjectAction subclass
|
||||
instance is created, it is registered as an action with bullet. Bullet will call into code in this
|
||||
instance with the btActionInterface every physics-simulation step.
|
||||
|
||||
Because the action can exist next to the interface's EntityTree or the entity-server's EntityTree,
|
||||
parallel versions of the factories and actions are needed.
|
||||
|
||||
In an entity-server, any type of action is instantiated as an AssignmentAction. This action isn't called
|
||||
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
|
||||
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
|
||||
right now the AssignmentAction class is a place-holder.
|
||||
|
||||
The action-objects are instantiated by singleton (dependecy) subclasses of EntityActionFactoryInterface.
|
||||
In the interface, the subclass is an InterfaceActionFactory and it will produce things like
|
||||
ObjectActionSpring. In an entity-server the subclass is an AssignmentActionFactory and it always
|
||||
produces AssignmentActions.
|
||||
|
||||
Depending on the action's type, it will have various arguments. When a script changes an argument of an
|
||||
action, the argument-holding member-variables of ObjectActionSpring (in this example) are updated and
|
||||
also serialized into _actionData in the EntityItem. Each subclass of ObjectAction knows how to serialize
|
||||
and deserialize its own arguments. _actionData is what gets sent over the wire or saved in an svo file.
|
||||
When a packet-reader receives data for _actionData, it will save it in the EntityItem; this causes the
|
||||
deserializer in the ObjectAction subclass to be called with the new data, thereby updating its argument
|
||||
variables. These argument variables are used by the code which is run when bullet does a callback.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
|
@ -174,3 +246,16 @@ QString EntityActionInterface::extractStringArgument(QString objectName, QVarian
|
|||
QString v = vV.toString();
|
||||
return v;
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType)
|
||||
{
|
||||
return stream << (quint16)entityActionType;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
|
||||
{
|
||||
quint16 actionTypeAsInt;
|
||||
stream >> actionTypeAsInt;
|
||||
entityActionType = (EntityActionType)actionTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ class EntitySimulation;
|
|||
|
||||
enum EntityActionType {
|
||||
// keep these synchronized with actionTypeFromString and actionTypeToString
|
||||
ACTION_TYPE_NONE,
|
||||
ACTION_TYPE_OFFSET,
|
||||
ACTION_TYPE_SPRING,
|
||||
ACTION_TYPE_HOLD
|
||||
ACTION_TYPE_NONE = 0,
|
||||
ACTION_TYPE_OFFSET = 1000,
|
||||
ACTION_TYPE_SPRING = 2000,
|
||||
ACTION_TYPE_HOLD = 3000
|
||||
};
|
||||
|
||||
|
||||
|
@ -32,10 +32,16 @@ public:
|
|||
EntityActionInterface() { }
|
||||
virtual ~EntityActionInterface() { }
|
||||
virtual const QUuid& getID() const = 0;
|
||||
virtual EntityActionType getType() { assert(false); return ACTION_TYPE_NONE; }
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
|
||||
virtual const EntityItemPointer& getOwnerEntity() const = 0;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const = 0;
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
|
||||
virtual bool updateArguments(QVariantMap arguments) = 0;
|
||||
virtual QVariantMap getArguments() = 0;
|
||||
|
||||
virtual QByteArray serialize() = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) = 0;
|
||||
|
||||
static EntityActionType actionTypeFromString(QString actionTypeString);
|
||||
static QString actionTypeToString(EntityActionType actionType);
|
||||
|
@ -67,4 +73,7 @@ protected:
|
|||
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
@ -22,12 +24,14 @@
|
|||
#include <SoundCache.h>
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityTree.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
|
||||
|
||||
bool EntityItem::_sendPhysicsUpdates = true;
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
_type(EntityTypes::Unknown),
|
||||
|
@ -54,6 +58,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
|||
_friction(ENTITY_ITEM_DEFAULT_FRICTION),
|
||||
_lifetime(ENTITY_ITEM_DEFAULT_LIFETIME),
|
||||
_script(ENTITY_ITEM_DEFAULT_SCRIPT),
|
||||
_scriptTimestamp(ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP),
|
||||
_collisionSoundURL(ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL),
|
||||
_registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT),
|
||||
_angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY),
|
||||
|
@ -63,8 +68,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
|||
_collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
|
||||
_locked(ENTITY_ITEM_DEFAULT_LOCKED),
|
||||
_userData(ENTITY_ITEM_DEFAULT_USER_DATA),
|
||||
_simulatorID(ENTITY_ITEM_DEFAULT_SIMULATOR_ID),
|
||||
_simulatorIDChangedTime(0),
|
||||
_simulationOwner(),
|
||||
_marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
|
||||
_name(ENTITY_ITEM_DEFAULT_NAME),
|
||||
_href(""),
|
||||
|
@ -85,6 +89,13 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
|
|||
}
|
||||
|
||||
EntityItem::~EntityItem() {
|
||||
// clear out any left-over actions
|
||||
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
||||
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
if (simulation) {
|
||||
clearActions(simulation);
|
||||
}
|
||||
|
||||
// these pointers MUST be correct at delete, else we probably have a dangling backpointer
|
||||
// to this EntityItem in the corresponding data structure.
|
||||
assert(!_simulated);
|
||||
|
@ -95,21 +106,24 @@ EntityItem::~EntityItem() {
|
|||
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_SIMULATION_OWNER;
|
||||
requestedProperties += PROP_POSITION;
|
||||
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
|
||||
requestedProperties += PROP_ROTATION;
|
||||
requestedProperties += PROP_DENSITY;
|
||||
requestedProperties += PROP_VELOCITY;
|
||||
requestedProperties += PROP_GRAVITY;
|
||||
requestedProperties += PROP_ANGULAR_VELOCITY;
|
||||
requestedProperties += PROP_ACCELERATION;
|
||||
|
||||
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
|
||||
requestedProperties += PROP_DENSITY;
|
||||
requestedProperties += PROP_GRAVITY;
|
||||
requestedProperties += PROP_DAMPING;
|
||||
requestedProperties += PROP_RESTITUTION;
|
||||
requestedProperties += PROP_FRICTION;
|
||||
requestedProperties += PROP_LIFETIME;
|
||||
requestedProperties += PROP_SCRIPT;
|
||||
requestedProperties += PROP_SCRIPT_TIMESTAMP;
|
||||
requestedProperties += PROP_COLLISION_SOUND_URL;
|
||||
requestedProperties += PROP_REGISTRATION_POINT;
|
||||
requestedProperties += PROP_ANGULAR_VELOCITY;
|
||||
requestedProperties += PROP_ANGULAR_DAMPING;
|
||||
requestedProperties += PROP_VISIBLE;
|
||||
requestedProperties += PROP_IGNORE_FOR_COLLISIONS;
|
||||
|
@ -118,10 +132,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_USER_DATA;
|
||||
requestedProperties += PROP_MARKETPLACE_ID;
|
||||
requestedProperties += PROP_NAME;
|
||||
requestedProperties += PROP_SIMULATOR_ID;
|
||||
requestedProperties += PROP_HREF;
|
||||
requestedProperties += PROP_DESCRIPTION;
|
||||
|
||||
requestedProperties += PROP_ACTION_DATA;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
|
@ -226,32 +240,35 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
// PROP_PAGED_PROPERTY,
|
||||
// PROP_CUSTOM_PROPERTIES_INCLUDED,
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, getPosition());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, getRotation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, getVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RESTITUTION, getRestitution());
|
||||
APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, getIgnoreForCollisions());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, getCollisionsWillMove());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
|
||||
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, getSimulatorID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData());
|
||||
|
||||
|
||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||
|
@ -315,8 +332,8 @@ int EntityItem::expectedBytes() {
|
|||
}
|
||||
|
||||
|
||||
// clients use this method to unpack FULL updates from entity-server
|
||||
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
|
||||
|
||||
// NOTE: This shouldn't happen. The only versions of the bit stream that didn't support split mtu buffers should
|
||||
|
@ -326,6 +343,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
return 0;
|
||||
}
|
||||
|
||||
args.entitiesPerPacket++;
|
||||
|
||||
// Header bytes
|
||||
// object ID [16 bytes]
|
||||
// ByteCountCoded(type code) [~1 byte]
|
||||
|
@ -340,14 +359,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
return 0;
|
||||
}
|
||||
|
||||
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
|
||||
glm::vec3 savePosition = getPosition();
|
||||
glm::quat saveRotation = getRotation();
|
||||
glm::vec3 saveVelocity = _velocity;
|
||||
glm::vec3 saveAngularVelocity = _angularVelocity;
|
||||
|
||||
int originalLength = bytesLeftToRead;
|
||||
QByteArray originalDataBuffer((const char*)data, originalLength);
|
||||
// TODO: figure out a way to avoid the big deep copy below.
|
||||
QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy!
|
||||
|
||||
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
||||
|
||||
|
@ -401,7 +415,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
qCDebug(entities) << " lastEdited =" << lastEdited;
|
||||
qCDebug(entities) << " ago=" << editedAgo << "seconds - " << agoAsString;
|
||||
#endif
|
||||
|
||||
|
||||
quint64 lastEditedFromBuffer = 0;
|
||||
quint64 lastEditedFromBufferAdjusted = 0;
|
||||
|
||||
|
@ -411,6 +425,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
dataAt += sizeof(lastEditedFromBuffer);
|
||||
bytesRead += sizeof(lastEditedFromBuffer);
|
||||
lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
|
||||
|
||||
if (lastEditedFromBufferAdjusted > now) {
|
||||
lastEditedFromBufferAdjusted = now;
|
||||
}
|
||||
|
@ -448,7 +463,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
ignoreServerPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ignoreServerPacket) {
|
||||
overwriteLocalData = false;
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -465,8 +480,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
_lastEdited = lastEditedFromBufferAdjusted;
|
||||
_lastEditedFromRemote = now;
|
||||
_lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;
|
||||
|
||||
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
|
||||
|
||||
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
|
||||
// the properties out of the bitstream (see below))
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
}
|
||||
|
@ -483,10 +498,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
#endif
|
||||
}
|
||||
|
||||
encodedUpdateDelta = updateDeltaCoder; // determine true length
|
||||
dataAt += encodedUpdateDelta.size();
|
||||
bytesRead += encodedUpdateDelta.size();
|
||||
|
||||
|
||||
// Newer bitstreams will have a last simulated and a last updated value
|
||||
quint64 lastSimulatedFromBufferAdjusted = now;
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) {
|
||||
|
@ -509,7 +525,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
dataAt += encodedSimulatedDelta.size();
|
||||
bytesRead += encodedSimulatedDelta.size();
|
||||
}
|
||||
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (overwriteLocalData) {
|
||||
qCDebug(entities) << "EntityItem::readEntityDataFromBuffer()... changed entity:" << getEntityItemID();
|
||||
|
@ -518,46 +534,77 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
qCDebug(entities) << " getLastUpdated:" << debugTime(getLastUpdated(), now);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Property Flags
|
||||
QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
bytesRead += propertyFlags.getEncodedLength();
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
|
||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
|
||||
float fromBuffer;
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer));
|
||||
dataAt += sizeof(fromBuffer);
|
||||
bytesRead += sizeof(fromBuffer);
|
||||
if (overwriteLocalData) {
|
||||
setRadius(fromBuffer);
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
|
||||
// pack SimulationOwner and terse update properties near each other
|
||||
|
||||
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
|
||||
// even when we would otherwise ignore the rest of the packet.
|
||||
|
||||
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
|
||||
QByteArray simOwnerData;
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
|
||||
SimulationOwner newSimOwner;
|
||||
newSimOwner.fromByteArray(simOwnerData);
|
||||
dataAt += bytes;
|
||||
bytesRead += bytes;
|
||||
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
bool weOwnIt = _simulationOwner.matchesValidID(nodeList->getSessionUUID());
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnIt;
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
} else {
|
||||
// legacy order of packing here
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
//READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
|
||||
|
@ -565,12 +612,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
|
||||
// this code for when there is only simulatorID and no simulation priority
|
||||
|
||||
// we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true
|
||||
// before we try to READ_ENTITY_PROPERTY it
|
||||
// before we try to READ_ENTITY_PROPERTY it
|
||||
bool temp = overwriteLocalData;
|
||||
overwriteLocalData = true;
|
||||
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, updateSimulatorID);
|
||||
READ_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, QUuid, updateSimulatorID);
|
||||
overwriteLocalData = temp;
|
||||
}
|
||||
|
||||
|
@ -583,12 +632,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
|
||||
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData);
|
||||
|
||||
////////////////////////////////////
|
||||
// WARNING: Do not add stream content here after the subclass. Always add it before the subclass
|
||||
//
|
||||
// NOTE: we had a bad version of the stream that we added stream data after the subclass. We can attempt to recover
|
||||
// NOTE: we had a bad version of the stream that we added stream data after the subclass. We can attempt to recover
|
||||
// by doing this parsing here... but it's not likely going to fully recover the content.
|
||||
//
|
||||
// TODO: Remove this conde once we've sufficiently migrated content past this damaged version
|
||||
|
@ -610,7 +662,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "skipTimeForward:" << skipTimeForward;
|
||||
#endif
|
||||
|
||||
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
||||
// we don't want the side effect of flag setting.
|
||||
simulateKinematicMotion(skipTimeForward, false);
|
||||
|
@ -620,19 +671,20 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
if (overwriteLocalData) {
|
||||
if (_simulatorID == myNodeID && !_simulatorID.isNull()) {
|
||||
// we own the simulation, so we keep our transform+velocities and remove any related dirty flags
|
||||
// rather than accept the values in the packet
|
||||
setPosition(savePosition);
|
||||
setRotation(saveRotation);
|
||||
_velocity = saveVelocity;
|
||||
_angularVelocity = saveAngularVelocity;
|
||||
_dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES);
|
||||
} else {
|
||||
if (!_simulationOwner.matchesValidID(myNodeID)) {
|
||||
|
||||
_lastSimulated = now;
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking for editing roundtrips here. We will tell our EntityTree that we just got incoming data about
|
||||
// and entity that was edited at some time in the past. The tree will determine how it wants to track this
|
||||
// information.
|
||||
if (_element && _element->getTree()) {
|
||||
_element->getTree()->trackIncomingEntityLastEdited(lastEditedFromBufferAdjusted, bytesRead);
|
||||
}
|
||||
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
@ -755,6 +807,10 @@ void EntityItem::simulate(const quint64& now) {
|
|||
}
|
||||
|
||||
void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
||||
if (hasActions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasAngularVelocity()) {
|
||||
// angular damping
|
||||
if (_angularDamping > 0.0f) {
|
||||
|
@ -766,7 +822,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
}
|
||||
|
||||
float angularSpeed = glm::length(_angularVelocity);
|
||||
|
||||
|
||||
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
|
||||
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
|
||||
if (setFlags && angularSpeed > 0.0f) {
|
||||
|
@ -774,8 +830,8 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
}
|
||||
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
} else {
|
||||
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
||||
// and break the integration into bullet-sized substeps
|
||||
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
||||
// and break the integration into bullet-sized substeps
|
||||
glm::quat rotation = getRotation();
|
||||
float dt = timeElapsed;
|
||||
while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) {
|
||||
|
@ -888,6 +944,7 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
|
||||
properties._type = getType();
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulationOwner, getSimulationOwner);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPosition);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
|
||||
|
@ -901,6 +958,7 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity);
|
||||
|
@ -912,11 +970,11 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData);
|
||||
|
||||
properties._defaultSettings = false;
|
||||
|
||||
|
@ -942,6 +1000,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
|
||||
// these affect TerseUpdate properties
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity);
|
||||
|
@ -963,10 +1022,10 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, updateSimulatorID);
|
||||
|
||||
// non-simulation properties below
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
|
||||
|
@ -977,6 +1036,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
|
||||
|
||||
if (somethingChanged) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
@ -1337,52 +1397,115 @@ void EntityItem::updateCreated(uint64_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::setSimulatorID(const QUuid& value) {
|
||||
_simulatorID = value;
|
||||
_simulatorIDChangedTime = usecTimestampNow();
|
||||
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
|
||||
_simulationOwner.set(id, priority);
|
||||
}
|
||||
|
||||
void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
|
||||
_simulationOwner.set(owner);
|
||||
}
|
||||
|
||||
void EntityItem::updateSimulatorID(const QUuid& value) {
|
||||
if (_simulatorID != value) {
|
||||
_simulatorID = value;
|
||||
_simulatorIDChangedTime = usecTimestampNow();
|
||||
if (_simulationOwner.setID(value)) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::clearSimulationOwnership() {
|
||||
_simulationOwner.clear();
|
||||
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
|
||||
// is only ever called entity-server-side and the flags are only used client-side
|
||||
//_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
|
||||
|
||||
}
|
||||
|
||||
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
|
||||
checkWaitingToRemove(simulation);
|
||||
if (!checkWaitingActionData(simulation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = addActionInternal(simulation, action);
|
||||
if (!result) {
|
||||
removeAction(simulation, action->getID());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
|
||||
assert(action);
|
||||
assert(simulation);
|
||||
auto actionOwnerEntity = action->getOwnerEntity().lock();
|
||||
assert(actionOwnerEntity);
|
||||
assert(actionOwnerEntity.get() == this);
|
||||
|
||||
const QUuid& actionID = action->getID();
|
||||
assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action);
|
||||
_objectActions[actionID] = action;
|
||||
|
||||
assert(action->getOwnerEntity().get() == this);
|
||||
|
||||
simulation->addAction(action);
|
||||
|
||||
return false;
|
||||
bool success;
|
||||
QByteArray newDataCache = serializeActions(success);
|
||||
if (success) {
|
||||
_allActionsDataCache = newDataCache;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
|
||||
checkWaitingToRemove(simulation);
|
||||
if (!checkWaitingActionData(simulation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_objectActions.contains(actionID)) {
|
||||
return false;
|
||||
}
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
return action->updateArguments(arguments);
|
||||
bool success = action->updateArguments(arguments);
|
||||
|
||||
if (success) {
|
||||
_allActionsDataCache = serializeActions(success);
|
||||
} else {
|
||||
qDebug() << "EntityItem::updateAction failed";
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
|
||||
checkWaitingToRemove(simulation);
|
||||
if (!checkWaitingActionData(simulation)) {
|
||||
return false;;
|
||||
}
|
||||
|
||||
return removeActionInternal(actionID);
|
||||
}
|
||||
|
||||
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
if (!simulation) {
|
||||
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
||||
simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
}
|
||||
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
_objectActions.remove(actionID);
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->removeFromSimulation(simulation);
|
||||
return true;
|
||||
_objectActions.remove(actionID);
|
||||
|
||||
if (simulation) {
|
||||
action->removeFromSimulation(simulation);
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
_allActionsDataCache = serializeActions(success);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EntityItem::clearActions(EntitySimulation* simulation) {
|
||||
bool EntityItem::clearActions(EntitySimulation* simulation) {
|
||||
_waitingActionData.clear();
|
||||
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
|
@ -1391,4 +1514,150 @@ void EntityItem::clearActions(EntitySimulation* simulation) {
|
|||
action->setOwnerEntity(nullptr);
|
||||
action->removeFromSimulation(simulation);
|
||||
}
|
||||
_actionsToRemove.clear();
|
||||
_allActionsDataCache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntityItem::deserializeActions(QByteArray allActionsData, EntitySimulation* simulation) const {
|
||||
bool success = true;
|
||||
QVector<QByteArray> serializedActions;
|
||||
if (allActionsData.size() > 0) {
|
||||
QDataStream serializedActionsStream(allActionsData);
|
||||
serializedActionsStream >> serializedActions;
|
||||
}
|
||||
|
||||
// Keep track of which actions got added or updated by the new actionData
|
||||
QSet<QUuid> updated;
|
||||
|
||||
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
||||
if (!simulation) {
|
||||
simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
}
|
||||
|
||||
if (simulation && entityTree) {
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
updated << actionID;
|
||||
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
// TODO: make sure types match? there isn't currently a way to
|
||||
// change the type of an existing action.
|
||||
action->deserialize(serializedAction);
|
||||
} else {
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
if (simulation) {
|
||||
EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id);
|
||||
EntityActionPointer action = actionFactory->factoryBA(simulation, entity, serializedAction);
|
||||
if (action) {
|
||||
entity->addActionInternal(simulation, action);
|
||||
}
|
||||
} else {
|
||||
// we can't yet add the action. This method will be called later.
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove any actions that weren't included in the new data.
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
if (!updated.contains(id)) {
|
||||
_actionsToRemove << id;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
// no simulation
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EntityItem::checkWaitingActionData(EntitySimulation* simulation) const {
|
||||
if (_waitingActionData.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
bool success = deserializeActions(_waitingActionData, simulation);
|
||||
if (success) {
|
||||
_waitingActionData.clear();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
|
||||
foreach(QUuid actionID, _actionsToRemove) {
|
||||
removeActionInternal(actionID, simulation);
|
||||
}
|
||||
_actionsToRemove.clear();
|
||||
}
|
||||
|
||||
void EntityItem::setActionData(QByteArray actionData) {
|
||||
checkWaitingToRemove();
|
||||
bool success = deserializeActions(actionData);
|
||||
_allActionsDataCache = actionData;
|
||||
if (success) {
|
||||
_waitingActionData.clear();
|
||||
} else {
|
||||
_waitingActionData = actionData;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray EntityItem::serializeActions(bool& success) const {
|
||||
QByteArray result;
|
||||
if (!checkWaitingActionData()) {
|
||||
return _waitingActionData;
|
||||
}
|
||||
|
||||
if (_objectActions.size() == 0) {
|
||||
success = true;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QVector<QByteArray> serializedActions;
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
QByteArray bytesForAction = action->serialize();
|
||||
serializedActions << bytesForAction;
|
||||
i++;
|
||||
}
|
||||
|
||||
QDataStream serializedActionsStream(&result, QIODevice::WriteOnly);
|
||||
serializedActionsStream << serializedActions;
|
||||
|
||||
if (result.size() >= _maxActionsDataSize) {
|
||||
success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionData() const {
|
||||
return _allActionsDataCache;
|
||||
}
|
||||
|
||||
QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
||||
QVariantMap result;
|
||||
|
||||
if (!checkWaitingActionData()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
result = action->getArguments();
|
||||
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "SimulationOwner.h"
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
|
@ -60,7 +61,6 @@ const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f;
|
|||
const float ACTIVATION_GRAVITY_DELTA = 0.1f;
|
||||
const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
|
||||
|
||||
|
||||
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
|
||||
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
|
||||
|
||||
|
@ -68,7 +68,6 @@ const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
|
|||
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
||||
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
||||
|
||||
|
||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
||||
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
||||
|
@ -92,8 +91,9 @@ public:
|
|||
DIRTY_LIFETIME = 0x0100,
|
||||
DIRTY_UPDATEABLE = 0x0200,
|
||||
DIRTY_MATERIAL = 0x00400,
|
||||
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object
|
||||
DIRTY_SIMULATOR_ID = 0x1000,
|
||||
DIRTY_PHYSICS_ACTIVATION = 0x0800, // should activate object in physics engine
|
||||
DIRTY_SIMULATOR_OWNERSHIP = 0x1000, // should claim simulator ownership
|
||||
DIRTY_SIMULATOR_ID = 0x2000, // the simulatorID has changed
|
||||
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
|
||||
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
|
||||
};
|
||||
|
@ -276,6 +276,10 @@ public:
|
|||
|
||||
const QString& getScript() const { return _script; }
|
||||
void setScript(const QString& value) { _script = value; }
|
||||
|
||||
quint64 getScriptTimestamp() const { return _scriptTimestamp; }
|
||||
void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; }
|
||||
|
||||
const QString& getCollisionSoundURL() const { return _collisionSoundURL; }
|
||||
void setCollisionSoundURL(const QString& value) { _collisionSoundURL = value; }
|
||||
|
||||
|
@ -314,10 +318,15 @@ public:
|
|||
const QString& getUserData() const { return _userData; }
|
||||
void setUserData(const QString& value) { _userData = value; }
|
||||
|
||||
QUuid getSimulatorID() const { return _simulatorID; }
|
||||
void setSimulatorID(const QUuid& value);
|
||||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||
void setSimulationOwner(const QUuid& id, quint8 priority);
|
||||
void setSimulationOwner(const SimulationOwner& owner);
|
||||
void promoteSimulationPriority(quint8 priority);
|
||||
|
||||
quint8 getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
||||
void updateSimulatorID(const QUuid& value);
|
||||
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
|
||||
void clearSimulationOwnership();
|
||||
|
||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
|
||||
|
@ -354,7 +363,7 @@ public:
|
|||
virtual void updateShapeType(ShapeType type) { /* do nothing */ }
|
||||
|
||||
uint32_t getDirtyFlags() const { return _dirtyFlags; }
|
||||
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
|
||||
void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; }
|
||||
|
||||
bool isMoving() const;
|
||||
|
||||
|
@ -375,10 +384,17 @@ public:
|
|||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
|
||||
void flagForOwnership() { _dirtyFlags |= DIRTY_SIMULATOR_OWNERSHIP; }
|
||||
|
||||
bool addAction(EntitySimulation* simulation, EntityActionPointer action);
|
||||
bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
|
||||
bool removeAction(EntitySimulation* simulation, const QUuid& actionID);
|
||||
void clearActions(EntitySimulation* simulation);
|
||||
bool clearActions(EntitySimulation* simulation);
|
||||
void setActionData(QByteArray actionData);
|
||||
const QByteArray getActionData() const;
|
||||
bool hasActions() { return !_objectActions.empty(); }
|
||||
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
|
||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -412,6 +428,7 @@ protected:
|
|||
float _friction;
|
||||
float _lifetime;
|
||||
QString _script;
|
||||
quint64 _scriptTimestamp;
|
||||
QString _collisionSoundURL;
|
||||
glm::vec3 _registrationPoint;
|
||||
glm::vec3 _angularVelocity;
|
||||
|
@ -421,8 +438,7 @@ protected:
|
|||
bool _collisionsWillMove;
|
||||
bool _locked;
|
||||
QString _userData;
|
||||
QUuid _simulatorID; // id of Node which is currently responsible for simulating this Entity
|
||||
quint64 _simulatorIDChangedTime; // when was _simulatorID last updated?
|
||||
SimulationOwner _simulationOwner;
|
||||
QString _marketplaceID;
|
||||
QString _name;
|
||||
QString _href; //Hyperlink href
|
||||
|
@ -452,7 +468,20 @@ protected:
|
|||
void* _physicsInfo = nullptr; // set by EntitySimulation
|
||||
bool _simulated; // set by EntitySimulation
|
||||
|
||||
bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action);
|
||||
bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr);
|
||||
bool deserializeActions(QByteArray allActionsData, EntitySimulation* simulation = nullptr) const;
|
||||
QByteArray serializeActions(bool& success) const;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
static int _maxActionsDataSize;
|
||||
mutable QByteArray _allActionsDataCache;
|
||||
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
|
||||
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
|
||||
// are used to keep track of and work around this situation.
|
||||
bool checkWaitingActionData(EntitySimulation* simulation = nullptr) const;
|
||||
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
||||
mutable QByteArray _waitingActionData;
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue