mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 10:43:56 +02:00
merge with hifi/plugins
This commit is contained in:
commit
f69b755166
231 changed files with 6479 additions and 6027 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);
|
|
@ -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) {
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -21,8 +21,8 @@ Hifi.Tooltip {
|
|||
|
||||
Rectangle {
|
||||
id: border
|
||||
color: "#7f000000"
|
||||
width: 322
|
||||
color: "#BF000000"
|
||||
width: 330
|
||||
height: col.height + hifi.layout.spacing * 2
|
||||
|
||||
Column {
|
||||
|
@ -40,6 +40,7 @@ Hifi.Tooltip {
|
|||
color: "white"
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: hifi.fonts.pixelSize * 2
|
||||
text: root.title
|
||||
wrapMode: Text.WrapAnywhere
|
||||
|
@ -61,6 +62,12 @@ Hifi.Tooltip {
|
|||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Item {
|
||||
id: firstSpacer
|
||||
width: col.width
|
||||
height: 5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: tooltipPic
|
||||
source: root.imageURL
|
||||
|
@ -68,7 +75,12 @@ Hifi.Tooltip {
|
|||
width: 320
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
verticalAlignment: Image.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
id: secondSpacer
|
||||
width: col.width
|
||||
height: 5
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -78,8 +90,8 @@ Hifi.Tooltip {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: root.description
|
||||
font.pixelSize: hifi.fonts.pixelSize
|
||||
wrapMode: Text.WrapAnywhere
|
||||
font.pixelSize: 16
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
#include <SceneScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SimpleAverage.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TextRenderer.h>
|
||||
#include <Tooltip.h>
|
||||
|
@ -196,6 +197,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
|
||||
|
||||
|
@ -1087,6 +1089,7 @@ void Application::paintGL() {
|
|||
uvec2 finalSize = toGlm(size);
|
||||
// Ensure the rendering context commands are completed when rendering
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFinish();
|
||||
_offscreenContext->doneCurrent();
|
||||
Q_ASSERT(!QOpenGLContext::currentContext());
|
||||
displayPlugin->preDisplay();
|
||||
|
@ -1130,7 +1133,6 @@ void Application::showEditEntitiesHelp() {
|
|||
InfoView::show(INFO_EDIT_ENTITIES_PATH);
|
||||
}
|
||||
|
||||
|
||||
void Application::resizeEvent(QResizeEvent * event) {
|
||||
resizeGL();
|
||||
}
|
||||
|
@ -1258,6 +1260,54 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (object == _glWindow) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
if (offscreenUi->eventFilter(object, event)) {
|
||||
return true;
|
||||
}
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
mousePressEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
mouseDoublePressEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::MouseButtonRelease:
|
||||
mouseReleaseEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::KeyPress:
|
||||
keyPressEvent((QKeyEvent*)event);
|
||||
return true;
|
||||
case QEvent::KeyRelease:
|
||||
keyReleaseEvent((QKeyEvent*)event);
|
||||
return true;
|
||||
case QEvent::FocusOut:
|
||||
focusOutEvent((QFocusEvent*)event);
|
||||
return true;
|
||||
case QEvent::TouchBegin:
|
||||
touchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||
event->accept();
|
||||
return true;
|
||||
case QEvent::TouchEnd:
|
||||
touchEndEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::TouchUpdate:
|
||||
touchUpdateEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::Wheel:
|
||||
wheelEvent(static_cast<QWheelEvent*>(event));
|
||||
return true;
|
||||
case QEvent::Drop:
|
||||
dropEvent(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1848,8 +1898,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.
|
||||
}
|
||||
|
@ -1892,13 +1958,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(getActiveDisplayPlugin()->isThrottled() ? THROTTLED_IDLE_TIMER_DELAY : 0);
|
||||
}
|
||||
|
||||
// check for any requested background downloads.
|
||||
emit checkBackgroundDownloads();
|
||||
lastIdleEnd = usecTimestampNow();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -2269,6 +2337,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() {
|
||||
|
@ -3588,6 +3657,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;
|
||||
|
@ -3609,7 +3680,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) {
|
||||
|
@ -4877,8 +4948,8 @@ void Application::updateDisplayMode() {
|
|||
DisplayPluginPointer oldDisplayPlugin = _displayPlugin;
|
||||
if (oldDisplayPlugin != newDisplayPlugin) {
|
||||
if (oldDisplayPlugin) {
|
||||
oldDisplayPlugin->removeEventFilter(offscreenUi.data());
|
||||
oldDisplayPlugin->removeEventFilter(qApp);
|
||||
oldDisplayPlugin->removeEventFilter(offscreenUi.data());
|
||||
}
|
||||
|
||||
if (!_currentDisplayPluginActions.isEmpty()) {
|
||||
|
@ -4894,8 +4965,8 @@ void Application::updateDisplayMode() {
|
|||
newDisplayPlugin->activate(this);
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
newDisplayPlugin->installEventFilter(offscreenUi.data());
|
||||
newDisplayPlugin->installEventFilter(qApp);
|
||||
newDisplayPlugin->installEventFilter(offscreenUi.data());
|
||||
QWindow* pluginWindow = newDisplayPlugin->getWindow();
|
||||
if (pluginWindow) {
|
||||
DependencyManager::get<OffscreenUi>()->setProxyWindow(pluginWindow);
|
||||
|
|
|
@ -470,7 +470,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);
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
#include <display-plugins/stereo/InterleavedStereoDisplayPlugin.h>
|
||||
#include <display-plugins/Basic2DWindowOpenGLDisplayPlugin.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <display-plugins/oculus/Oculus_0_6_DisplayPlugin.h>
|
||||
#else
|
||||
#include <display-plugins/oculus/Oculus_0_5_DisplayPlugin.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <display-plugins/openvr/OpenVrDisplayPlugin.h>
|
||||
|
||||
|
@ -57,10 +56,10 @@ const DisplayPluginList& getDisplayPlugins() {
|
|||
new NullDisplayPlugin(),
|
||||
new SideBySideStereoDisplayPlugin(),
|
||||
// new InterleavedStereoDisplayPlugin(),
|
||||
#ifdef Q_OS_WIN
|
||||
new Oculus_0_6_DisplayPlugin(),
|
||||
#else
|
||||
#if (OVR_MAJOR_VERSION == 5)
|
||||
new Oculus_0_5_DisplayPlugin(),
|
||||
#else
|
||||
new Oculus_0_6_DisplayPlugin(),
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -533,7 +533,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");
|
||||
|
|
|
@ -209,6 +209,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";
|
||||
|
@ -266,6 +267,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);
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
static bool isRoomTracking = true;
|
||||
|
@ -92,6 +93,7 @@ MyAvatar::MyAvatar() :
|
|||
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
|
||||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_collisionSoundURL(""),
|
||||
_characterController(this),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
|
@ -680,6 +682,7 @@ void MyAvatar::saveData() {
|
|||
settings.endArray();
|
||||
|
||||
settings.setValue("displayName", _displayName);
|
||||
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
@ -805,6 +808,7 @@ void MyAvatar::loadData() {
|
|||
settings.endArray();
|
||||
|
||||
setDisplayName(settings.value("displayName").toString());
|
||||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
@ -1206,6 +1210,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()) {
|
||||
|
@ -1235,9 +1246,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) {
|
||||
|
@ -1434,7 +1443,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;
|
||||
|
@ -1655,7 +1665,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
|
||||
|
@ -1668,8 +1678,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; }
|
||||
|
@ -206,6 +210,7 @@ public slots:
|
|||
|
||||
signals:
|
||||
void transformChanged();
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -235,6 +240,7 @@ private:
|
|||
float _scriptedMotorTimescale; // timescale for avatar to achieve its target velocity
|
||||
int _scriptedMotorFrame;
|
||||
quint32 _motionBehaviors;
|
||||
QString _collisionSoundURL;
|
||||
|
||||
DynamicCharacterController _characterController;
|
||||
|
||||
|
|
|
@ -780,7 +780,7 @@ 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 0
|
||||
if (_shapes.isEmpty()) {
|
||||
|
@ -788,17 +788,17 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) {
|
|||
// 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;
|
||||
|
@ -810,9 +810,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));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,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; }
|
||||
|
|
|
@ -25,18 +25,13 @@
|
|||
|
||||
|
||||
// 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;
|
||||
|
@ -235,7 +230,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);
|
||||
}
|
||||
|
@ -387,8 +381,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;
|
||||
}
|
||||
|
@ -506,7 +500,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();
|
||||
|
@ -735,7 +729,7 @@ glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) {
|
|||
|
||||
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();
|
||||
|
@ -744,7 +738,7 @@ glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos
|
|||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
|
||||
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 +52,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
|
|||
_localElements = AddStatItem("Local Elements");
|
||||
_localElementsMemory = AddStatItem("Elements Memory");
|
||||
_sendingMode = AddStatItem("Sending Mode");
|
||||
|
||||
_processedPackets = AddStatItem("Processed 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 +129,34 @@ 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);
|
||||
}
|
||||
_lastRefresh = now;
|
||||
|
||||
// Update labels
|
||||
|
||||
QLabel* label;
|
||||
|
@ -203,9 +241,100 @@ 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 averagePacketsPerSecond = entities->getAveragePacketsPerSecond();
|
||||
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);
|
||||
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket);
|
||||
|
||||
QString averagePacketsPerSecondString = locale.toString(averagePacketsPerSecond);
|
||||
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond);
|
||||
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond);
|
||||
|
||||
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
|
||||
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
|
||||
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
|
||||
|
||||
label = _labels[_processedPackets];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(averagePacketsPerSecondString) << " per second";
|
||||
|
||||
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);
|
||||
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());
|
||||
|
@ -206,6 +208,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);
|
||||
|
@ -223,8 +227,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());
|
||||
|
|
|
@ -121,8 +121,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)) {
|
||||
|
@ -135,7 +135,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) {
|
||||
|
@ -146,19 +145,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);
|
||||
|
@ -279,15 +265,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());
|
||||
}
|
||||
|
|
|
@ -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,22 +32,22 @@ 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();
|
||||
#if 0
|
||||
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
|
||||
app->setViewMatrixTranslation(oldTranslation + getPosition());
|
||||
_entityTreeRenderer->render(args);
|
||||
Application::getInstance()->setViewMatrixTranslation(oldTranslation);
|
||||
#endif
|
||||
} glPopMatrix();
|
||||
|
||||
auto batch = args ->_batch;
|
||||
Application* app = Application::getInstance();
|
||||
glm::vec3 oldTranslation = app->getCurrentViewFrustum()->getPosition();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,324 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/29
|
||||
// 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 "OglplusHelpers.h"
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
using namespace oglplus;
|
||||
using namespace oglplus::shapes;
|
||||
|
||||
static const char * SIMPLE_TEXTURED_VS = R"VS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
uniform mat4 Projection = mat4(1);
|
||||
uniform mat4 ModelView = mat4(1);
|
||||
|
||||
layout(location = 0) in vec3 Position;
|
||||
layout(location = 1) in vec2 TexCoord;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = Projection * ModelView * vec4(Position, 1);
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
|
||||
)VS";
|
||||
|
||||
static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
uniform sampler2D sampler;
|
||||
uniform float Alpha = 1.0;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
out vec4 vFragColor;
|
||||
|
||||
void main() {
|
||||
vec4 c = texture(sampler, vTexCoord);
|
||||
c.a = min(Alpha, c.a);
|
||||
vFragColor = c;
|
||||
}
|
||||
|
||||
)FS";
|
||||
|
||||
|
||||
ProgramPtr loadDefaultShader() {
|
||||
ProgramPtr result;
|
||||
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
||||
return result;
|
||||
}
|
||||
|
||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
||||
using namespace oglplus;
|
||||
try {
|
||||
result = ProgramPtr(new Program());
|
||||
// attach the shaders to the program
|
||||
result->AttachShader(
|
||||
VertexShader()
|
||||
.Source(GLSLSource(vs))
|
||||
.Compile()
|
||||
);
|
||||
result->AttachShader(
|
||||
FragmentShader()
|
||||
.Source(GLSLSource(fs))
|
||||
.Compile()
|
||||
);
|
||||
result->Link();
|
||||
} catch (ProgramBuildError & err) {
|
||||
Q_UNUSED(err);
|
||||
Q_ASSERT_X(false, "compileProgram", "Failed to build shader program");
|
||||
qFatal((const char*)err.Message);
|
||||
result.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) {
|
||||
using namespace oglplus;
|
||||
Vec3f a(1, 0, 0);
|
||||
Vec3f b(0, 1, 0);
|
||||
if (aspect > 1) {
|
||||
b[1] /= aspect;
|
||||
} else {
|
||||
a[0] *= aspect;
|
||||
}
|
||||
return ShapeWrapperPtr(
|
||||
new shapes::ShapeWrapper({ "Position", "TexCoord" }, shapes::Plane(a, b), *program)
|
||||
);
|
||||
}
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
static glm::vec3 getPoint(float yaw, float pitch) {
|
||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||
glm::sin(-pitch),
|
||||
glm::cos(-pitch) * (-glm::cos(yaw)));
|
||||
}
|
||||
|
||||
|
||||
class SphereSection : public DrawingInstructionWriter, public DrawMode {
|
||||
public:
|
||||
using IndexArray = std::vector<GLuint>;
|
||||
using PosArray = std::vector<float>;
|
||||
using TexArray = std::vector<float>;
|
||||
/// The type of the index container returned by Indices()
|
||||
// vertex positions
|
||||
PosArray _pos_data;
|
||||
// vertex tex coords
|
||||
TexArray _tex_data;
|
||||
IndexArray _idx_data;
|
||||
unsigned int _prim_count{ 0 };
|
||||
|
||||
public:
|
||||
SphereSection(
|
||||
const float fov,
|
||||
const float aspectRatio,
|
||||
const int slices_,
|
||||
const int stacks_) {
|
||||
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
|
||||
if (fov >= PI) {
|
||||
qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
|
||||
}
|
||||
|
||||
int gridSize = std::max(slices_, stacks_);
|
||||
int gridSizeLog2 = 1;
|
||||
while (1 << gridSizeLog2 < gridSize) {
|
||||
++gridSizeLog2;
|
||||
}
|
||||
gridSize = (1 << gridSizeLog2) + 1;
|
||||
// Compute number of vertices needed
|
||||
int vertices = gridSize * gridSize;
|
||||
_pos_data.resize(vertices * 3);
|
||||
_tex_data.resize(vertices * 2);
|
||||
|
||||
// Compute vertices positions and texture UV coordinate
|
||||
for (int y = 0; y <= gridSize; ++y) {
|
||||
for (int x = 0; x <= gridSize; ++x) {
|
||||
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < gridSize; i++) {
|
||||
float stacksRatio = (float)i / (float)(gridSize - 1); // First stack is 0.0f, last stack is 1.0f
|
||||
// abs(theta) <= fov / 2.0f
|
||||
float pitch = -fov * (stacksRatio - 0.5f);
|
||||
for (int j = 0; j < gridSize; j++) {
|
||||
float slicesRatio = (float)j / (float)(gridSize - 1); // First slice is 0.0f, last slice is 1.0f
|
||||
// abs(phi) <= fov * aspectRatio / 2.0f
|
||||
float yaw = -fov * aspectRatio * (slicesRatio - 0.5f);
|
||||
int vertex = i * gridSize + j;
|
||||
int posOffset = vertex * 3;
|
||||
int texOffset = vertex * 2;
|
||||
vec3 pos = getPoint(yaw, pitch);
|
||||
_pos_data[posOffset] = pos.x;
|
||||
_pos_data[posOffset + 1] = pos.y;
|
||||
_pos_data[posOffset + 2] = pos.z;
|
||||
_tex_data[texOffset] = slicesRatio;
|
||||
_tex_data[texOffset + 1] = stacksRatio;
|
||||
}
|
||||
} // done with vertices
|
||||
|
||||
int rowLen = gridSize;
|
||||
|
||||
// gridsize now refers to the triangles, not the vertices, so reduce by one
|
||||
// or die by fencepost error http://en.wikipedia.org/wiki/Off-by-one_error
|
||||
--gridSize;
|
||||
int quads = gridSize * gridSize;
|
||||
for (int t = 0; t < quads; ++t) {
|
||||
int x =
|
||||
((t & 0x0001) >> 0) |
|
||||
((t & 0x0004) >> 1) |
|
||||
((t & 0x0010) >> 2) |
|
||||
((t & 0x0040) >> 3) |
|
||||
((t & 0x0100) >> 4) |
|
||||
((t & 0x0400) >> 5) |
|
||||
((t & 0x1000) >> 6) |
|
||||
((t & 0x4000) >> 7);
|
||||
int y =
|
||||
((t & 0x0002) >> 1) |
|
||||
((t & 0x0008) >> 2) |
|
||||
((t & 0x0020) >> 3) |
|
||||
((t & 0x0080) >> 4) |
|
||||
((t & 0x0200) >> 5) |
|
||||
((t & 0x0800) >> 6) |
|
||||
((t & 0x2000) >> 7) |
|
||||
((t & 0x8000) >> 8);
|
||||
int i = x * (rowLen) + y;
|
||||
|
||||
_idx_data.push_back(i);
|
||||
_idx_data.push_back(i + 1);
|
||||
_idx_data.push_back(i + rowLen + 1);
|
||||
|
||||
_idx_data.push_back(i + rowLen + 1);
|
||||
_idx_data.push_back(i + rowLen);
|
||||
_idx_data.push_back(i);
|
||||
}
|
||||
_prim_count = quads * 2;
|
||||
}
|
||||
|
||||
/// Returns the winding direction of faces
|
||||
FaceOrientation FaceWinding(void) const {
|
||||
return FaceOrientation::CCW;
|
||||
}
|
||||
|
||||
typedef GLuint(SphereSection::*VertexAttribFunc)(std::vector<GLfloat>&) const;
|
||||
|
||||
/// Makes the vertex positions and returns the number of values per vertex
|
||||
template <typename T>
|
||||
GLuint Positions(std::vector<T>& dest) const {
|
||||
dest.clear();
|
||||
dest.insert(dest.begin(), _pos_data.begin(), _pos_data.end());
|
||||
return 3;
|
||||
}
|
||||
|
||||
/// Makes the vertex normals and returns the number of values per vertex
|
||||
template <typename T>
|
||||
GLuint Normals(std::vector<T>& dest) const {
|
||||
dest.clear();
|
||||
return 3;
|
||||
}
|
||||
|
||||
/// Makes the vertex tangents and returns the number of values per vertex
|
||||
template <typename T>
|
||||
GLuint Tangents(std::vector<T>& dest) const {
|
||||
dest.clear();
|
||||
return 3;
|
||||
}
|
||||
|
||||
/// Makes the vertex bi-tangents and returns the number of values per vertex
|
||||
template <typename T>
|
||||
GLuint Bitangents(std::vector<T>& dest) const {
|
||||
dest.clear();
|
||||
return 3;
|
||||
}
|
||||
|
||||
/// Makes the texture coordinates returns the number of values per vertex
|
||||
template <typename T>
|
||||
GLuint TexCoordinates(std::vector<T>& dest) const {
|
||||
dest.clear();
|
||||
dest.insert(dest.begin(), _tex_data.begin(), _tex_data.end());
|
||||
return 2;
|
||||
}
|
||||
|
||||
typedef VertexAttribsInfo<
|
||||
SphereSection,
|
||||
std::tuple<
|
||||
VertexPositionsTag,
|
||||
VertexNormalsTag,
|
||||
VertexTangentsTag,
|
||||
VertexBitangentsTag,
|
||||
VertexTexCoordinatesTag
|
||||
>
|
||||
> VertexAttribs;
|
||||
|
||||
Spheref MakeBoundingSphere(void) const {
|
||||
GLfloat min_x = _pos_data[3], max_x = _pos_data[3];
|
||||
GLfloat min_y = _pos_data[4], max_y = _pos_data[4];
|
||||
GLfloat min_z = _pos_data[5], max_z = _pos_data[5];
|
||||
for (std::size_t v = 0, vn = _pos_data.size() / 3; v != vn; ++v) {
|
||||
GLfloat x = _pos_data[v * 3 + 0];
|
||||
GLfloat y = _pos_data[v * 3 + 1];
|
||||
GLfloat z = _pos_data[v * 3 + 2];
|
||||
|
||||
if (min_x > x) min_x = x;
|
||||
if (min_y > y) min_y = y;
|
||||
if (min_z > z) min_z = z;
|
||||
if (max_x < x) max_x = x;
|
||||
if (max_y < y) max_y = y;
|
||||
if (max_z < z) max_z = z;
|
||||
}
|
||||
|
||||
Vec3f c(
|
||||
(min_x + max_x) * 0.5f,
|
||||
(min_y + max_y) * 0.5f,
|
||||
(min_z + max_z) * 0.5f
|
||||
);
|
||||
|
||||
return Spheref(
|
||||
c.x(), c.y(), c.z(),
|
||||
Distance(c, Vec3f(min_x, min_y, min_z))
|
||||
);
|
||||
}
|
||||
|
||||
/// Queries the bounding sphere coordinates and dimensions
|
||||
template <typename T>
|
||||
void BoundingSphere(oglplus::Sphere<T>& bounding_sphere) const {
|
||||
bounding_sphere = oglplus::Sphere<T>(MakeBoundingSphere());
|
||||
}
|
||||
|
||||
|
||||
/// Returns element indices that are used with the drawing instructions
|
||||
const IndexArray & Indices(Default = Default()) const {
|
||||
return _idx_data;
|
||||
}
|
||||
|
||||
/// Returns the instructions for rendering of faces
|
||||
DrawingInstructions Instructions(PrimitiveType primitive) const {
|
||||
DrawingInstructions instr = this->MakeInstructions();
|
||||
DrawOperation operation;
|
||||
operation.method = DrawOperation::Method::DrawElements;
|
||||
operation.mode = primitive;
|
||||
operation.first = 0;
|
||||
operation.count = _prim_count * 3;
|
||||
operation.restart_index = DrawOperation::NoRestartIndex();
|
||||
operation.phase = 0;
|
||||
this->AddInstruction(instr, operation);
|
||||
return std::move(instr);
|
||||
}
|
||||
|
||||
/// Returns the instructions for rendering of faces
|
||||
DrawingInstructions Instructions(Default = Default()) const {
|
||||
return Instructions(PrimitiveType::Triangles);
|
||||
}
|
||||
};
|
||||
|
||||
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov, float aspect, int slices, int stacks) {
|
||||
using namespace oglplus;
|
||||
return ShapeWrapperPtr(
|
||||
new shapes::ShapeWrapper({ "Position", "TexCoord" }, SphereSection(fov, aspect, slices, stacks), *program)
|
||||
);
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/05/26
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#pragma warning(disable : 4068)
|
||||
|
||||
#define OGLPLUS_USE_GLEW 1
|
||||
#define OGLPLUS_USE_GLCOREARB_H 0
|
||||
#define OGLPLUS_USE_BOOST_CONFIG 1
|
||||
#define OGLPLUS_NO_SITE_CONFIG 1
|
||||
#define OGLPLUS_LOW_PROFILE 1
|
||||
#include <GL/glew.h>
|
||||
#include <oglplus/gl.hpp>
|
||||
|
||||
#include <oglplus/all.hpp>
|
||||
#include <oglplus/interop/glm.hpp>
|
||||
#include <oglplus/bound/texture.hpp>
|
||||
#include <oglplus/bound/framebuffer.hpp>
|
||||
#include <oglplus/bound/renderbuffer.hpp>
|
||||
#include <oglplus/shapes/wrapper.hpp>
|
||||
#include <oglplus/shapes/plane.hpp>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
using FramebufferPtr = std::shared_ptr<oglplus::Framebuffer>;
|
||||
using ShapeWrapperPtr = std::shared_ptr<oglplus::shapes::ShapeWrapper>;
|
||||
using BufferPtr = std::shared_ptr<oglplus::Buffer>;
|
||||
using VertexArrayPtr = std::shared_ptr<oglplus::VertexArray>;
|
||||
using ProgramPtr = std::shared_ptr<oglplus::Program>;
|
||||
using Mat4Uniform = oglplus::Uniform<mat4>;
|
||||
|
||||
ProgramPtr loadDefaultShader();
|
||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
||||
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
||||
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32);
|
||||
|
||||
|
||||
// A basic wrapper for constructing a framebuffer with a renderbuffer
|
||||
// for the depth attachment and an undefined type for the color attachement
|
||||
// This allows us to reuse the basic framebuffer code for both the Mirror
|
||||
// FBO as well as the Oculus swap textures we will use to render the scene
|
||||
// Though we don't really need depth at all for the mirror FBO, or even an
|
||||
// FBO, but using one means I can just use a glBlitFramebuffer to get it onto
|
||||
// the screen.
|
||||
template <
|
||||
typename C,
|
||||
typename D
|
||||
>
|
||||
struct FramebufferWrapper {
|
||||
uvec2 size;
|
||||
oglplus::Framebuffer fbo;
|
||||
C color;
|
||||
D depth;
|
||||
|
||||
FramebufferWrapper() {}
|
||||
|
||||
virtual ~FramebufferWrapper() {
|
||||
}
|
||||
|
||||
virtual void Init(const uvec2 & size) {
|
||||
this->size = size;
|
||||
initColor();
|
||||
initDepth();
|
||||
initDone();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void Bound(F f) {
|
||||
Bound(oglplus::Framebuffer::Target::Draw, f);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void Bound(oglplus::Framebuffer::Target target , F f) {
|
||||
fbo.Bind(target);
|
||||
onBind(target);
|
||||
f();
|
||||
onUnbind(target);
|
||||
oglplus::DefaultFramebuffer().Bind(target);
|
||||
}
|
||||
|
||||
void Viewport() {
|
||||
oglplus::Context::Viewport(size.x, size.y);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onBind(oglplus::Framebuffer::Target target) {}
|
||||
virtual void onUnbind(oglplus::Framebuffer::Target target) {}
|
||||
|
||||
static GLenum toEnum(oglplus::Framebuffer::Target target) {
|
||||
switch (target) {
|
||||
case oglplus::Framebuffer::Target::Draw:
|
||||
return GL_DRAW_FRAMEBUFFER;
|
||||
case oglplus::Framebuffer::Target::Read:
|
||||
return GL_READ_FRAMEBUFFER;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return GL_FRAMEBUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void initDepth() {}
|
||||
|
||||
virtual void initColor() {}
|
||||
|
||||
virtual void initDone() = 0;
|
||||
};
|
||||
|
||||
struct BasicFramebufferWrapper : public FramebufferWrapper <oglplus::Texture, oglplus::Renderbuffer> {
|
||||
protected:
|
||||
virtual void initDepth() override {
|
||||
using namespace oglplus;
|
||||
Context::Bound(Renderbuffer::Target::Renderbuffer, depth)
|
||||
.Storage(
|
||||
PixelDataInternalFormat::DepthComponent,
|
||||
size.x, size.y);
|
||||
}
|
||||
|
||||
virtual void initColor() override {
|
||||
using namespace oglplus;
|
||||
Context::Bound(oglplus::Texture::Target::_2D, color)
|
||||
.MinFilter(TextureMinFilter::Linear)
|
||||
.MagFilter(TextureMagFilter::Linear)
|
||||
.WrapS(TextureWrap::ClampToEdge)
|
||||
.WrapT(TextureWrap::ClampToEdge)
|
||||
.Image2D(
|
||||
0, PixelDataInternalFormat::RGBA8,
|
||||
size.x, size.y,
|
||||
0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
virtual void initDone() override {
|
||||
using namespace oglplus;
|
||||
static const Framebuffer::Target target = Framebuffer::Target::Draw;
|
||||
Bound(target, [&] {
|
||||
fbo.AttachTexture(target, FramebufferAttachment::Color, color, 0);
|
||||
fbo.AttachRenderbuffer(target, FramebufferAttachment::Depth, depth);
|
||||
fbo.Complete(target);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
using BasicFramebufferWrapperPtr = std::shared_ptr<BasicFramebufferWrapper>;
|
|
@ -36,15 +36,36 @@ void OpenGLDisplayPlugin::finishFrame() {
|
|||
doneCurrent();
|
||||
};
|
||||
|
||||
static float PLANE_VERTICES[] = {
|
||||
-1, -1, 0, 0,
|
||||
-1, +1, 0, 1,
|
||||
+1, -1, 1, 0,
|
||||
+1, +1, 1, 1,
|
||||
};
|
||||
|
||||
void OpenGLDisplayPlugin::customizeContext(PluginContainer * container) {
|
||||
using namespace oglplus;
|
||||
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||
Context::Disable(Capability::Blend);
|
||||
Context::Disable(Capability::DepthTest);
|
||||
Context::Disable(Capability::CullFace);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
_program = loadDefaultShader();
|
||||
_plane = loadPlane(_program);
|
||||
Context::ClearColor(0, 0, 0, 1);
|
||||
auto attribs = _program->ActiveAttribs();
|
||||
for(size_t i = 0; i < attribs.Size(); ++i) {
|
||||
auto attrib = attribs.At(i);
|
||||
if (String("Position") == attrib.Name()) {
|
||||
_positionAttribute = attrib.Index();
|
||||
} else if (String("TexCoord") == attrib.Name()) {
|
||||
_texCoordAttribute = attrib.Index();
|
||||
}
|
||||
qDebug() << attrib.Name().c_str();
|
||||
}
|
||||
_vertexBuffer.reset(new oglplus::Buffer());
|
||||
_vertexBuffer->Bind(Buffer::Target::Array);
|
||||
_vertexBuffer->Data(Buffer::Target::Array, BufferData(PLANE_VERTICES));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::activate(PluginContainer * container) {
|
||||
|
@ -56,8 +77,9 @@ void OpenGLDisplayPlugin::deactivate() {
|
|||
|
||||
makeCurrent();
|
||||
Q_ASSERT(0 == glGetError());
|
||||
_plane.reset();
|
||||
_program.reset();
|
||||
_vertexBuffer.reset();
|
||||
// glDeleteBuffers(1, &_vertexBuffer);
|
||||
// _vertexBuffer = 0;
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
|
@ -104,13 +126,23 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
void OpenGLDisplayPlugin::display(
|
||||
GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||
using namespace oglplus;
|
||||
|
||||
uvec2 size = getRecommendedRenderSize();
|
||||
Context::Viewport(size.x, size.y);
|
||||
Context::Clear().ColorBuffer();
|
||||
|
||||
_program->Bind();
|
||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
drawUnitQuad();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::drawUnitQuad() {
|
||||
using namespace oglplus;
|
||||
_program->Bind();
|
||||
_vertexBuffer->Bind(Buffer::Target::Array);
|
||||
glEnableVertexAttribArray(_positionAttribute);
|
||||
glVertexAttribPointer(_positionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0);
|
||||
glEnableVertexAttribArray(_texCoordAttribute);
|
||||
glVertexAttribPointer(_texCoordAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glDisableVertexAttribArray(_positionAttribute);
|
||||
glDisableVertexAttribArray(_texCoordAttribute);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glUseProgram(0);
|
||||
}
|
|
@ -35,14 +35,16 @@ protected:
|
|||
// Needs to be called by the activate method after the GL context has been created to
|
||||
// initialize OpenGL context settings needed by the plugin
|
||||
virtual void customizeContext(PluginContainer * container);
|
||||
|
||||
virtual void drawUnitQuad();
|
||||
virtual void makeCurrent() = 0;
|
||||
virtual void doneCurrent() = 0;
|
||||
virtual void swapBuffers() = 0;
|
||||
|
||||
QTimer _timer;
|
||||
ProgramPtr _program;
|
||||
ShapeWrapperPtr _plane;
|
||||
BufferPtr _vertexBuffer;
|
||||
GLint _positionAttribute{0};
|
||||
GLint _texCoordAttribute{0};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
void OculusBaseDisplayPlugin::activate(PluginContainer * container) {
|
||||
glm::uvec2 eyeSizes[2];
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _hmd->MaxEyeFov[eye]);
|
||||
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
|
||||
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
|
||||
ovrMatrix4f ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||
|
|
|
@ -32,6 +32,7 @@ protected:
|
|||
ovrEyeRenderDesc _eyeRenderDescs[2];
|
||||
ovrPosef _eyePoses[2];
|
||||
ovrVector3f _eyeOffsets[2];
|
||||
ovrFovPort _eyeFovs[2];
|
||||
mat4 _eyeProjections[2];
|
||||
mat4 _compositeEyeProjections[2];
|
||||
uvec2 _desiredFramebufferSize;
|
||||
|
|
|
@ -22,23 +22,22 @@
|
|||
#include <OVR_CAPI_GL.h>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <OglplusHelpers.h>
|
||||
|
||||
#include "plugins/PluginContainer.h"
|
||||
|
||||
#include "OculusHelpers.h"
|
||||
#include <oglplus/opt/list_init.hpp>
|
||||
#include <oglplus/shapes/vector.hpp>
|
||||
#include <oglplus/opt/list_init.hpp>
|
||||
#include <oglplus/shapes/obj_mesh.hpp>
|
||||
|
||||
#include "../OglplusHelpers.h"
|
||||
using namespace oglplus;
|
||||
|
||||
|
||||
#define RIFT_SDK_DISTORTION 0
|
||||
|
||||
const QString Oculus_0_5_DisplayPlugin::NAME("Oculus Rift (0.5)");
|
||||
|
||||
const QString & Oculus_0_5_DisplayPlugin::getName() const {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
|
||||
bool Oculus_0_5_DisplayPlugin::isSupported() const {
|
||||
if (!ovr_Initialize(nullptr)) {
|
||||
return false;
|
||||
|
@ -51,6 +50,83 @@ bool Oculus_0_5_DisplayPlugin::isSupported() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
static const char* OVR_DISTORTION_VS = R"SHADER(#version 120
|
||||
#pragma line __LINE__
|
||||
uniform vec2 EyeToSourceUVScale;
|
||||
uniform vec2 EyeToSourceUVOffset;
|
||||
uniform mat4 EyeRotationStart;
|
||||
uniform mat4 EyeRotationEnd;
|
||||
|
||||
attribute vec2 Position;
|
||||
attribute vec4 Color;
|
||||
attribute vec2 TexCoord0;
|
||||
attribute vec2 TexCoord1;
|
||||
attribute vec2 TexCoord2;
|
||||
|
||||
varying vec4 oColor;
|
||||
varying vec2 oTexCoord0;
|
||||
varying vec2 oTexCoord1;
|
||||
varying vec2 oTexCoord2;
|
||||
|
||||
void main() {
|
||||
gl_Position.x = Position.x;
|
||||
gl_Position.y = Position.y;
|
||||
gl_Position.z = 0.0;
|
||||
gl_Position.w = 1.0;
|
||||
// Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
|
||||
// These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
|
||||
vec3 TanEyeAngleR = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );
|
||||
vec3 TanEyeAngleG = vec3 ( TexCoord1.x, TexCoord1.y, 1.0 );
|
||||
vec3 TanEyeAngleB = vec3 ( TexCoord2.x, TexCoord2.y, 1.0 );
|
||||
// Apply the two 3x3 timewarp rotations to these vectors.
|
||||
vec3 TransformedRStart = (EyeRotationStart * vec4(TanEyeAngleR, 0)).xyz;
|
||||
vec3 TransformedGStart = (EyeRotationStart * vec4(TanEyeAngleG, 0)).xyz;
|
||||
vec3 TransformedBStart = (EyeRotationStart * vec4(TanEyeAngleB, 0)).xyz;
|
||||
vec3 TransformedREnd = (EyeRotationEnd * vec4(TanEyeAngleR, 0)).xyz;
|
||||
vec3 TransformedGEnd = (EyeRotationEnd * vec4(TanEyeAngleG, 0)).xyz;
|
||||
vec3 TransformedBEnd = (EyeRotationEnd * vec4(TanEyeAngleB, 0)).xyz;
|
||||
// And blend between them.
|
||||
vec3 TransformedR = mix ( TransformedRStart, TransformedREnd, Color.a );
|
||||
vec3 TransformedG = mix ( TransformedGStart, TransformedGEnd, Color.a );
|
||||
vec3 TransformedB = mix ( TransformedBStart, TransformedBEnd, Color.a );
|
||||
|
||||
// Project them back onto the Z=1 plane of the rendered images.
|
||||
float RecipZR = 1.0 / TransformedR.z;
|
||||
float RecipZG = 1.0 / TransformedG.z;
|
||||
float RecipZB = 1.0 / TransformedB.z;
|
||||
vec2 FlattenedR = vec2 ( TransformedR.x * RecipZR, TransformedR.y * RecipZR );
|
||||
vec2 FlattenedG = vec2 ( TransformedG.x * RecipZG, TransformedG.y * RecipZG );
|
||||
vec2 FlattenedB = vec2 ( TransformedB.x * RecipZB, TransformedB.y * RecipZB );
|
||||
|
||||
// These are now still in TanEyeAngle space.
|
||||
// Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
|
||||
vec2 SrcCoordR = FlattenedR * EyeToSourceUVScale + EyeToSourceUVOffset;
|
||||
vec2 SrcCoordG = FlattenedG * EyeToSourceUVScale + EyeToSourceUVOffset;
|
||||
vec2 SrcCoordB = FlattenedB * EyeToSourceUVScale + EyeToSourceUVOffset;
|
||||
oTexCoord0 = SrcCoordR;
|
||||
oTexCoord1 = SrcCoordG;
|
||||
oTexCoord2 = SrcCoordB;
|
||||
oColor = vec4(Color.r, Color.r, Color.r, Color.r); // Used for vignette fade.
|
||||
}
|
||||
|
||||
)SHADER";
|
||||
|
||||
static const char* OVR_DISTORTION_FS = R"SHADER(#version 120
|
||||
#pragma line __LINE__
|
||||
uniform sampler2D Texture0;
|
||||
#extension GL_ARB_shader_texture_lod : enable
|
||||
#extension GL_ARB_draw_buffers : enable
|
||||
#extension GL_EXT_gpu_shader4 : enable
|
||||
|
||||
varying vec4 oColor;
|
||||
varying vec2 oTexCoord0;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(0, 0, 0, 1); // texture2D(Texture0, oTexCoord0, 0.0);
|
||||
//gl_FragColor.a = 1.0;
|
||||
}
|
||||
)SHADER";
|
||||
|
||||
void Oculus_0_5_DisplayPlugin::activate(PluginContainer * container) {
|
||||
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||
Q_ASSERT(false);
|
||||
|
@ -68,10 +144,99 @@ void Oculus_0_5_DisplayPlugin::activate(PluginContainer * container) {
|
|||
|
||||
QSurfaceFormat format;
|
||||
initSurfaceFormat(format);
|
||||
_hmdWindow->setFlags(Qt::FramelessWindowHint);
|
||||
_hmdWindow->setFormat(format);
|
||||
_hmdWindow->create();
|
||||
//_hmdWindow->setGeometry(_hmd->WindowsPos.x, _hmd->WindowsPos.y, _hmd->Resolution.w, _hmd->Resolution.h);
|
||||
_hmdWindow->setGeometry(0, -1080, _hmd->Resolution.w, _hmd->Resolution.h);
|
||||
_hmdWindow->show();
|
||||
_hmdWindow->installEventFilter(this);
|
||||
_hmdWindow->makeCurrent();
|
||||
ovrRenderAPIConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig));
|
||||
config.Header.API = ovrRenderAPI_OpenGL;
|
||||
config.Header.BackBufferSize = _hmd->Resolution;
|
||||
config.Header.Multisample = 1;
|
||||
int distortionCaps = 0
|
||||
| ovrDistortionCap_Vignette
|
||||
| ovrDistortionCap_Overdrive
|
||||
| ovrDistortionCap_TimeWarp
|
||||
;
|
||||
|
||||
memset(_eyeTextures, 0, sizeof(ovrTexture) * 2);
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
auto& header = _eyeTextures[eye].Header;
|
||||
header.API = ovrRenderAPI_OpenGL;
|
||||
header.TextureSize = { (int)_desiredFramebufferSize.x, (int)_desiredFramebufferSize.y };
|
||||
header.RenderViewport.Size = header.TextureSize;
|
||||
header.RenderViewport.Size.w /= 2;
|
||||
if (eye == ovrEye_Right) {
|
||||
header.RenderViewport.Pos.x = header.RenderViewport.Size.w;
|
||||
}
|
||||
});
|
||||
#if RIFT_SDK_DISTORTION
|
||||
ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config, 0, _eyeFovs, nullptr);
|
||||
Q_ASSERT(result);
|
||||
#else
|
||||
compileProgram(_distortProgram, OVR_DISTORTION_VS, OVR_DISTORTION_FS);
|
||||
|
||||
auto uniforms = _distortProgram->ActiveUniforms();
|
||||
for (int i = 0; i < uniforms.Size(); ++i) {
|
||||
auto uniform = uniforms.At(i);
|
||||
qDebug() << uniform.Name().c_str() << " @ " << uniform.Index();
|
||||
if (uniform.Name() == String("EyeToSourceUVScale")) {
|
||||
_uniformScale = uniform.Index();
|
||||
} else if (uniform.Name() == String("EyeToSourceUVOffset")) {
|
||||
_uniformOffset = uniform.Index();
|
||||
} else if (uniform.Name() == String("EyeRotationStart")) {
|
||||
_uniformEyeRotStart = uniform.Index();
|
||||
} else if (uniform.Name() == String("EyeRotationEnd")) {
|
||||
_uniformEyeRotEnd = uniform.Index();
|
||||
}
|
||||
}
|
||||
|
||||
auto attribs = _distortProgram->ActiveAttribs();
|
||||
for (int i = 0; i < attribs.Size(); ++i) {
|
||||
auto attrib = attribs.At(i);
|
||||
qDebug() << attrib.Name().c_str() << " @ " << attrib.Index();
|
||||
if (attrib.Name() == String("Position")) {
|
||||
_attrPosition = attrib.Index();
|
||||
} else if (attrib.Name() == String("TexCoord0")) {
|
||||
_attrTexCoord0 = attrib.Index();
|
||||
} else if (attrib.Name() == String("TexCoord1")) {
|
||||
_attrTexCoord1 = attrib.Index();
|
||||
} else if (attrib.Name() == String("TexCoord2")) {
|
||||
_attrTexCoord2 = attrib.Index();
|
||||
}
|
||||
}
|
||||
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
ovrDistortionMesh meshData;
|
||||
ovrHmd_CreateDistortionMesh(_hmd, eye, _eyeFovs[eye], distortionCaps, &meshData);
|
||||
{
|
||||
auto& buffer = _eyeVertexBuffers[eye];
|
||||
buffer.reset(new oglplus::Buffer());
|
||||
buffer->Bind(Buffer::Target::Array);
|
||||
buffer->Data(Buffer::Target::Array, BufferData(meshData.VertexCount, meshData.pVertexData));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto& buffer = _eyeIndexBuffers[eye];
|
||||
buffer.reset(new oglplus::Buffer());
|
||||
buffer->Bind(Buffer::Target::ElementArray);
|
||||
buffer->Data(Buffer::Target::ElementArray, BufferData(meshData.IndexCount, meshData.pIndexData));
|
||||
_indexCount[eye] = meshData.IndexCount;
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
ovrHmd_DestroyDistortionMesh(&meshData);
|
||||
const auto& header = _eyeTextures[eye].Header;
|
||||
ovrHmd_GetRenderScaleAndOffset(_eyeFovs[eye], header.TextureSize, header.RenderViewport, _offsetAndScale[eye]);
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Oculus_0_5_DisplayPlugin::deactivate() {
|
||||
|
@ -85,8 +250,63 @@ void Oculus_0_5_DisplayPlugin::deactivate() {
|
|||
ovr_Shutdown();
|
||||
}
|
||||
|
||||
static ovrFrameTiming timing;
|
||||
|
||||
void Oculus_0_5_DisplayPlugin::preRender() {
|
||||
#if RIFT_SDK_DISTORTION
|
||||
ovrHmd_BeginFrame(_hmd, _frameIndex);
|
||||
#else
|
||||
timing = ovrHmd_BeginFrameTiming(_hmd, _frameIndex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Oculus_0_5_DisplayPlugin::preDisplay() {
|
||||
_hmdWindow->makeCurrent();
|
||||
}
|
||||
|
||||
void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||
++_frameIndex;
|
||||
|
||||
#if RIFT_SDK_DISTORTION
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
reinterpret_cast<ovrGLTexture&>(_eyeTextures[eye]).OGL.TexId = finalTexture;
|
||||
});
|
||||
ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures);
|
||||
#else
|
||||
ovr_WaitTillTime(timing.TimewarpPointSeconds);
|
||||
glViewport(0, 0, _hmd->Resolution.w, _hmd->Resolution.h);
|
||||
|
||||
//_distortProgram->Bind();
|
||||
glClearColor(1, 0, 1, 1);
|
||||
Context::Clear().ColorBuffer();
|
||||
Context::Disable(Capability::CullFace);
|
||||
_distortProgram->Bind();
|
||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||
glViewport(0, 0, _hmd->Resolution.w, _hmd->Resolution.h);
|
||||
// Generates internal compiler error on MSVC 12
|
||||
ovr_for_each_eye([&](ovrEyeType eye){
|
||||
glUniform2fv(_uniformOffset, 1, &_offsetAndScale[eye][0].x);
|
||||
glUniform2fv(_uniformScale, 1, &_offsetAndScale[eye][1].x);
|
||||
_eyeVertexBuffers[eye]->Bind(Buffer::Target::Array);
|
||||
glEnableVertexAttribArray(_attrPosition);
|
||||
glVertexAttribPointer(_attrPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ovrDistortionVertex), (void*)offsetof(ovrDistortionVertex, ScreenPosNDC));
|
||||
glEnableVertexAttribArray(_attrTexCoord0);
|
||||
glVertexAttribPointer(_attrTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(ovrDistortionVertex), (void*)offsetof(ovrDistortionVertex, TanEyeAnglesR));
|
||||
glEnableVertexAttribArray(_attrTexCoord1);
|
||||
glVertexAttribPointer(_attrTexCoord1, 2, GL_FLOAT, GL_FALSE, sizeof(ovrDistortionVertex), (void*)offsetof(ovrDistortionVertex, TanEyeAnglesG));
|
||||
glEnableVertexAttribArray(_attrTexCoord2);
|
||||
glVertexAttribPointer(_attrTexCoord2, 2, GL_FLOAT, GL_FALSE, sizeof(ovrDistortionVertex), (void*)offsetof(ovrDistortionVertex, TanEyeAnglesB));
|
||||
_eyeIndexBuffers[eye]->Bind(Buffer::Target::ElementArray);
|
||||
glDrawElements(GL_TRIANGLES, _indexCount[eye], GL_UNSIGNED_SHORT, 0);
|
||||
glDisableVertexAttribArray(_attrPosition);
|
||||
glDisableVertexAttribArray(_attrTexCoord0);
|
||||
glDisableVertexAttribArray(_attrTexCoord1);
|
||||
glDisableVertexAttribArray(_attrTexCoord2);
|
||||
});
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// Pass input events on to the application
|
||||
|
@ -96,12 +316,12 @@ bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
|
||||
/*
|
||||
The swapbuffer call here is only required if we want to mirror the content to the screen.
|
||||
However, it should only be done if we can reliably disable v-sync on the mirror surface,
|
||||
However, it should only be done if we can reliably disable v-sync on the mirror surface,
|
||||
otherwise the swapbuffer delay will interefere with the framerate of the headset
|
||||
*/
|
||||
*/
|
||||
void Oculus_0_5_DisplayPlugin::finishFrame() {
|
||||
swapBuffers();
|
||||
doneCurrent();
|
||||
_hmdWindow->swapBuffers();
|
||||
_hmdWindow->doneCurrent();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,12 +32,32 @@ public:
|
|||
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||
|
||||
protected:
|
||||
virtual void preRender() override;
|
||||
virtual void preDisplay() override;
|
||||
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
||||
// Do not perform swap in finish
|
||||
virtual void finishFrame() override;
|
||||
|
||||
private:
|
||||
ovrTexture _eyeTextures[2];
|
||||
#if RIFT_SDK_DISTORTION
|
||||
#else
|
||||
ovrVector2f _offsetAndScale[2][2];
|
||||
ProgramPtr _distortProgram;
|
||||
BufferPtr _eyeIndexBuffers[2];
|
||||
BufferPtr _eyeVertexBuffers[2];
|
||||
GLuint _indexCount[2];
|
||||
|
||||
GLuint _uniformScale{ -1 };
|
||||
GLuint _uniformOffset{ -1 };
|
||||
GLuint _uniformEyeRotStart{ -1 };
|
||||
GLuint _uniformEyeRotEnd{ -1 };
|
||||
GLuint _attrPosition{ -1 };
|
||||
GLuint _attrTexCoord0{ -1 };
|
||||
GLuint _attrTexCoord1{ -1 };
|
||||
GLuint _attrTexCoord2{ -1 };
|
||||
|
||||
#endif
|
||||
static const QString NAME;
|
||||
GlWindow* _hmdWindow;
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <oglplus/opt/list_init.hpp>
|
||||
#include <oglplus/shapes/obj_mesh.hpp>
|
||||
|
||||
#include "../OglplusHelpers.h"
|
||||
#include <OglplusHelpers.h>
|
||||
|
||||
|
||||
// A base class for FBO wrappers that need to use the Oculus C
|
||||
|
@ -245,13 +245,8 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
|
|||
_sceneFbo->Bound([&] {
|
||||
auto size = _sceneFbo->size;
|
||||
Context::Viewport(size.x, size.y);
|
||||
|
||||
_program->Bind();
|
||||
Mat4Uniform(*_program, "Projection").Set(mat4());
|
||||
Mat4Uniform(*_program, "ModelView").Set(mat4());
|
||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
drawUnitQuad();
|
||||
});
|
||||
|
||||
ovrLayerEyeFov& sceneLayer = getSceneLayer();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
batch.setModelTransform(transformToTopLeft);
|
||||
}
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||
|
||||
float scale = _lineHeight / _textRenderer->getFontSize();
|
||||
transformToTopLeft.setScale(scale); // Scale to have the correct line height
|
||||
|
|
|
@ -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),
|
||||
|
@ -64,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(""),
|
||||
|
@ -86,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);
|
||||
|
@ -96,13 +106,16 @@ 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;
|
||||
|
@ -111,7 +124,6 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
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;
|
||||
|
@ -120,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;
|
||||
}
|
||||
|
||||
|
@ -228,13 +240,16 @@ 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());
|
||||
|
@ -242,19 +257,18 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
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,
|
||||
|
@ -318,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
|
||||
|
@ -329,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]
|
||||
|
@ -343,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;
|
||||
|
||||
|
@ -404,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;
|
||||
|
||||
|
@ -414,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;
|
||||
}
|
||||
|
@ -451,7 +463,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
ignoreServerPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ignoreServerPacket) {
|
||||
overwriteLocalData = false;
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -468,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
|
||||
}
|
||||
|
@ -486,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) {
|
||||
|
@ -512,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();
|
||||
|
@ -521,47 +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_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_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);
|
||||
|
@ -569,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;
|
||||
}
|
||||
|
||||
|
@ -587,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
|
||||
|
@ -614,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);
|
||||
|
@ -624,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;
|
||||
}
|
||||
|
||||
|
@ -759,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) {
|
||||
|
@ -770,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) {
|
||||
|
@ -778,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) {
|
||||
|
@ -892,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);
|
||||
|
@ -917,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;
|
||||
|
||||
|
@ -947,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);
|
||||
|
@ -968,7 +1022,6 @@ 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);
|
||||
|
@ -983,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();
|
||||
|
@ -1343,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();
|
||||
|
@ -1397,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
|
||||
};
|
||||
|
@ -317,11 +317,16 @@ 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; }
|
||||
|
@ -358,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;
|
||||
|
||||
|
@ -379,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:
|
||||
|
||||
|
@ -426,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
|
||||
|
@ -457,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
|
||||
|
|
|
@ -38,7 +38,7 @@ EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM -
|
|||
EntityItemProperties::EntityItemProperties() :
|
||||
|
||||
CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE),
|
||||
CONSTRUCT_PROPERTY(position, 0),
|
||||
CONSTRUCT_PROPERTY(position, 0.0f),
|
||||
CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS),
|
||||
CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION),
|
||||
CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY),
|
||||
|
@ -73,7 +73,7 @@ CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED),
|
|||
CONSTRUCT_PROPERTY(textures, ""),
|
||||
CONSTRUCT_PROPERTY(animationSettings, ""),
|
||||
CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA),
|
||||
CONSTRUCT_PROPERTY(simulatorID, ENTITY_ITEM_DEFAULT_SIMULATOR_ID),
|
||||
CONSTRUCT_PROPERTY(simulationOwner, SimulationOwner()),
|
||||
CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT),
|
||||
CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
|
||||
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
|
||||
|
@ -100,7 +100,7 @@ CONSTRUCT_PROPERTY(sourceUrl, ""),
|
|||
CONSTRUCT_PROPERTY(lineWidth, LineEntityItem::DEFAULT_LINE_WIDTH),
|
||||
CONSTRUCT_PROPERTY(linePoints, QVector<glm::vec3>()),
|
||||
CONSTRUCT_PROPERTY(faceCamera, TextEntityItem::DEFAULT_FACE_CAMERA),
|
||||
|
||||
CONSTRUCT_PROPERTY(actionData, QByteArray()),
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
|
@ -183,7 +183,6 @@ QString EntityItemProperties::getAnimationSettings() const {
|
|||
|
||||
void EntityItemProperties::setCreated(QDateTime &v) {
|
||||
_created = v.toMSecsSinceEpoch() * 1000; // usec per msec
|
||||
qDebug() << "EntityItemProperties::setCreated QDateTime" << v << _created;
|
||||
}
|
||||
|
||||
void EntityItemProperties::debugDump() const {
|
||||
|
@ -289,8 +288,8 @@ void EntityItemProperties::setBackgroundModeFromString(const QString& background
|
|||
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
|
||||
CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DENSITY, density);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity);
|
||||
|
@ -324,7 +323,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures);
|
||||
CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SIMULATOR_ID, simulatorID);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SIMULATION_OWNER, simulationOwner);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXT, text);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight);
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
|
||||
|
@ -353,6 +352,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_HREF, href);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
|
||||
CHECK_PROPERTY_CHANGE(PROP_FACE_CAMERA, faceCamera);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ACTION_DATA, actionData);
|
||||
|
||||
changedProperties += _stage.getChangedProperties();
|
||||
changedProperties += _atmosphere.getChangedProperties();
|
||||
|
@ -419,7 +419,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(locked);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(textures);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(userData);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(simulatorID, getSimulatorIDAsString());
|
||||
//COPY_PROPERTY_TO_QSCRIPTVALUE(simulationOwner); // TODO: expose this for JSON saves?
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(text);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(textColor, getTextColor());
|
||||
|
@ -450,6 +450,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(href);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(description);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(faceCamera);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(actionData);
|
||||
|
||||
// Sitting properties support
|
||||
if (!skipDefaults) {
|
||||
|
@ -563,6 +564,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(faceCamera, bool, setFaceCamera);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(actionData, QByteArray, setActionData);
|
||||
|
||||
if (!honorReadOnly) {
|
||||
// this is used by the json reader to set things that we don't want javascript to able to affect.
|
||||
|
@ -570,7 +572,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec
|
||||
return result;
|
||||
});
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID);
|
||||
// TODO: expose this to QScriptValue for JSON saves?
|
||||
//COPY_PROPERTY_FROM_QSCRIPTVALUE(simulationOwner, ???, setSimulatorPriority);
|
||||
}
|
||||
|
||||
_stage.copyFromScriptValue(object, _defaultSettings);
|
||||
|
@ -705,6 +708,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
// PROP_PAGED_PROPERTY,
|
||||
// PROP_CUSTOM_PROPERTIES_INCLUDED,
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation());
|
||||
|
@ -727,7 +731,6 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, properties.getCollisionsWillMove());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
|
||||
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
|
||||
|
||||
|
@ -814,6 +817,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -959,7 +963,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
processedBytes += propertyFlags.getEncodedLength();
|
||||
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation);
|
||||
|
@ -982,7 +987,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
|
||||
|
||||
|
@ -1064,6 +1068,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -1098,6 +1103,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
}
|
||||
|
||||
void EntityItemProperties::markAllChanged() {
|
||||
_simulationOwnerChanged = true;
|
||||
_positionChanged = true;
|
||||
_dimensionsChanged = true;
|
||||
_rotationChanged = true;
|
||||
|
@ -1110,7 +1116,6 @@ void EntityItemProperties::markAllChanged() {
|
|||
_frictionChanged = true;
|
||||
_lifetimeChanged = true;
|
||||
_userDataChanged = true;
|
||||
_simulatorIDChanged = true;
|
||||
_scriptChanged = true;
|
||||
_scriptTimestampChanged = true;
|
||||
_collisionSoundURLChanged = true;
|
||||
|
@ -1175,9 +1180,8 @@ void EntityItemProperties::markAllChanged() {
|
|||
|
||||
_hrefChanged = true;
|
||||
_descriptionChanged = true;
|
||||
|
||||
_faceCameraChanged = true;
|
||||
|
||||
_actionDataChanged = true;
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
|
@ -1227,3 +1231,27 @@ bool EntityItemProperties::hasTerseUpdateChanges() const {
|
|||
// a TerseUpdate includes the transform and its derivatives
|
||||
return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::hasMiscPhysicsChanges() const {
|
||||
return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged
|
||||
|| _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged ||
|
||||
_compoundShapeURLChanged || _collisionsWillMoveChanged || _ignoreForCollisionsChanged;
|
||||
}
|
||||
|
||||
void EntityItemProperties::clearSimulationOwner() {
|
||||
_simulationOwner.clear();
|
||||
_simulationOwnerChanged = true;
|
||||
}
|
||||
|
||||
void EntityItemProperties::setSimulationOwner(const QUuid& id, uint8_t priority) {
|
||||
if (!_simulationOwner.matchesValidID(id) || _simulationOwner.getPriority() != priority) {
|
||||
_simulationOwner.set(id, priority);
|
||||
_simulationOwnerChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
|
||||
if (_simulationOwner.fromByteArray(data)) {
|
||||
_simulationOwnerChanged = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "EntityPropertyFlags.h"
|
||||
#include "SimulationOwner.h"
|
||||
#include "SkyboxPropertyGroup.h"
|
||||
#include "StagePropertyGroup.h"
|
||||
|
||||
|
@ -120,7 +121,7 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString);
|
||||
DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString);
|
||||
DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString);
|
||||
DEFINE_PROPERTY_REF(PROP_SIMULATOR_ID, SimulatorID, simulatorID, QUuid);
|
||||
DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString);
|
||||
DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
|
||||
|
@ -152,7 +153,8 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
|
||||
DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
|
||||
DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool);
|
||||
|
||||
DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray);
|
||||
|
||||
static QString getBackgroundModeString(BackgroundMode mode);
|
||||
|
||||
|
||||
|
@ -196,7 +198,7 @@ public:
|
|||
const QStringList& getTextureNames() const { return _textureNames; }
|
||||
void setTextureNames(const QStringList& value) { _textureNames = value; }
|
||||
|
||||
QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); }
|
||||
QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); }
|
||||
|
||||
void setVoxelDataDirty() { _voxelDataChanged = true; }
|
||||
|
||||
|
@ -205,6 +207,14 @@ public:
|
|||
void setCreated(QDateTime& v);
|
||||
|
||||
bool hasTerseUpdateChanges() const;
|
||||
bool hasMiscPhysicsChanges() const;
|
||||
|
||||
void clearSimulationOwner();
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const QByteArray& data);
|
||||
void promoteSimulationPriority(quint8 priority) { _simulationOwner.promotePriority(priority); }
|
||||
|
||||
void setActionDataDirty() { _actionDataChanged = true; }
|
||||
|
||||
private:
|
||||
QUuid _id;
|
||||
|
@ -284,7 +294,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulatorID, simulatorID, QUuid());
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner());
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, "");
|
||||
|
@ -304,7 +314,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, "");
|
||||
|
||||
properties.getStage().debugDump();
|
||||
properties.getAtmosphere().debugDump();
|
||||
properties.getSkybox().debugDump();
|
||||
|
|
|
@ -86,7 +86,7 @@ enum EntityPropertyList {
|
|||
|
||||
// available to all entities
|
||||
PROP_LOCKED,
|
||||
|
||||
|
||||
PROP_TEXTURES, // used by Model entities
|
||||
PROP_ANIMATION_SETTINGS, // used by Model entities
|
||||
PROP_USER_DATA, // all entities
|
||||
|
@ -100,11 +100,11 @@ enum EntityPropertyList {
|
|||
PROP_EMIT_STRENGTH,
|
||||
PROP_LOCAL_GRAVITY,
|
||||
PROP_PARTICLE_RADIUS,
|
||||
|
||||
|
||||
PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities
|
||||
PROP_MARKETPLACE_ID, // all entities
|
||||
PROP_ACCELERATION, // all entities
|
||||
PROP_SIMULATOR_ID, // all entities
|
||||
PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID
|
||||
PROP_NAME, // all entities
|
||||
PROP_COLLISION_SOUND_URL,
|
||||
PROP_RESTITUTION,
|
||||
|
@ -121,10 +121,11 @@ enum EntityPropertyList {
|
|||
// used by hyperlinks
|
||||
PROP_HREF,
|
||||
PROP_DESCRIPTION,
|
||||
|
||||
|
||||
PROP_FACE_CAMERA,
|
||||
PROP_SCRIPT_TIMESTAMP,
|
||||
|
||||
PROP_ACTION_DATA,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
|
|
@ -9,18 +9,20 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
|
||||
#include <VariantMapToScriptValue.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityTree.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "ModelEntityItem.h"
|
||||
#include "SimulationOwner.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
|
||||
EntityScriptingInterface::EntityScriptingInterface() :
|
||||
_entityTree(NULL)
|
||||
|
@ -61,16 +63,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
|
|||
}
|
||||
}
|
||||
|
||||
void bidForSimulationOwnership(EntityItemProperties& properties) {
|
||||
// We make a bid for simulation ownership by declaring our sessionID as simulation owner
|
||||
// in the outgoing properties. The EntityServer may accept the bid or might not.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
properties.setSimulatorID(myNodeID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
|
||||
|
||||
EntityItemProperties propertiesWithSimID = properties;
|
||||
|
@ -83,11 +75,15 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
_entityTree->lockForWrite();
|
||||
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
|
||||
if (entity) {
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
||||
bidForSimulationOwnership(propertiesWithSimID);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
|
||||
// and make note of it now, so we can act on it right away.
|
||||
entity->setSimulatorID(propertiesWithSimID.getSimulatorID());
|
||||
entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
success = false;
|
||||
|
@ -113,7 +109,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
if (entity) {
|
||||
results = entity->getProperties();
|
||||
|
||||
// TODO: improve sitting points and naturalDimensions in the future,
|
||||
// TODO: improve sitting points and naturalDimensions in the future,
|
||||
// for now we've included the old sitting points model behavior for entity types that are models
|
||||
// we've also added this hack for setting natural dimensions of models
|
||||
if (entity->getType() == EntityTypes::Model) {
|
||||
|
@ -146,23 +142,35 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper
|
|||
if (entity) {
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
properties.setType(entity->getType());
|
||||
if (properties.hasTerseUpdateChanges()) {
|
||||
bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
|
||||
bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;
|
||||
if (hasPhysicsChanges) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
|
||||
|
||||
if (entity->getSimulatorID() == myNodeID) {
|
||||
// we think we already own the simulation, so make sure to send ALL TerseUpdate properties
|
||||
entity->getAllTerseUpdateProperties(properties);
|
||||
if (hasTerseUpdateChanges) {
|
||||
entity->getAllTerseUpdateProperties(properties);
|
||||
}
|
||||
// TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object
|
||||
// is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update
|
||||
// and instead let the physics simulation decide when to send a terse update. This would remove
|
||||
// the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling
|
||||
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
|
||||
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
|
||||
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
|
||||
// simulation.
|
||||
|
||||
if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {
|
||||
// we re-assert our simulation ownership at a higher priority
|
||||
properties.setSimulationOwner(myNodeID,
|
||||
glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
|
||||
}
|
||||
} else {
|
||||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
// we make a bid for (or assert existing) simulation ownership
|
||||
properties.setSimulatorID(myNodeID);
|
||||
}
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
}
|
||||
|
@ -204,7 +212,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
|||
}
|
||||
|
||||
QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
|
||||
EntityItemID result;
|
||||
EntityItemID result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
EntityItemPointer closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
|
@ -264,8 +272,8 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlock
|
|||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
|
||||
Octree::lockType lockType,
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
|
||||
Octree::lockType lockType,
|
||||
bool precisionPicking) {
|
||||
|
||||
|
||||
|
@ -273,8 +281,8 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
if (_entityTree) {
|
||||
OctreeElement* element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
result.entityID = intersectedEntity->getEntityItemID();
|
||||
|
@ -318,15 +326,15 @@ bool EntityScriptingInterface::getSendPhysicsUpdates() const {
|
|||
}
|
||||
|
||||
|
||||
RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
||||
intersects(false),
|
||||
RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
||||
intersects(false),
|
||||
accurate(true), // assume it's accurate
|
||||
entityID(),
|
||||
properties(),
|
||||
distance(0),
|
||||
face(),
|
||||
entity(NULL)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
|
||||
|
@ -341,7 +349,7 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c
|
|||
|
||||
obj.setProperty("distance", value.distance);
|
||||
|
||||
QString faceName = "";
|
||||
QString faceName = "";
|
||||
// handle BoxFace
|
||||
switch (value.face) {
|
||||
case MIN_X_FACE:
|
||||
|
@ -446,35 +454,35 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line
|
|||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
|
||||
}
|
||||
|
||||
|
||||
EntityTypes::EntityType entityType = entity->getType();
|
||||
|
||||
|
||||
if (entityType != EntityTypes::Line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
|
||||
LineEntityItem* lineEntity = static_cast<LineEntityItem*>(entity.get());
|
||||
_entityTree->lockForWrite();
|
||||
bool success = actor(*lineEntity);
|
||||
entity->setLastEdited(now);
|
||||
entity->setLastBroadcast(now);
|
||||
_entityTree->unlock();
|
||||
|
||||
|
||||
_entityTree->lockForRead();
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
_entityTree->unlock();
|
||||
|
||||
|
||||
properties.setLinePointsDirty();
|
||||
properties.setLastEdited(now);
|
||||
|
||||
|
||||
|
||||
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
|
||||
return success;
|
||||
}
|
||||
|
@ -509,7 +517,6 @@ bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& poin
|
|||
{
|
||||
return lineEntity.appendPoint(point);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -537,6 +544,16 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
|
|||
|
||||
bool success = actor(simulation, entity);
|
||||
_entityTree->unlock();
|
||||
|
||||
// transmit the change
|
||||
_entityTree->lockForRead();
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
_entityTree->unlock();
|
||||
properties.setActionDataDirty();
|
||||
auto now = usecTimestampNow();
|
||||
properties.setLastEdited(now);
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -557,7 +574,14 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
if (actionType == ACTION_TYPE_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) {
|
||||
EntityActionPointer action = actionFactory->factory(simulation, actionType, actionID, entity, arguments);
|
||||
if (action) {
|
||||
entity->addAction(simulation, action);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -571,13 +595,39 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
|
||||
bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) {
|
||||
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
return entity->updateAction(simulation, actionID, arguments);
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
|
||||
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
return entity->removeAction(simulation, actionID);
|
||||
});
|
||||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
|
||||
QVector<QUuid> result;
|
||||
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
QList<QUuid> actionIDs = entity->getActionIDs();
|
||||
result = QVector<QUuid>::fromList(actionIDs);
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap EntityScriptingInterface::getActionArguments(const QUuid& entityID, const QUuid& actionID) {
|
||||
QVariantMap result;
|
||||
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
result = entity->getActionArguments(actionID);
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ public slots:
|
|||
Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value);
|
||||
Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value);
|
||||
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
|
||||
|
||||
|
||||
Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector<glm::vec3>& points);
|
||||
Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point);
|
||||
|
||||
|
@ -131,6 +131,8 @@ public slots:
|
|||
Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments);
|
||||
Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments);
|
||||
Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID);
|
||||
Q_INVOKABLE QVector<QUuid> getActionIDs(const QUuid& entityID);
|
||||
Q_INVOKABLE QVariantMap getActionArguments(const QUuid& entityID, const QUuid& actionID);
|
||||
|
||||
signals:
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
@ -165,7 +167,7 @@ private:
|
|||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking);
|
||||
|
||||
EntityTree* _entityTree;
|
||||
|
|
|
@ -260,3 +260,28 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addAction(EntityActionPointer action) {
|
||||
lock();
|
||||
_actionsToAdd += action;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeAction(const QUuid actionID) {
|
||||
lock();
|
||||
_actionsToRemove += actionID;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeActions(QList<QUuid> actionIDsToRemove) {
|
||||
lock();
|
||||
_actionsToRemove += actionIDsToRemove;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void EntitySimulation::applyActionChanges() {
|
||||
lock();
|
||||
_actionsToAdd.clear();
|
||||
_actionsToRemove.clear();
|
||||
unlock();
|
||||
}
|
||||
|
|
|
@ -57,10 +57,10 @@ public:
|
|||
|
||||
friend class EntityTree;
|
||||
|
||||
virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; }
|
||||
virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; }
|
||||
virtual void removeActions(QList<QUuid> actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; }
|
||||
virtual void applyActionChanges() { _actionsToAdd.clear(); _actionsToRemove.clear(); }
|
||||
virtual void addAction(EntityActionPointer action);
|
||||
virtual void removeAction(const QUuid actionID);
|
||||
virtual void removeActions(QList<QUuid> actionIDsToRemove);
|
||||
virtual void applyActionChanges();
|
||||
|
||||
protected: // these only called by the EntityTree?
|
||||
/// \param entity pointer to EntityItem to be added
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
#include "QVariantGLM.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "RecurseOctreeToMapOperator.h"
|
||||
|
||||
|
||||
const quint64 SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
|
||||
#include "LogHandler.h"
|
||||
|
||||
|
||||
EntityTree::EntityTree(bool shouldReaverage) :
|
||||
|
@ -34,6 +32,7 @@ EntityTree::EntityTree(bool shouldReaverage) :
|
|||
_simulation(NULL)
|
||||
{
|
||||
_rootElement = createNewElement();
|
||||
resetClientEditStats();
|
||||
}
|
||||
|
||||
EntityTree::~EntityTree() {
|
||||
|
@ -60,6 +59,8 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
|||
}
|
||||
_entityToElementMap.clear();
|
||||
Octree::eraseAllOctreeElements(createNewRoot);
|
||||
|
||||
resetClientEditStats();
|
||||
}
|
||||
|
||||
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
||||
|
@ -150,23 +151,34 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
|
|||
} else {
|
||||
if (getIsServer()) {
|
||||
bool simulationBlocked = !entity->getSimulatorID().isNull();
|
||||
if (properties.simulatorIDChanged()) {
|
||||
QUuid submittedID = properties.getSimulatorID();
|
||||
if (properties.simulationOwnerChanged()) {
|
||||
QUuid submittedID = properties.getSimulationOwner().getID();
|
||||
// a legit interface will only submit their own ID or NULL:
|
||||
if (submittedID.isNull()) {
|
||||
if (entity->getSimulatorID() == senderID) {
|
||||
// We only allow the simulation owner to clear their own simulationID's.
|
||||
simulationBlocked = false;
|
||||
properties.clearSimulationOwner(); // clear everything
|
||||
}
|
||||
// else: We assume the sender really did believe it was the simulation owner when it sent
|
||||
} else if (submittedID == senderID) {
|
||||
// the sender is trying to take or continue ownership
|
||||
if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) {
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
// the sender it taking ownership
|
||||
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
simulationBlocked = false;
|
||||
} else if (entity->getSimulatorID() == senderID) {
|
||||
// the sender is asserting ownership
|
||||
simulationBlocked = false;
|
||||
} else {
|
||||
// the sender is trying to steal ownership from another simulator
|
||||
// so we apply the ownership change filter
|
||||
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
|
||||
// so we apply the rules for ownership change:
|
||||
// (1) higher priority wins
|
||||
// (2) equal priority wins if ownership filter has expired except...
|
||||
uint8_t oldPriority = entity->getSimulationPriority();
|
||||
uint8_t newPriority = properties.getSimulationOwner().getPriority();
|
||||
if (newPriority > oldPriority ||
|
||||
(newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
|
||||
simulationBlocked = false;
|
||||
}
|
||||
}
|
||||
|
@ -174,12 +186,17 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
|
|||
// the entire update is suspect --> ignore it
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
simulationBlocked = senderID != entity->getSimulatorID();
|
||||
}
|
||||
if (simulationBlocked) {
|
||||
// squash the physics-related changes.
|
||||
properties.setSimulatorIDChanged(false);
|
||||
// squash ownership and physics-related changes.
|
||||
properties.setSimulationOwnerChanged(false);
|
||||
properties.setPositionChanged(false);
|
||||
properties.setRotationChanged(false);
|
||||
properties.setVelocityChanged(false);
|
||||
properties.setAngularVelocityChanged(false);
|
||||
properties.setAccelerationChanged(false);
|
||||
}
|
||||
}
|
||||
// else client accepts what the server says
|
||||
|
@ -232,7 +249,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
|
|||
if (getIsClient()) {
|
||||
// if our Node isn't allowed to create entities in this domain, don't try.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (!nodeList->getThisNodeCanRez()) {
|
||||
if (nodeList && !nodeList->getThisNodeCanRez()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -574,41 +591,61 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
|
||||
case PacketTypeEntityAdd:
|
||||
case PacketTypeEntityEdit: {
|
||||
quint64 startDecode = 0, endDecode = 0;
|
||||
quint64 startLookup = 0, endLookup = 0;
|
||||
quint64 startUpdate = 0, endUpdate = 0;
|
||||
quint64 startCreate = 0, endCreate = 0;
|
||||
quint64 startLogging = 0, endLogging = 0;
|
||||
|
||||
_totalEditMessages++;
|
||||
|
||||
EntityItemID entityItemID;
|
||||
EntityItemProperties properties;
|
||||
startDecode = usecTimestampNow();
|
||||
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength,
|
||||
processedBytes, entityItemID, properties);
|
||||
endDecode = usecTimestampNow();
|
||||
|
||||
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
||||
// an existing entity... handle appropriately
|
||||
if (validEditPacket) {
|
||||
// search for the entity by EntityItemID
|
||||
startLookup = usecTimestampNow();
|
||||
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
endLookup = usecTimestampNow();
|
||||
if (existingEntity && packetType == PacketTypeEntityEdit) {
|
||||
// if the EntityItem exists, then update it
|
||||
startLogging = usecTimestampNow();
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
endLogging = usecTimestampNow();
|
||||
|
||||
startUpdate = usecTimestampNow();
|
||||
updateEntity(entityItemID, properties, senderNode);
|
||||
existingEntity->markAsChangedOnServer();
|
||||
endUpdate = usecTimestampNow();
|
||||
_totalUpdates++;
|
||||
} else if (packetType == PacketTypeEntityAdd) {
|
||||
if (senderNode->getCanRez()) {
|
||||
// this is a new entity... assign a new entityID
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] adding entity.";
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
startCreate = usecTimestampNow();
|
||||
EntityItemPointer newEntity = addEntity(entityItemID, properties);
|
||||
endCreate = usecTimestampNow();
|
||||
_totalCreates++;
|
||||
if (newEntity) {
|
||||
newEntity->markAsChangedOnServer();
|
||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||
|
||||
startLogging = usecTimestampNow();
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:"
|
||||
<< newEntity->getEntityItemID();
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
endLogging = usecTimestampNow();
|
||||
|
||||
}
|
||||
} else {
|
||||
|
@ -616,9 +653,19 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
<< "] attempted to add an entity.";
|
||||
}
|
||||
} else {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("^Add or Edit failed.*");
|
||||
qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_totalDecodeTime += endDecode - startDecode;
|
||||
_totalLookupTime += endLookup - startLookup;
|
||||
_totalUpdateTime += endUpdate - startUpdate;
|
||||
_totalCreateTime += endCreate - startCreate;
|
||||
_totalLoggingTime += endLogging - startLogging;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1076,3 +1123,29 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EntityTree::resetClientEditStats() {
|
||||
_treeResetTime = usecTimestampNow();
|
||||
_maxEditDelta = 0;
|
||||
_totalEditDeltas = 0;
|
||||
_totalTrackedEdits = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead) {
|
||||
// we don't want to track all edit deltas, just those edits that have happend
|
||||
// since we connected to this domain. This will filter out all previously created
|
||||
// content and only track new edits
|
||||
if (lastEditedTime > _treeResetTime) {
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceEdit = now - lastEditedTime;
|
||||
|
||||
_totalEditDeltas += sinceEdit;
|
||||
_totalEditBytes += bytesRead;
|
||||
_totalTrackedEdits++;
|
||||
if (sinceEdit > _maxEditDelta) {
|
||||
_maxEditDelta = sinceEdit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,6 +168,31 @@ public:
|
|||
|
||||
float getContentsLargestDimension();
|
||||
|
||||
virtual void resetEditStats() {
|
||||
_totalEditMessages = 0;
|
||||
_totalUpdates = 0;
|
||||
_totalCreates = 0;
|
||||
_totalDecodeTime = 0;
|
||||
_totalLookupTime = 0;
|
||||
_totalUpdateTime = 0;
|
||||
_totalCreateTime = 0;
|
||||
_totalLoggingTime = 0;
|
||||
}
|
||||
|
||||
virtual quint64 getAverageDecodeTime() const { return _totalEditMessages == 0 ? 0 : _totalDecodeTime / _totalEditMessages; }
|
||||
virtual quint64 getAverageLookupTime() const { return _totalEditMessages == 0 ? 0 : _totalLookupTime / _totalEditMessages; }
|
||||
virtual quint64 getAverageUpdateTime() const { return _totalUpdates == 0 ? 0 : _totalUpdateTime / _totalUpdates; }
|
||||
virtual quint64 getAverageCreateTime() const { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; }
|
||||
virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; }
|
||||
|
||||
void trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead);
|
||||
quint64 getAverageEditDeltas() const
|
||||
{ return _totalTrackedEdits == 0 ? 0 : _totalEditDeltas / _totalTrackedEdits; }
|
||||
quint64 getAverageEditBytes() const
|
||||
{ return _totalTrackedEdits == 0 ? 0 : _totalEditBytes / _totalTrackedEdits; }
|
||||
quint64 getMaxEditDelta() const { return _maxEditDelta; }
|
||||
quint64 getTotalTrackedEdits() const { return _totalTrackedEdits; }
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
|
@ -202,6 +227,25 @@ private:
|
|||
|
||||
bool _wantEditLogging = false;
|
||||
void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL);
|
||||
|
||||
|
||||
// some performance tracking properties - only used in server trees
|
||||
int _totalEditMessages = 0;
|
||||
int _totalUpdates = 0;
|
||||
int _totalCreates = 0;
|
||||
quint64 _totalDecodeTime = 0;
|
||||
quint64 _totalLookupTime = 0;
|
||||
quint64 _totalUpdateTime = 0;
|
||||
quint64 _totalCreateTime = 0;
|
||||
quint64 _totalLoggingTime = 0;
|
||||
|
||||
// these performance statistics are only used in the client
|
||||
void resetClientEditStats();
|
||||
int _totalTrackedEdits = 0;
|
||||
quint64 _totalEditBytes = 0;
|
||||
quint64 _totalEditDeltas = 0;
|
||||
quint64 _maxEditDelta = 0;
|
||||
quint64 _treeResetTime = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue