mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 04:03:59 +02:00
merged and normals flipping
This commit is contained in:
commit
0ce6737b34
256 changed files with 8859 additions and 6368 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.
|
||||
|
|
82
assignment-client/src/AssignmentAction.cpp
Normal file
82
assignment-client/src/AssignmentAction.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// 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, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityActionInterface(type, id),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentAction::~AssignmentAction() {
|
||||
}
|
||||
|
||||
void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const {
|
||||
simulation->removeAction(_id);
|
||||
}
|
||||
|
||||
QByteArray AssignmentAction::serialize() const {
|
||||
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.";
|
||||
}
|
53
assignment-client/src/AssignmentAction.h
Normal file
53
assignment-client/src/AssignmentAction.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// 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, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
|
||||
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() const;
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
|
||||
private:
|
||||
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
|
49
assignment-client/src/AssignmentActionFactory.cpp
Normal file
49
assignment-client/src/AssignmentActionFactory.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// 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, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
return (EntityActionPointer) new AssignmentAction(type, id, ownerEntity);
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
|
||||
const 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(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;
|
||||
}
|
29
assignment-client/src/AssignmentActionFactory.h
Normal file
29
assignment-client/src/AssignmentActionFactory.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// 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(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual EntityActionPointer factoryBA(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();
|
||||
|
|
|
@ -760,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>";
|
||||
|
|
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)
|
||||
|
|
7
cmake/externals/oglplus/CMakeLists.txt
vendored
7
cmake/externals/oglplus/CMakeLists.txt
vendored
|
@ -4,14 +4,17 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
GIT_REPOSITORY https://github.com/matus-chochlik/oglplus.git
|
||||
GIT_TAG a2681383928b1166f176512cbe0f95e96fe68d08
|
||||
URL http://iweb.dl.sourceforge.net/project/oglplus/oglplus-0.63.x/oglplus-0.63.0.zip
|
||||
URL_MD5 de984ab245b185b45c87415c0e052135
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include ${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)
|
|
@ -9,9 +9,7 @@
|
|||
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,
|
||||
|
@ -22,44 +20,47 @@ 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
|
||||
});
|
||||
function deleteAnimationTestEntitites() {
|
||||
var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
if (properties.name == "AnimationTest") {
|
||||
Entities.deleteEntity(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteAnimationTestEntitites();
|
||||
|
||||
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
|
||||
}));
|
||||
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",
|
||||
name: "AnimationTest",
|
||||
position: Vec3.sum(center, position),
|
||||
dimensions: { x: radius, y: radius, z: radius },
|
||||
color: color,
|
||||
ignoreCollisions: true,
|
||||
collisionsWillMove: false
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
|
||||
function scriptEnding() {
|
||||
Entities.deleteEntity(planet);
|
||||
for (var i = 0; i < moons.length; i++) {
|
||||
Entities.deleteEntity(moons[i]);
|
||||
}
|
||||
|
@ -70,22 +71,20 @@ 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;
|
||||
updateCount++;
|
||||
if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 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);
|
||||
Script.scriptEnding.connect(deleteAnimationTestEntitites);
|
||||
|
|
58
examples/cubePerfTest.js
Normal file
58
examples/cubePerfTest.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// 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 SIDE_SIZE = 10;
|
||||
|
||||
var center = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
var PARTICLE_MIN_SIZE = 2.50;
|
||||
var PARTICLE_MAX_SIZE = 2.50;
|
||||
var LIFETIME = 600;
|
||||
var boxes = [];
|
||||
|
||||
var ids = Entities.findEntities({ x: 512, y: 512, z: 512 }, 50);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
if (properties.name == "PerfTest") {
|
||||
Entities.deleteEntity(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create initial test particles that will move according to gravity from the planets
|
||||
for (var x = 0; x < SIDE_SIZE; x++) {
|
||||
for (var y = 0; y < SIDE_SIZE; y++) {
|
||||
for (var z = 0; z < SIDE_SIZE; z++) {
|
||||
var gray = Math.random() * 155;
|
||||
var cube = Math.random() > 0.5;
|
||||
var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
|
||||
var position = { x: 512 + x * 0.2, y: 512 + y * 0.2, z: 512 + z * 0.2};
|
||||
var radius = Math.random() * 0.1;
|
||||
boxes.push(Entities.addEntity({
|
||||
type: cube ? "Box" : "Sphere",
|
||||
name: "PerfTest",
|
||||
position: position,
|
||||
dimensions: { x: radius, y: radius, z: radius },
|
||||
color: color,
|
||||
ignoreCollisions: true,
|
||||
collisionsWillMove: false,
|
||||
lifetime: LIFETIME
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
for (var i = 0; i < boxes.length; i++) {
|
||||
//Entities.deleteEntity(boxes[i]);
|
||||
}
|
||||
}
|
||||
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);
|
|
@ -12,7 +12,6 @@ Script.load("progress.js");
|
|||
Script.load("edit.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
Script.load("inspect.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("grab.js");
|
||||
|
|
25
examples/example/games/airHockey.js
Normal file → Executable file
25
examples/example/games/airHockey.js
Normal file → Executable file
|
@ -83,6 +83,30 @@ var puck_name_index = 2;
|
|||
var light_name_index = 3;
|
||||
var floor_name_index = 4;
|
||||
|
||||
//Create Spawn and Del. Button Vars.
|
||||
|
||||
function updateButtonPosition() {
|
||||
Overlays.editOverlay(spawnButton, {
|
||||
x: screenSize.x / 2 + PADDING,
|
||||
y: screenSize.y - (BUTTON_SIZE * 2 + PADDING),
|
||||
});
|
||||
Overlays.editOverlay(deleteButton, {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE,
|
||||
y: screenSize.y - (BUTTON_SIZE * 2 + PADDING),
|
||||
});
|
||||
}
|
||||
|
||||
function onScriptUpdate() {
|
||||
var oldScreenSize = screenSize;
|
||||
|
||||
screenSize = Controller.getViewportDimensions();
|
||||
|
||||
if (screenSize.x !== oldScreenSize.x || screenSize.y !== oldScreenSize.y) {
|
||||
updateButtonPosition();
|
||||
}
|
||||
}
|
||||
|
||||
screenSize = Controller.getViewportDimensions();
|
||||
|
||||
var deleteButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE,
|
||||
|
@ -112,6 +136,7 @@ var spawnButton = Overlays.addOverlay("image", {
|
|||
alpha: 1
|
||||
});
|
||||
|
||||
Script.update.connect(onScriptUpdate);
|
||||
|
||||
|
||||
var floor, edge1, edge2, edge3a, edge3b, edge4a, edge4b, light;
|
||||
|
|
70
examples/example/games/make-dummy.js
Normal file
70
examples/example/games/make-dummy.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// make-dummy.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Makes a boxing-dummy that responds to collisions.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
//
|
||||
"use strict";
|
||||
/*jslint vars: true*/
|
||||
var Overlays, Entities, Controller, Script, MyAvatar, Vec3; // Referenced globals provided by High Fidelity.
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var rezButton = Overlays.addOverlay("image", {
|
||||
x: 100,
|
||||
y: 350,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
|
||||
if (clickedOverlay === rezButton) {
|
||||
var boxId;
|
||||
|
||||
var position = Vec3.sum(MyAvatar.position, {x: 1.0, y: 0.4, z: 0.0});
|
||||
boxId = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "dummy",
|
||||
position: position,
|
||||
dimensions: {x: 0.3, y: 0.7, z: 0.3},
|
||||
gravity: {x: 0.0, y: -3.0, z: 0.0},
|
||||
damping: 0.2,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
||||
var pointToOffsetFrom = Vec3.sum(position, {x: 0.0, y: 2.0, z: 0.0});
|
||||
Entities.addAction("offset", boxId, {pointToOffsetFrom: pointToOffsetFrom,
|
||||
linearDistance: 2.0,
|
||||
// linearTimeScale: 0.005
|
||||
linearTimeScale: 0.1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(rezButton);
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
322
examples/example/games/sword.js
Normal file
322
examples/example/games/sword.js
Normal file
|
@ -0,0 +1,322 @@
|
|||
// stick.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Allow avatar to hold a stick
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
"use strict";
|
||||
/*jslint vars: true*/
|
||||
var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar, Settings; // Referenced globals provided by High Fidelity.
|
||||
Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
|
||||
|
||||
var hand = Settings.getValue("highfidelity.sword.hand", "right");
|
||||
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||
var controllerID;
|
||||
var controllerActive;
|
||||
var stickID = null;
|
||||
var actionID = nullActionID;
|
||||
var targetIDs = [];
|
||||
var dimensions = { x: 0.3, y: 0.15, z: 2.0 };
|
||||
var BUTTON_SIZE = 32;
|
||||
|
||||
var stickModel = "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx";
|
||||
var swordModel = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx";
|
||||
var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.obj";
|
||||
var swordCollisionSoundURL = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/swordStrike1.wav";
|
||||
var avatarCollisionSoundURL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
|
||||
var whichModel = "sword";
|
||||
var originalAvatarCollisionSound;
|
||||
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar", function () {
|
||||
return {x: 100, y: 380};
|
||||
});
|
||||
|
||||
var SWORD_IMAGE = "http://s3.amazonaws.com/hifi-public/images/billiardsReticle.png"; // Toggle between brandishing/sheathing sword (creating if necessary)
|
||||
var TARGET_IMAGE = "http://s3.amazonaws.com/hifi-public/images/puck.png"; // Create a target dummy
|
||||
var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; // Remove sword and all target dummies.f
|
||||
var SWITCH_HANDS_IMAGE = "http://s3.amazonaws.com/hifi-public/images/up-arrow.svg"; // Toggle left vs right hand. Persists in settings.
|
||||
var swordButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: SWORD_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var targetButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: TARGET_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var switchHandsButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: SWITCH_HANDS_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var cleanupButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: CLEANUP_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var flasher;
|
||||
function clearFlash() {
|
||||
if (!flasher) {
|
||||
return;
|
||||
}
|
||||
Script.clearTimeout(flasher.timer);
|
||||
Overlays.deleteOverlay(flasher.overlay);
|
||||
flasher = null;
|
||||
}
|
||||
function flash(color) {
|
||||
clearFlash();
|
||||
flasher = {};
|
||||
flasher.overlay = Overlays.addOverlay("text", {
|
||||
backgroundColor: color,
|
||||
backgroundAlpha: 0.7,
|
||||
width: Window.innerWidth,
|
||||
height: Window.innerHeight
|
||||
});
|
||||
flasher.timer = Script.setTimeout(clearFlash, 500);
|
||||
}
|
||||
|
||||
var health = 100;
|
||||
var display2d, display3d;
|
||||
function trackAvatarWithText() {
|
||||
Entities.editEntity(display3d, {
|
||||
position: Vec3.sum(MyAvatar.position, {x: 0, y: 1.5, z: 0}),
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, 180, 0))
|
||||
});
|
||||
}
|
||||
function updateDisplay() {
|
||||
var text = health.toString();
|
||||
if (!display2d) {
|
||||
health = 100;
|
||||
display2d = Overlays.addOverlay("text", {
|
||||
text: text,
|
||||
font: { size: 20 },
|
||||
color: {red: 0, green: 255, blue: 0},
|
||||
backgroundColor: {red: 100, green: 100, blue: 100}, // Why doesn't this and the next work?
|
||||
backgroundAlpha: 0.9,
|
||||
x: toolBar.x - 5, // I'd like to add the score to the toolBar and have it drag with it, but toolBar doesn't support text (just buttons).
|
||||
y: toolBar.y - 30 // So next best thing is to position it each time as if it were on top.
|
||||
});
|
||||
display3d = Entities.addEntity({
|
||||
name: MyAvatar.displayName + " score",
|
||||
textColor: {red: 255, green: 255, blue: 255},
|
||||
type: "Text",
|
||||
text: text,
|
||||
lineHeight: 0.14,
|
||||
backgroundColor: {red: 64, green: 64, blue: 64},
|
||||
dimensions: {x: 0.3, y: 0.2, z: 0.01},
|
||||
});
|
||||
Script.update.connect(trackAvatarWithText);
|
||||
} else {
|
||||
Overlays.editOverlay(display2d, {text: text});
|
||||
Entities.editEntity(display3d, {text: text});
|
||||
}
|
||||
}
|
||||
function removeDisplay() {
|
||||
if (display2d) {
|
||||
Overlays.deleteOverlay(display2d);
|
||||
display2d = null;
|
||||
Script.update.disconnect(trackAvatarWithText);
|
||||
Entities.deleteEntity(display3d);
|
||||
display3d = null;
|
||||
}
|
||||
}
|
||||
function computeEnergy(collision, entityID) {
|
||||
var id = entityID || collision.idA || collision.idB;
|
||||
var entity = id && Entities.getEntityProperties(id);
|
||||
var mass = entity ? (entity.density * entity.dimensions.x * entity.dimensions.y * entity.dimensions.z) : 1;
|
||||
var linearVelocityChange = Vec3.length(collision.velocityChange);
|
||||
var energy = 0.5 * mass * linearVelocityChange * linearVelocityChange;
|
||||
return Math.min(Math.max(1.0, Math.round(energy)), 20);
|
||||
}
|
||||
function gotHit(collision) {
|
||||
var energy = computeEnergy(collision);
|
||||
print("Got hit - " + energy + " from " + collision.idA + " " + collision.idB);
|
||||
health -= energy;
|
||||
flash({red: 255, green: 0, blue: 0});
|
||||
updateDisplay();
|
||||
}
|
||||
function scoreHit(idA, idB, collision) {
|
||||
var energy = computeEnergy(collision, idA);
|
||||
print("Score + " + energy + " from " + JSON.stringify(idA) + " " + JSON.stringify(idB));
|
||||
health += energy;
|
||||
flash({red: 0, green: 255, blue: 0});
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function isFighting() {
|
||||
return stickID && (actionID !== nullActionID);
|
||||
}
|
||||
|
||||
function initControls() {
|
||||
print("Sword hand is " + hand);
|
||||
if (hand === "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
}
|
||||
}
|
||||
var inHand = false;
|
||||
function positionStick(stickOrientation) {
|
||||
var reorient = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
var baseOffset = {x: -dimensions.z * 0.8, y: 0, z: 0};
|
||||
var offset = Vec3.multiplyQbyV(reorient, baseOffset);
|
||||
stickOrientation = Quat.multiply(reorient, stickOrientation);
|
||||
inHand = false;
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: offset,
|
||||
relativeRotation: stickOrientation
|
||||
});
|
||||
}
|
||||
function resetToHand() { // Maybe coordinate with positionStick?
|
||||
if (inHand) { // Optimization: bail if we're already inHand.
|
||||
return;
|
||||
}
|
||||
print('Reset to hand');
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
hand: hand, // It should not be necessary to repeat these two, but there seems to be a bug in that that
|
||||
timeScale: 0.05 // they do not retain their earlier values if you don't repeat them.
|
||||
});
|
||||
inHand = true;
|
||||
}
|
||||
function mouseMoveEvent(event) {
|
||||
if (event.deviceID) { // Not a MOUSE mouse event, but a (e.g., hydra) mouse event, with x/y that is not meaningful for us.
|
||||
resetToHand(); // Can only happen when controller is uncradled, so let's drive with that, resetting our attachement.
|
||||
return;
|
||||
}
|
||||
controllerActive = (Vec3.length(Controller.getSpatialControlPosition(controllerID)) > 0);
|
||||
//print("Mouse move with hand controller " + (controllerActive ? "active" : "inactive") + JSON.stringify(event));
|
||||
if (controllerActive || !isFighting()) {
|
||||
print('Attempting attachment reset');
|
||||
resetToHand();
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
var windowCenterY = Window.innerHeight / 2;
|
||||
var mouseXCenterOffset = event.x - windowCenterX;
|
||||
var mouseYCenterOffset = event.y - windowCenterY;
|
||||
var mouseXRatio = mouseXCenterOffset / windowCenterX;
|
||||
var mouseYRatio = mouseYCenterOffset / windowCenterY;
|
||||
|
||||
var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * 90, mouseXRatio * 90, 0);
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
function removeSword() {
|
||||
if (stickID) {
|
||||
print('deleting action ' + actionID + ' and entity ' + stickID);
|
||||
Entities.deleteAction(stickID, actionID);
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
actionID = nullActionID;
|
||||
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.disconnect(gotHit);
|
||||
// removeEventhHandler happens automatically when the entity is deleted.
|
||||
}
|
||||
inHand = false;
|
||||
if (originalAvatarCollisionSound !== undefined) {
|
||||
MyAvatar.collisionSoundURL = originalAvatarCollisionSound;
|
||||
}
|
||||
removeDisplay();
|
||||
}
|
||||
function cleanUp(leaveButtons) {
|
||||
removeSword();
|
||||
targetIDs.forEach(function (id) {
|
||||
Entities.deleteAction(id.entity, id.action);
|
||||
Entities.deleteEntity(id.entity);
|
||||
});
|
||||
targetIDs = [];
|
||||
if (!leaveButtons) {
|
||||
toolBar.cleanup();
|
||||
}
|
||||
}
|
||||
function makeSword() {
|
||||
initControls();
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: swordModel,
|
||||
compoundShapeURL: swordCollisionShape,
|
||||
dimensions: dimensions,
|
||||
position: (hand === 'right') ? MyAvatar.getRightPalmPosition() : MyAvatar.getLeftPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: 0.1,
|
||||
collisionSoundURL: swordCollisionSoundURL,
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
hand: hand,
|
||||
timeScale: 0.05
|
||||
});
|
||||
if (actionID === nullActionID) {
|
||||
print('*** FAILED TO MAKE SWORD ACTION ***');
|
||||
cleanUp();
|
||||
}
|
||||
if (originalAvatarCollisionSound === undefined) {
|
||||
originalAvatarCollisionSound = MyAvatar.collisionSoundURL; // We won't get MyAvatar.collisionWithEntity unless there's a sound URL. (Bug.)
|
||||
SoundCache.getSound(avatarCollisionSoundURL); // Interface does not currently "preload" this? (Bug?)
|
||||
}
|
||||
MyAvatar.collisionSoundURL = avatarCollisionSoundURL;
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.connect(gotHit);
|
||||
Script.addEventHandler(stickID, 'collisionWithEntity', scoreHit);
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function onClick(event) {
|
||||
switch (Overlays.getOverlayAtPoint(event)) {
|
||||
case swordButton:
|
||||
if (!stickID) {
|
||||
makeSword();
|
||||
} else {
|
||||
removeSword();
|
||||
}
|
||||
break;
|
||||
case targetButton:
|
||||
var position = Vec3.sum(MyAvatar.position, {x: 1.0, y: 0.4, z: 0.0});
|
||||
var boxId = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "dummy",
|
||||
position: position,
|
||||
dimensions: {x: 0.3, y: 0.7, z: 0.3},
|
||||
gravity: {x: 0.0, y: -3.0, z: 0.0},
|
||||
damping: 0.2,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
||||
var pointToOffsetFrom = Vec3.sum(position, {x: 0.0, y: 2.0, z: 0.0});
|
||||
var action = Entities.addAction("offset", boxId, {pointToOffsetFrom: pointToOffsetFrom,
|
||||
linearDistance: 2.0,
|
||||
// linearTimeScale: 0.005
|
||||
linearTimeScale: 0.1
|
||||
});
|
||||
targetIDs.push({entity: boxId, action: action});
|
||||
break;
|
||||
case switchHandsButton:
|
||||
cleanUp('leaveButtons');
|
||||
hand = hand === "right" ? "left" : "right";
|
||||
Settings.setValue("highfidelity.sword.hand", hand);
|
||||
makeSword();
|
||||
break;
|
||||
case cleanupButton:
|
||||
cleanUp('leaveButtons');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mousePressEvent.connect(onClick);
|
|
@ -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);
|
||||
|
|
73
examples/stick-hydra.js
Normal file
73
examples/stick-hydra.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
// stick-hydra.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-7-9
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Allow avatar to hold a stick and control it with a hand-tracker
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var hand = "left";
|
||||
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||
var controllerID;
|
||||
var controllerActive;
|
||||
var stickID = null;
|
||||
var actionID = nullActionID;
|
||||
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.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 0.0, y: 90.0, z: 0.0}),
|
||||
hand: hand,
|
||||
timeScale: 0.15});
|
||||
if (actionID == nullActionID) {
|
||||
cleanUp();
|
||||
}
|
||||
makingNewStick = false;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
|
||||
function cleanUp() {
|
||||
if (stickID) {
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initControls(){
|
||||
if (hand == "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
makeNewStick();
|
|
@ -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() {
|
||||
|
|
|
@ -2,6 +2,32 @@ var controlHeld = false;
|
|||
var shiftHeld = false;
|
||||
|
||||
|
||||
function attemptVoxelChange(intersection) {
|
||||
var ids = Entities.findEntities(intersection.intersection, 10);
|
||||
var success = false;
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
if (controlHeld) {
|
||||
// hold control to erase a sphere
|
||||
if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0)) {
|
||||
success = true;
|
||||
}
|
||||
} else if (shiftHeld) {
|
||||
// hold shift to set all voxels to 255
|
||||
if (Entities.setAllVoxels(id, 255)) {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
// no modifier key means to add a sphere
|
||||
if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
|
@ -9,20 +35,21 @@ function mousePressEvent(event) {
|
|||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
// var props = Entities.getEntityProperties(intersection.entityID);
|
||||
|
||||
// we've used a picking ray to decide where to add the new sphere of voxels. If we pick nothing
|
||||
// or if we pick a non-PolyVox entity, we fall through to the next picking attempt.
|
||||
if (intersection.intersects) {
|
||||
var ids = Entities.findEntities(intersection.intersection, 10);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
if (controlHeld) {
|
||||
Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0);
|
||||
} else if (shiftHeld) {
|
||||
Entities.setAllVoxels(id, 255);
|
||||
} else {
|
||||
Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255);
|
||||
}
|
||||
if (attemptVoxelChange(intersection)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if the PolyVox entity is empty, we can't pick against its voxel. try picking against its
|
||||
// bounding box, instead.
|
||||
intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking
|
||||
if (intersection.intersects) {
|
||||
attemptVoxelChange(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
inline bool extraDebugging() { return _extraDebugging; }
|
||||
inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; }
|
||||
|
||||
virtual void addMessage(QString) = 0;
|
||||
virtual void addMessage(const QString&) = 0;
|
||||
virtual QString getLogData() = 0;
|
||||
virtual void locateLog() = 0;
|
||||
|
||||
|
@ -32,7 +32,7 @@ signals:
|
|||
void logReceived(QString message);
|
||||
|
||||
private:
|
||||
bool _extraDebugging;
|
||||
bool _extraDebugging{ false };
|
||||
};
|
||||
|
||||
#endif // hifi_AbstractLoggerInterface_h
|
||||
|
|
|
@ -394,6 +394,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
static_cast<NodeList*>(dependency)->deleteLater();
|
||||
});
|
||||
|
||||
// setup a timer for domain-server check ins
|
||||
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
|
||||
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
||||
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
// put the NodeList and datagram processing on the node thread
|
||||
nodeList->moveToThread(nodeThread);
|
||||
|
||||
|
@ -887,12 +892,6 @@ void Application::paintGL() {
|
|||
|
||||
{
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
/*
|
||||
gpu::Context context(new gpu::GLBackend());
|
||||
RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
*/
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
|
@ -968,6 +967,8 @@ void Application::paintGL() {
|
|||
} else if (TV3DManager::isConnected()) {
|
||||
TV3DManager::display(&renderArgs, _myCamera);
|
||||
} else {
|
||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||
|
||||
DependencyManager::get<GlowEffect>()->prepare(&renderArgs);
|
||||
|
||||
// Viewport is assigned to the size of the framebuffer
|
||||
|
@ -982,7 +983,7 @@ void Application::paintGL() {
|
|||
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
}
|
||||
|
||||
renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
|
||||
|
@ -1002,6 +1003,7 @@ void Application::paintGL() {
|
|||
|
||||
|
||||
if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
|
||||
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
|
||||
_glWidget->swapBuffers();
|
||||
}
|
||||
|
||||
|
@ -1014,6 +1016,7 @@ void Application::paintGL() {
|
|||
|
||||
void Application::runTests() {
|
||||
runTimingTests();
|
||||
runUnitTests();
|
||||
}
|
||||
|
||||
void Application::audioMuteToggled() {
|
||||
|
@ -1050,6 +1053,7 @@ void Application::resetCameras(Camera& camera, const glm::uvec2& size) {
|
|||
}
|
||||
|
||||
void Application::resizeGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
|
||||
// Otherwise, it must rebuild the FBOs
|
||||
QSize renderSize;
|
||||
|
@ -1182,7 +1186,6 @@ bool Application::event(QEvent* event) {
|
|||
}
|
||||
|
||||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
|
||||
event->accept();
|
||||
|
@ -1526,6 +1529,7 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
}
|
||||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
// Used by application overlay to determine how to draw cursor(s)
|
||||
_lastMouseMoveWasSimulated = deviceID > 0;
|
||||
if (!_lastMouseMoveWasSimulated) {
|
||||
|
@ -1781,12 +1785,10 @@ void Application::checkFPS() {
|
|||
_frameCount = 0;
|
||||
_datagramProcessor->resetCounters();
|
||||
_timerStart.start();
|
||||
|
||||
// ask the node list to check in with the domain server
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
void Application::idle() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
static SimpleAverage<float> interIdleDurations;
|
||||
static uint64_t lastIdleEnd{ 0 };
|
||||
|
||||
|
@ -1796,7 +1798,9 @@ void Application::idle() {
|
|||
static uint64_t lastReportTime = now;
|
||||
if ((now - lastReportTime) >= (USECS_PER_SECOND)) {
|
||||
static QString LOGLINE("Average inter-idle time: %1 us for %2 samples");
|
||||
qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount());
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::LogExtraTimings)) {
|
||||
qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount());
|
||||
}
|
||||
interIdleDurations.reset();
|
||||
lastReportTime = now;
|
||||
}
|
||||
|
@ -1826,6 +1830,7 @@ void Application::idle() {
|
|||
PerformanceTimer perfTimer("update");
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
PROFILE_RANGE(__FUNCTION__ "/idleUpdate");
|
||||
update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS));
|
||||
}
|
||||
{
|
||||
|
@ -1849,7 +1854,7 @@ void Application::idle() {
|
|||
// After finishing all of the above work, ensure the idle timer is set to the proper interval,
|
||||
// depending on whether we're throttling or not
|
||||
idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 0);
|
||||
}
|
||||
}
|
||||
|
||||
// check for any requested background downloads.
|
||||
emit checkBackgroundDownloads();
|
||||
|
@ -2302,38 +2307,42 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
bool isLookingAtSomeone = false;
|
||||
glm::vec3 lookAtSpot;
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
// When I am in mirror mode, just look right at the camera (myself)
|
||||
// When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
|
||||
// looking in a mirror one's eyes appear steady.
|
||||
if (!OculusManager::isConnected()) {
|
||||
lookAtSpot = _myCamera.getPosition();
|
||||
} else {
|
||||
if (_myAvatar->isLookingAtLeftEye()) {
|
||||
lookAtSpot = OculusManager::getLeftEyePosition();
|
||||
} else {
|
||||
lookAtSpot = OculusManager::getRightEyePosition();
|
||||
}
|
||||
lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition();
|
||||
}
|
||||
|
||||
} else {
|
||||
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock();
|
||||
if (lookingAt && _myAvatar != lookingAt.get()) {
|
||||
isLookingAtSomeone = true;
|
||||
// If I am looking at someone else, look directly at one of their eyes
|
||||
if (tracker && !tracker->isMuted()) {
|
||||
// If a face tracker is active, look at the eye for the side my gaze is biased toward
|
||||
if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
|
||||
// Look at their right eye
|
||||
lookAtSpot = static_cast<Avatar*>(lookingAt.get())->getHead()->getRightEyePosition();
|
||||
} else {
|
||||
// Look at their left eye
|
||||
lookAtSpot = static_cast<Avatar*>(lookingAt.get())->getHead()->getLeftEyePosition();
|
||||
isLookingAtSomeone = true;
|
||||
Head* lookingAtHead = static_cast<Avatar*>(lookingAt.get())->getHead();
|
||||
|
||||
const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE;
|
||||
glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
|
||||
glm::vec3 fromLookingAtToMe = glm::normalize(_myAvatar->getHead()->getEyePosition()
|
||||
- lookingAtHead->getEyePosition());
|
||||
float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe);
|
||||
|
||||
if (faceAngle < MAXIMUM_FACE_ANGLE) {
|
||||
// Randomly look back and forth between look targets
|
||||
switch (_myAvatar->getEyeContactTarget()) {
|
||||
case LEFT_EYE:
|
||||
lookAtSpot = lookingAtHead->getLeftEyePosition();
|
||||
break;
|
||||
case RIGHT_EYE:
|
||||
lookAtSpot = lookingAtHead->getRightEyePosition();
|
||||
break;
|
||||
case MOUTH:
|
||||
lookAtSpot = lookingAtHead->getMouthPosition();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Need to add randomly looking back and forth between left and right eye for case with no tracker
|
||||
if (_myAvatar->isLookingAtLeftEye()) {
|
||||
lookAtSpot = static_cast<Avatar*>(lookingAt.get())->getHead()->getLeftEyePosition();
|
||||
} else {
|
||||
lookAtSpot = static_cast<Avatar*>(lookingAt.get())->getHead()->getRightEyePosition();
|
||||
}
|
||||
// Just look at their head (mid point between eyes)
|
||||
lookAtSpot = lookingAtHead->getEyePosition();
|
||||
}
|
||||
} else {
|
||||
// I am not looking at anyone else, so just look forward
|
||||
|
@ -2341,14 +2350,13 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
(_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||
}
|
||||
}
|
||||
//
|
||||
// Deflect the eyes a bit to match the detected Gaze from 3D camera if active
|
||||
//
|
||||
if (tracker && !tracker->isMuted()) {
|
||||
|
||||
// Deflect the eyes a bit to match the detected gaze from Faceshift if active.
|
||||
// DDE doesn't track eyes.
|
||||
if (tracker && typeid(*tracker) == typeid(Faceshift) && !tracker->isMuted()) {
|
||||
float eyePitch = tracker->getEstimatedEyePitch();
|
||||
float eyeYaw = tracker->getEstimatedEyeYaw();
|
||||
const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
|
||||
// deflect using Faceshift gaze data
|
||||
glm::vec3 origin = _myAvatar->getHead()->getEyePosition();
|
||||
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
|
||||
float deflection = DependencyManager::get<Faceshift>()->getEyeDeflection();
|
||||
|
@ -2948,6 +2956,7 @@ QRect Application::getDesirableApplicationGeometry() {
|
|||
// or the "myCamera".
|
||||
//
|
||||
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
// We will use these below, from either the camera or head vectors calculated above
|
||||
viewFrustum.setProjection(camera.getProjection());
|
||||
|
||||
|
@ -3304,7 +3313,7 @@ namespace render {
|
|||
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
|
||||
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
|
||||
|
||||
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
|
||||
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
|
||||
/ closestData.getAtmosphereOuterRadius();
|
||||
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
|
||||
if (height < closestData.getAtmosphereInnerRadius()) {
|
||||
|
@ -3312,8 +3321,8 @@ namespace render {
|
|||
alpha = 0.0f;
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
|
@ -3324,8 +3333,8 @@ namespace render {
|
|||
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
|
||||
|
||||
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
float directionY = glm::clamp(sunDirection.y,
|
||||
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
|
||||
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
|
||||
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
|
||||
}
|
||||
|
@ -3381,14 +3390,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
// load the view frustum
|
||||
loadViewFrustum(theCamera, _displayViewFrustum);
|
||||
|
||||
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
//glScalef(-1.0f, 1.0f, 1.0f);
|
||||
//glFrontFace(GL_CW);
|
||||
} else {
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
// transform view according to theCamera
|
||||
// could be myCamera (if in normal mode)
|
||||
// or could be viewFrustumOffsetCamera if in offset mode
|
||||
|
@ -3406,9 +3407,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
Transform viewTransform;
|
||||
viewTransform.setTranslation(theCamera.getPosition());
|
||||
viewTransform.setRotation(rotation);
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
// viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
if (renderArgs->_renderSide != RenderArgs::MONO) {
|
||||
glm::mat4 invView = glm::inverse(_untranslatedViewMatrix);
|
||||
|
||||
|
@ -3463,9 +3461,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
// Assuming nothing get's rendered through that
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
|
||||
// render models...
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
// render models...
|
||||
PerformanceTimer perfTimer("entities");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... entities...");
|
||||
|
@ -3473,11 +3470,11 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
|
||||
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
|
||||
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
|
||||
renderDebugFlags =
|
||||
(RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
|
||||
(RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
|
||||
}
|
||||
renderArgs->_debugFlags = renderDebugFlags;
|
||||
_entities.render(renderArgs);
|
||||
|
@ -3502,8 +3499,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
|
||||
} else {
|
||||
|
||||
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
|
||||
[](WorldBoxRenderData& payload) {
|
||||
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
|
||||
[](WorldBoxRenderData& payload) {
|
||||
payload._val++;
|
||||
});
|
||||
}
|
||||
|
@ -3522,7 +3519,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("SceneProcessPendingChanges");
|
||||
PerformanceTimer perfTimer("SceneProcessPendingChanges");
|
||||
_main3DScene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
_main3DScene->processPendingChangesQueue();
|
||||
|
@ -3546,6 +3543,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;
|
||||
|
@ -4829,7 +4828,7 @@ qreal Application::getDevicePixelRatio() {
|
|||
mat4 Application::getEyeProjection(int eye) const {
|
||||
if (isHMDMode()) {
|
||||
return OculusManager::getEyeProjection(eye);
|
||||
}
|
||||
}
|
||||
|
||||
return _viewFrustum.getProjection();
|
||||
}
|
||||
|
|
|
@ -29,12 +29,13 @@ DatagramProcessor::DatagramProcessor(QObject* parent) :
|
|||
}
|
||||
|
||||
void DatagramProcessor::processDatagrams() {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"DatagramProcessor::processDatagrams()");
|
||||
|
||||
if (_isShuttingDown) {
|
||||
return; // bail early... we're shutting down.
|
||||
}
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"DatagramProcessor::processDatagrams()");
|
||||
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
|
|
|
@ -21,11 +21,34 @@ const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
|
|||
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
|
||||
const QString LOGS_DIRECTORY = "Logs";
|
||||
|
||||
class FilePersistThread : public GenericQueueThread < QString > {
|
||||
public:
|
||||
FilePersistThread(const FileLogger& logger) : _logger(logger) {
|
||||
setObjectName("LogFileWriter");
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool processQueueItems(const Queue& messages) {
|
||||
QFile file(_logger._fileName);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
foreach(const QString& message, messages) {
|
||||
out << message;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
const FileLogger& _logger;
|
||||
};
|
||||
|
||||
static FilePersistThread* _persistThreadInstance;
|
||||
|
||||
FileLogger::FileLogger(QObject* parent) :
|
||||
AbstractLoggerInterface(parent),
|
||||
_logData("")
|
||||
AbstractLoggerInterface(parent)
|
||||
{
|
||||
setExtraDebugging(false);
|
||||
_persistThreadInstance = new FilePersistThread(*this);
|
||||
_persistThreadInstance->initialize(true, QThread::LowestPriority);
|
||||
|
||||
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
|
||||
QHostAddress clientAddress = getLocalAddress();
|
||||
|
@ -33,18 +56,24 @@ FileLogger::FileLogger(QObject* parent) :
|
|||
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
|
||||
}
|
||||
|
||||
void FileLogger::addMessage(QString message) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
emit logReceived(message);
|
||||
_logData += message;
|
||||
FileLogger::~FileLogger() {
|
||||
_persistThreadInstance->terminate();
|
||||
}
|
||||
|
||||
QFile file(_fileName);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
out << message;
|
||||
}
|
||||
void FileLogger::addMessage(const QString& message) {
|
||||
_persistThreadInstance->queueItem(message);
|
||||
emit logReceived(message);
|
||||
}
|
||||
|
||||
void FileLogger::locateLog() {
|
||||
FileUtils::locateFile(_fileName);
|
||||
}
|
||||
|
||||
QString FileLogger::getLogData() {
|
||||
QString result;
|
||||
QFile f(_fileName);
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
result = QTextStream(&f).readAll();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,23 +13,24 @@
|
|||
#define hifi_FileLogger_h
|
||||
|
||||
#include "AbstractLoggerInterface.h"
|
||||
#include <QMutex>
|
||||
#include <GenericQueueThread.h>
|
||||
|
||||
class FileLogger : public AbstractLoggerInterface {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileLogger(QObject* parent = NULL);
|
||||
virtual ~FileLogger();
|
||||
|
||||
virtual void addMessage(QString);
|
||||
virtual QString getLogData() { return _logData; }
|
||||
virtual void locateLog();
|
||||
virtual void addMessage(const QString&) override;
|
||||
virtual QString getLogData() override;
|
||||
virtual void locateLog() override;
|
||||
|
||||
private:
|
||||
QString _logData;
|
||||
QString _fileName;
|
||||
QMutex _mutex;
|
||||
|
||||
friend class FilePersistThread;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // hifi_FileLogger_h
|
||||
|
|
|
@ -58,6 +58,7 @@ void GLCanvas::initializeGL() {
|
|||
}
|
||||
|
||||
void GLCanvas::paintGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
if (!_throttleRendering && !Application::getInstance()->getWindow()->isMinimized()) {
|
||||
Application::getInstance()->paintGL();
|
||||
}
|
||||
|
|
|
@ -18,32 +18,50 @@
|
|||
#include "InterfaceActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = nullptr;
|
||||
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_OFFSET:
|
||||
action = (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
|
||||
break;
|
||||
return (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
|
||||
case ACTION_TYPE_SPRING:
|
||||
action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
break;
|
||||
return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
case ACTION_TYPE_HOLD:
|
||||
action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
|
||||
break;
|
||||
return (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
|
||||
}
|
||||
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
ownerEntity->addAction(simulation, action);
|
||||
return action;
|
||||
}
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
action = nullptr;
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factoryBA(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);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,12 @@ class InterfaceActionFactory : public EntityActionFactoryInterface {
|
|||
public:
|
||||
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~InterfaceActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data);
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
||||
|
|
|
@ -529,7 +529,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests()));
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace MenuOption {
|
|||
const QString LodTools = "LOD Tools";
|
||||
const QString Login = "Login";
|
||||
const QString Log = "Log";
|
||||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
|
@ -268,6 +269,7 @@ namespace MenuOption {
|
|||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowBordersEntityNodes = "Show Entity Nodes";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
|
||||
const QString SimpleShadows = "Simple";
|
||||
const QString SixenseEnabled = "Enable Hydra Support";
|
||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <QThread>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextRenderer.h>
|
||||
|
||||
|
@ -229,6 +230,43 @@ void runTimingTests() {
|
|||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f",
|
||||
(double)(elapsedUsecs / numTests), (double)result);
|
||||
|
||||
|
||||
quint64 BYTE_CODE_MAX_TEST_VALUE = 99999999;
|
||||
quint64 BYTE_CODE_TESTS_SKIP = 999;
|
||||
|
||||
QByteArray extraJunk;
|
||||
const int EXTRA_JUNK_SIZE = 200;
|
||||
extraJunk.append((unsigned char)255);
|
||||
for (int i = 0; i < EXTRA_JUNK_SIZE; i++) {
|
||||
extraJunk.append(QString("junk"));
|
||||
}
|
||||
|
||||
{
|
||||
startTime.start();
|
||||
quint64 tests = 0;
|
||||
quint64 failed = 0;
|
||||
for (quint64 value = 0; value < BYTE_CODE_MAX_TEST_VALUE; value += BYTE_CODE_TESTS_SKIP) {
|
||||
quint64 valueA = value; // usecTimestampNow();
|
||||
ByteCountCoded<quint64> codedValueA = valueA;
|
||||
QByteArray codedValueABuffer = codedValueA;
|
||||
codedValueABuffer.append(extraJunk);
|
||||
ByteCountCoded<quint64> decodedValueA;
|
||||
decodedValueA.decode(codedValueABuffer);
|
||||
quint64 valueADecoded = decodedValueA;
|
||||
tests++;
|
||||
if (valueA != valueADecoded) {
|
||||
qDebug() << "FAILED! value:" << valueA << "decoded:" << valueADecoded;
|
||||
failed++;
|
||||
}
|
||||
|
||||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp) << "ByteCountCoded<quint64> usecs: " << elapsedUsecs
|
||||
<< "per test:" << (double) (elapsedUsecs / tests)
|
||||
<< "tests:" << tests
|
||||
<< "failed:" << failed;
|
||||
}
|
||||
}
|
||||
|
||||
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
|
||||
|
@ -271,3 +309,39 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void runUnitTests() {
|
||||
|
||||
quint64 LAST_TEST = 10;
|
||||
quint64 SKIP_BY = 1;
|
||||
|
||||
for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) {
|
||||
qDebug() << "value:" << value;
|
||||
|
||||
ByteCountCoded<quint64> codedValue = value;
|
||||
|
||||
QByteArray codedValueBuffer = codedValue;
|
||||
|
||||
codedValueBuffer.append((unsigned char)255);
|
||||
codedValueBuffer.append(QString("junk"));
|
||||
|
||||
qDebug() << "codedValueBuffer:";
|
||||
outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size());
|
||||
|
||||
ByteCountCoded<quint64> valueDecoder;
|
||||
size_t bytesConsumed = valueDecoder.decode(codedValueBuffer);
|
||||
quint64 valueDecoded = valueDecoder;
|
||||
qDebug() << "valueDecoded:" << valueDecoded;
|
||||
qDebug() << "bytesConsumed:" << bytesConsumed;
|
||||
|
||||
|
||||
if (value == valueDecoded) {
|
||||
qDebug() << "SUCCESS!";
|
||||
} else {
|
||||
qDebug() << "FAILED!";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ void drawText(int x, int y, float scale, float radians, int mono,
|
|||
void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0);
|
||||
|
||||
void runTimingTests();
|
||||
void runUnitTests();
|
||||
|
||||
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
|
||||
const glm::vec3& sphereCenter, float sphereRadius, float& distance);
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include <AudioClient.h>
|
||||
#include <AudioConstants.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <TextureCache.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "AudioScope.h"
|
||||
|
||||
|
@ -104,7 +107,7 @@ void AudioScope::freeScope() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioScope::render(int width, int height) {
|
||||
void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
|
||||
|
||||
if (!_isEnabled) {
|
||||
return;
|
||||
|
@ -122,24 +125,26 @@ void AudioScope::render(int width, int height) {
|
|||
int y = (height - (int)SCOPE_HEIGHT) / 2;
|
||||
int w = (int)SCOPE_WIDTH;
|
||||
int h = (int)SCOPE_HEIGHT;
|
||||
|
||||
renderBackground(backgroundColor, x, y, w, h);
|
||||
renderGrid(gridColor, x, y, w, h, gridRows, gridCols);
|
||||
|
||||
renderLineStrip(_inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
|
||||
renderLineStrip(_outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
|
||||
renderLineStrip(_outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
|
||||
|
||||
gpu::Batch batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
batch.setResourceTexture(0, textureCache->getWhiteTexture());
|
||||
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
|
||||
batch.setProjectionTransform(legacyProjection);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewTransform(Transform());
|
||||
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor);
|
||||
geometryCache->renderGrid(batch, x, y, w, h, gridRows, gridCols, gridColor, _audioScopeGrid);
|
||||
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
|
||||
renderLineStrip(batch, _outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
|
||||
renderLineStrip(batch, _outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
|
||||
renderArgs->_context->syncCache();
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
||||
void AudioScope::renderBackground(const glm::vec4& color, int x, int y, int width, int height) {
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
|
||||
}
|
||||
|
||||
void AudioScope::renderGrid(const glm::vec4& color, int x, int y, int width, int height, int rows, int cols) {
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(x, y, width, height, rows, cols, color, _audioScopeGrid);
|
||||
}
|
||||
|
||||
void AudioScope::renderLineStrip(int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) {
|
||||
void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) {
|
||||
|
||||
int16_t sample;
|
||||
int16_t* samples = ((int16_t*) byteArray->data()) + offset;
|
||||
|
@ -194,7 +199,7 @@ void AudioScope::renderLineStrip(int id, const glm::vec4& color, int x, int y, i
|
|||
|
||||
|
||||
geometryCache->updateVertices(id, points, color);
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, id);
|
||||
geometryCache->renderVertices(batch, gpu::LINE_STRIP, id);
|
||||
}
|
||||
|
||||
int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamplesPerChannel,
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <RenderArgs.h>
|
||||
|
||||
|
||||
class AudioScope : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -28,7 +31,7 @@ public:
|
|||
void freeScope();
|
||||
void reallocateScope(int frames);
|
||||
|
||||
void render(int width, int height);
|
||||
void render(RenderArgs* renderArgs, int width, int height);
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
|
@ -48,9 +51,7 @@ private slots:
|
|||
|
||||
private:
|
||||
// Audio scope methods for rendering
|
||||
static void renderBackground(const glm::vec4& color, int x, int y, int width, int height);
|
||||
void renderGrid(const glm::vec4& color, int x, int y, int width, int height, int rows, int cols);
|
||||
void renderLineStrip(int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray);
|
||||
void renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray);
|
||||
|
||||
// Audio scope methods for data acquisition
|
||||
int addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamples,
|
||||
|
|
|
@ -644,7 +644,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
|
|||
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
|
||||
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
batch.setUniformTexture(0, _billboardTexture->getGPUTexture());
|
||||
batch.setResourceTexture(0, _billboardTexture->getGPUTexture());
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
@ -694,9 +694,9 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas)
|
||||
|
||||
// Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY
|
||||
double pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
float pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
// Handles pixel density (especially for macs retina displays)
|
||||
double devicePixelRatio = qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
float devicePixelRatio = (float)qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
|
||||
// Compute correct scale to apply
|
||||
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
|
||||
|
@ -708,9 +708,10 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight;
|
||||
|
||||
// Compute orientation
|
||||
glm::vec3 eulerAngles = ::safeEulerAngles(frustum.getOrientation());
|
||||
eulerAngles.z = 0.0f; // Cancel roll
|
||||
glm::quat orientation(eulerAngles); // back to quaternions
|
||||
glm::vec3 dPosition = frustum.getPosition() - getPosition();
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
|
||||
// Set transform (The order IS important)
|
||||
result.setTranslation(textPosition);
|
||||
|
@ -749,7 +750,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
|
|||
const int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// Compute background position/size
|
||||
static const float SLIGHTLY_BEHIND = -0.05f;
|
||||
static const float SLIGHTLY_IN_FRONT = 0.1f;
|
||||
const int border = 0.1f * nameDynamicRect.height();
|
||||
const int left = text_x - border;
|
||||
const int bottom = text_y - border;
|
||||
|
@ -764,16 +765,16 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
|
|||
|
||||
// Compute display name transform
|
||||
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
|
||||
|
||||
// Render background slightly behind to avoid z-fighting
|
||||
auto backgroundTransform = textTransform;
|
||||
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
|
||||
batch.setModelTransform(backgroundTransform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
batch.setModelTransform(textTransform);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor);
|
||||
// Render actual name
|
||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
// Render text slightly in front to avoid z-fighting
|
||||
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
|
||||
batch.setModelTransform(textTransform);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,22 @@
|
|||
// 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(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity),
|
||||
_relativePosition(glm::vec3(0.0f)),
|
||||
_relativeRotation(glm::quat()),
|
||||
_hand("right"),
|
||||
_mine(false)
|
||||
{
|
||||
_type = ACTION_TYPE_HOLD;
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::AvatarActionHold";
|
||||
#endif
|
||||
|
@ -28,6 +37,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()) {
|
||||
|
@ -36,23 +52,32 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
}
|
||||
|
||||
glm::vec3 palmPosition;
|
||||
glm::quat palmRotation;
|
||||
if (_hand == "right") {
|
||||
palmPosition = myAvatar->getRightPalmPosition();
|
||||
palmRotation = myAvatar->getRightPalmRotation();
|
||||
} else {
|
||||
palmPosition = myAvatar->getLeftPalmPosition();
|
||||
palmRotation = myAvatar->getLeftPalmRotation();
|
||||
}
|
||||
|
||||
auto rotation = myAvatar->getWorldAlignedOrientation();
|
||||
auto rotation = palmRotation * _relativeRotation;
|
||||
auto offset = rotation * _relativePosition;
|
||||
auto position = palmPosition + offset;
|
||||
rotation *= _relativeRotation;
|
||||
unlock();
|
||||
|
||||
if (!tryLockForWrite()) {
|
||||
return;
|
||||
}
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
|
||||
if (_positionalTarget != position || _rotationalTarget != rotation) {
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
}
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
}
|
||||
unlock();
|
||||
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
|
@ -60,58 +85,74 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
|
||||
|
||||
bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
||||
bool rPOk = true;
|
||||
bool ok = true;
|
||||
glm::vec3 relativePosition =
|
||||
EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false);
|
||||
bool rROk = true;
|
||||
EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
|
||||
if (!ok) {
|
||||
relativePosition = _relativePosition;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
glm::quat relativeRotation =
|
||||
EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false);
|
||||
bool tSOk = true;
|
||||
EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
|
||||
if (!ok) {
|
||||
relativeRotation = _relativeRotation;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
float timeScale =
|
||||
EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
|
||||
bool hOk = true;
|
||||
EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
|
||||
if (!ok) {
|
||||
timeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
QString hand =
|
||||
EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false);
|
||||
EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
|
||||
if (!ok || !(hand == "left" || hand == "right")) {
|
||||
hand = _hand;
|
||||
}
|
||||
|
||||
lockForWrite();
|
||||
if (rPOk) {
|
||||
if (relativePosition != _relativePosition
|
||||
|| relativeRotation != _relativeRotation
|
||||
|| timeScale != _linearTimeScale
|
||||
|| hand != _hand) {
|
||||
lockForWrite();
|
||||
_relativePosition = relativePosition;
|
||||
} else if (!_parametersSet) {
|
||||
_relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (rROk) {
|
||||
_relativeRotation = relativeRotation;
|
||||
} else if (!_parametersSet) {
|
||||
_relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
const float MIN_TIMESCALE = 0.1f;
|
||||
_linearTimeScale = glm::min(MIN_TIMESCALE, timeScale);
|
||||
_angularTimeScale = _linearTimeScale;
|
||||
_hand = hand;
|
||||
|
||||
if (tSOk) {
|
||||
_linearTimeScale = timeScale;
|
||||
_angularTimeScale = timeScale;
|
||||
} else if (!_parametersSet) {
|
||||
_linearTimeScale = 0.2;
|
||||
_angularTimeScale = 0.2;
|
||||
_mine = true;
|
||||
_active = true;
|
||||
activateBody();
|
||||
unlock();
|
||||
}
|
||||
|
||||
if (hOk) {
|
||||
hand = hand.toLower();
|
||||
if (hand == "left") {
|
||||
_hand = "left";
|
||||
} else if (hand == "right") {
|
||||
_hand = "right";
|
||||
} else {
|
||||
qDebug() << "hold action -- invalid hand argument:" << hand;
|
||||
_hand = "right";
|
||||
}
|
||||
} else if (!_parametersSet) {
|
||||
_hand = "right";
|
||||
}
|
||||
|
||||
_parametersSet = 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) {
|
||||
ObjectActionSpring::deserialize(serializedArguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,17 +19,23 @@
|
|||
|
||||
class AvatarActionHold : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
|
||||
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionHold();
|
||||
|
||||
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
|
||||
|
|
|
@ -256,7 +256,6 @@ 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
|
||||
|
@ -285,6 +284,7 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
|
|||
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
||||
|
||||
AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
|
||||
myAvatar->collisionWithEntity(collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -296,7 +296,7 @@ void Head::relaxLean(float deltaTime) {
|
|||
|
||||
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
|
||||
if (_renderLookatVectors) {
|
||||
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
|
||||
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +324,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() {
|
|||
}
|
||||
|
||||
void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
|
||||
_isLookingAtMe = true;
|
||||
_isLookingAtMe = true;
|
||||
_correctedLookAtPosition = correctedLookAtPosition;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,6 @@
|
|||
#include "InterfaceConfig.h"
|
||||
#include "world.h"
|
||||
|
||||
enum eyeContactTargets {
|
||||
LEFT_EYE,
|
||||
RIGHT_EYE,
|
||||
MOUTH
|
||||
};
|
||||
|
||||
const float EYE_EAR_GAP = 0.08f;
|
||||
|
||||
|
@ -77,6 +72,7 @@ public:
|
|||
const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; }
|
||||
glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); }
|
||||
|
||||
FaceModel& getFaceModel() { return _faceModel; }
|
||||
const FaceModel& getFaceModel() const { return _faceModel; }
|
||||
|
@ -148,7 +144,7 @@ private:
|
|||
FaceModel _faceModel;
|
||||
|
||||
glm::vec3 _correctedLookAtPosition;
|
||||
|
||||
|
||||
int _leftEyeLookAtID;
|
||||
int _rightEyeLookAtID;
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
#include <TextRenderer.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/OculusManager.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "AvatarManager.h"
|
||||
#include "Environment.h"
|
||||
|
@ -42,7 +45,6 @@
|
|||
#include "MyAvatar.h"
|
||||
#include "Physics.h"
|
||||
#include "Recorder.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
|
@ -97,7 +99,7 @@ MyAvatar::MyAvatar() :
|
|||
_shouldRender(true),
|
||||
_billboardValid(false),
|
||||
_feetTouchFloor(true),
|
||||
_isLookingAtLeftEye(true),
|
||||
_eyeContactTarget(LEFT_EYE),
|
||||
_realWorldFieldOfView("realWorldFieldOfView",
|
||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
_firstPersonSkeletonModel(this),
|
||||
|
@ -369,6 +371,12 @@ glm::vec3 MyAvatar::getLeftPalmPosition() {
|
|||
return leftHandPosition;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::getLeftPalmRotation() {
|
||||
glm::quat leftRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
|
||||
return leftRotation;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getRightPalmPosition() {
|
||||
glm::vec3 rightHandPosition;
|
||||
getSkeletonModel().getRightHandPosition(rightHandPosition);
|
||||
|
@ -378,6 +386,12 @@ glm::vec3 MyAvatar::getRightPalmPosition() {
|
|||
return rightHandPosition;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::getRightPalmRotation() {
|
||||
glm::quat rightRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||
return rightRotation;
|
||||
}
|
||||
|
||||
void MyAvatar::clearReferential() {
|
||||
changeReferential(NULL);
|
||||
}
|
||||
|
@ -884,7 +898,6 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
|
||||
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
|
||||
|
||||
int howManyLookingAtMe = 0;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
|
||||
bool isCurrentTarget = avatar->getIsLookAtTarget();
|
||||
|
@ -897,17 +910,22 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
_targetAvatarPosition = avatarPointer->getPosition();
|
||||
smallestAngleTo = angleTo;
|
||||
}
|
||||
// Check if this avatar is looking at me, and fix their gaze on my camera if so
|
||||
if (Application::getInstance()->isLookingAtMyAvatar(avatar)) {
|
||||
howManyLookingAtMe++;
|
||||
// Have that avatar look directly at my camera
|
||||
// Philip TODO: correct to look at left/right eye
|
||||
if (qApp->isHMDMode()) {
|
||||
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
|
||||
// FIXME what is the point of this?
|
||||
// avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition());
|
||||
// Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face.
|
||||
// Offset their gaze according to whether they're looking at one of my eyes or my mouth.
|
||||
glm::vec3 gazeOffset = avatar->getHead()->getLookAtPosition() - getHead()->getEyePosition();
|
||||
const float HUMAN_EYE_SEPARATION = 0.065f;
|
||||
float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition());
|
||||
gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation;
|
||||
|
||||
if (Application::getInstance()->isHMDMode()) {
|
||||
//avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getCamera()->getPosition()
|
||||
// + OculusManager::getMidEyePosition() + gazeOffset);
|
||||
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
|
||||
+ OculusManager::getMidEyePosition() + gazeOffset);
|
||||
} else {
|
||||
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
|
||||
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
|
||||
+ gazeOffset);
|
||||
}
|
||||
} else {
|
||||
avatar->getHead()->clearCorrectedLookAtPosition();
|
||||
|
@ -924,12 +942,24 @@ void MyAvatar::clearLookAtTargetAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
bool MyAvatar::isLookingAtLeftEye() {
|
||||
float const CHANCE_OF_CHANGING_EYE = 0.01f;
|
||||
if (randFloat() < CHANCE_OF_CHANGING_EYE) {
|
||||
_isLookingAtLeftEye = !_isLookingAtLeftEye;
|
||||
eyeContactTarget MyAvatar::getEyeContactTarget() {
|
||||
float const CHANCE_OF_CHANGING_TARGET = 0.01f;
|
||||
if (randFloat() < CHANCE_OF_CHANGING_TARGET) {
|
||||
float const FIFTY_FIFTY_CHANCE = 0.5f;
|
||||
switch (_eyeContactTarget) {
|
||||
case LEFT_EYE:
|
||||
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? MOUTH : RIGHT_EYE;
|
||||
break;
|
||||
case RIGHT_EYE:
|
||||
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? LEFT_EYE : MOUTH;
|
||||
break;
|
||||
case MOUTH:
|
||||
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? RIGHT_EYE : LEFT_EYE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _isLookingAtLeftEye;
|
||||
|
||||
return _eyeContactTarget;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getDefaultEyePosition() const {
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
|
||||
class ModelItemID;
|
||||
|
||||
enum eyeContactTarget {
|
||||
LEFT_EYE,
|
||||
RIGHT_EYE,
|
||||
MOUTH
|
||||
};
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
|
||||
|
@ -94,7 +100,7 @@ public:
|
|||
|
||||
bool isMyAvatar() const { return true; }
|
||||
|
||||
bool isLookingAtLeftEye();
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||
|
||||
|
@ -189,10 +195,12 @@ public slots:
|
|||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
void updateMotionBehavior();
|
||||
|
||||
|
||||
glm::vec3 getLeftPalmPosition();
|
||||
glm::quat getLeftPalmRotation();
|
||||
glm::vec3 getRightPalmPosition();
|
||||
|
||||
glm::quat getRightPalmRotation();
|
||||
|
||||
void clearReferential();
|
||||
bool setModelReferential(const QUuid& id);
|
||||
bool setJointReferential(const QUuid& id, int jointIndex);
|
||||
|
@ -209,6 +217,7 @@ public slots:
|
|||
signals:
|
||||
void transformChanged();
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
void collisionWithEntity(const Collision& collision);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -251,7 +260,7 @@ private:
|
|||
QList<AnimationHandlePointer> _animationHandles;
|
||||
|
||||
bool _feetTouchFloor;
|
||||
bool _isLookingAtLeftEye;
|
||||
eyeContactTarget _eyeContactTarget;
|
||||
|
||||
RecorderPointer _recorder;
|
||||
|
||||
|
|
|
@ -249,7 +249,6 @@ static GlWindow* _outputWindow{ nullptr };
|
|||
static bool _isConnected = false;
|
||||
static ovrHmd _ovrHmd;
|
||||
static ovrFovPort _eyeFov[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffset[ovrEye_Count];
|
||||
static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
|
||||
static ovrSizei _renderTargetSize;
|
||||
static glm::mat4 _eyeProjection[ovrEye_Count];
|
||||
|
@ -281,8 +280,10 @@ static ovrPosef _eyeRenderPoses[ovrEye_Count];
|
|||
static ovrRecti _eyeViewports[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffsets[ovrEye_Count];
|
||||
|
||||
|
||||
glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
|
||||
glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
|
||||
glm::vec3 OculusManager::getMidEyePosition() { return (_eyePositions[ovrEye_Left] + _eyePositions[ovrEye_Right]) / 2.0f; }
|
||||
|
||||
void OculusManager::connect(QOpenGLContext* shareContext) {
|
||||
qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING;
|
||||
|
@ -692,13 +693,13 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
|
|||
_eyeRenderPoses[eye] = eyePoses[eye];
|
||||
// Set the camera rotation for this eye
|
||||
|
||||
vec3 eyePosition = toGlm(_eyeRenderPoses[eye].Position);
|
||||
eyePosition = whichCamera.getRotation() * eyePosition;
|
||||
_eyePositions[eye] = toGlm(_eyeRenderPoses[eye].Position);
|
||||
_eyePositions[eye] = whichCamera.getRotation() * _eyePositions[eye];
|
||||
quat eyeRotation = toGlm(_eyeRenderPoses[eye].Orientation);
|
||||
|
||||
// Update our camera to what the application camera is doing
|
||||
_camera->setRotation(whichCamera.getRotation() * eyeRotation);
|
||||
_camera->setPosition(whichCamera.getPosition() + eyePosition);
|
||||
_camera->setPosition(whichCamera.getPosition() + _eyePositions[eye]);
|
||||
configureCamera(*_camera);
|
||||
_camera->update(1.0f / Application::getInstance()->getFps());
|
||||
|
||||
|
@ -910,4 +911,4 @@ mat4 OculusManager::getEyePose(int eye) {
|
|||
mat4 OculusManager::getHeadPose() {
|
||||
ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
|
||||
return toGlm(ts.HeadPose.ThePose);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
|
||||
static glm::vec3 getLeftEyePosition();
|
||||
static glm::vec3 getRightEyePosition();
|
||||
static glm::vec3 getMidEyePosition();
|
||||
|
||||
static int getHMDScreen();
|
||||
|
||||
|
|
|
@ -109,7 +109,6 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
|
|||
glScissor(portalX, portalY, portalW, portalH);
|
||||
|
||||
glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ);
|
||||
float fov = atan(1.0f / projection[1][1]);
|
||||
projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0));
|
||||
eyeCamera.setProjection(projection);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -184,11 +179,12 @@ void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorI
|
|||
_cursors[iconId] = DependencyManager::get<TextureCache>()->
|
||||
getImageTexture(iconPath);
|
||||
}
|
||||
batch.setUniformTexture(0, _cursors[iconId]);
|
||||
batch.setResourceTexture(0, _cursors[iconId]);
|
||||
}
|
||||
|
||||
// Draws the FBO texture for the screen
|
||||
void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
@ -234,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);
|
||||
}
|
||||
|
@ -258,6 +253,7 @@ vec2 getPolarCoordinates(const PalmData& palm) {
|
|||
|
||||
// Draws the FBO texture for Oculus rift.
|
||||
void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
@ -386,8 +382,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;
|
||||
}
|
||||
|
@ -505,7 +501,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();
|
||||
|
@ -734,7 +730,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();
|
||||
|
@ -743,7 +739,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;
|
||||
|
@ -77,6 +74,7 @@ ApplicationOverlay::~ApplicationOverlay() {
|
|||
|
||||
// Renders the overlays either to a texture or to the screen
|
||||
void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
CHECK_GL_ERROR();
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
|
||||
|
||||
|
@ -101,6 +99,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
if (_uiTexture) {
|
||||
gpu::Batch batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -115,6 +114,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
glm::vec2 size = qApp->getCanvasSize();
|
||||
|
||||
mat4 legacyProjection = glm::ortho<float>(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
|
||||
|
@ -132,11 +132,12 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
|||
emit qApp->renderingOverlay();
|
||||
qApp->getOverlays().renderHUD(renderArgs);
|
||||
|
||||
DependencyManager::get<AudioScope>()->render(renderArgs, _overlayFramebuffer->size().width(), _overlayFramebuffer->size().height());
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
renderArgs->_context->syncCache();
|
||||
fboViewport(_overlayFramebuffer);
|
||||
}
|
||||
|
||||
|
@ -199,7 +200,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
|
|||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setModelTransform(mat4());
|
||||
batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
|
||||
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
|
||||
batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH);
|
||||
|
||||
// TODO animate the disconnect border for some excitement while not connected?
|
||||
|
@ -222,6 +223,7 @@ GLuint ApplicationOverlay::getOverlayTexture() {
|
|||
}
|
||||
|
||||
void ApplicationOverlay::buildFramebufferObject() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
QSize fboSize = qApp->getDeviceSize();
|
||||
if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) {
|
||||
// Already built
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -21,11 +21,14 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
#include "../octree/OctreePacketProcessor.h"
|
||||
#include "ui/OctreeStatsDialog.h"
|
||||
|
||||
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
|
||||
_model(model) {
|
||||
_model(model),
|
||||
_averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
{
|
||||
|
||||
_statCount = 0;
|
||||
_octreeServerLabelsCount = 0;
|
||||
|
@ -50,6 +53,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
|
|||
_localElements = AddStatItem("Local Elements");
|
||||
_localElementsMemory = AddStatItem("Elements Memory");
|
||||
_sendingMode = AddStatItem("Sending Mode");
|
||||
|
||||
_processedPackets = AddStatItem("Entity Packets");
|
||||
_processedPacketsElements = AddStatItem("Processed Packets Elements");
|
||||
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
|
||||
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
|
||||
|
||||
_entityUpdateTime = AddStatItem("Entity Update Time");
|
||||
_entityUpdates = AddStatItem("Entity Updates");
|
||||
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
|
@ -119,6 +130,36 @@ OctreeStatsDialog::~OctreeStatsDialog() {
|
|||
|
||||
void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
|
||||
|
||||
// Processed Entities Related stats
|
||||
auto entities = Application::getInstance()->getEntities();
|
||||
auto entitiesTree = entities->getTree();
|
||||
|
||||
// Do this ever paint event... even if we don't update
|
||||
auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits();
|
||||
|
||||
// track our updated per second
|
||||
const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND;
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceLastWindow = now - _lastWindowAt;
|
||||
auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits;
|
||||
float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND;
|
||||
float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds;
|
||||
if (sinceLastWindow > SAMPLING_WINDOW) {
|
||||
_averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond);
|
||||
_lastWindowAt = now;
|
||||
_lastKnownTrackedEdits = totalTrackedEdits;
|
||||
}
|
||||
|
||||
// Only refresh our stats every once in a while, unless asked for realtime
|
||||
quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND;
|
||||
quint64 sinceLastRefresh = now - _lastRefresh;
|
||||
if (sinceLastRefresh < REFRESH_AFTER) {
|
||||
return QDialog::paintEvent(event);
|
||||
}
|
||||
const int FLOATING_POINT_PRECISION = 3;
|
||||
|
||||
_lastRefresh = now;
|
||||
|
||||
// Update labels
|
||||
|
||||
QLabel* label;
|
||||
|
@ -203,9 +244,110 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
|
|||
"Leaves: " << qPrintable(serversLeavesString) << "";
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Processed Packets Elements
|
||||
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
|
||||
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
|
||||
|
||||
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
|
||||
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
|
||||
|
||||
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
|
||||
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
|
||||
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
|
||||
|
||||
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION);
|
||||
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
|
||||
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
|
||||
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
|
||||
|
||||
label = _labels[_processedPackets];
|
||||
const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor();
|
||||
|
||||
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
|
||||
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
|
||||
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
|
||||
|
||||
QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
|
||||
"Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
|
||||
"Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsElements];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(averageElementsPerPacketString) << " per packet / " <<
|
||||
"" << qPrintable(averageElementsPerSecondString) << " per second";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsEntities];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(averageEntitiesPerPacketString) << " per packet / " <<
|
||||
"" << qPrintable(averageEntitiesPerSecondString) << " per second";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
label = _labels[_processedPacketsTiming];
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " <<
|
||||
"Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " <<
|
||||
"Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Entity Edits update time
|
||||
label = _labels[_entityUpdateTime];
|
||||
auto averageEditDelta = entitiesTree->getAverageEditDeltas();
|
||||
auto maxEditDelta = entitiesTree->getMaxEditDelta();
|
||||
|
||||
QString averageEditDeltaString = locale.toString((uint)averageEditDelta);
|
||||
QString maxEditDeltaString = locale.toString((uint)maxEditDelta);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " <<
|
||||
"Max: " << qPrintable(maxEditDeltaString) << " (usecs)";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
// Entity Edits
|
||||
label = _labels[_entityUpdates];
|
||||
auto bytesPerEdit = entitiesTree->getAverageEditBytes();
|
||||
|
||||
auto updatesPerSecond = _averageUpdatesPerSecond.getAverage();
|
||||
if (updatesPerSecond < 1) {
|
||||
updatesPerSecond = 0; // we don't really care about small updates per second so suppress those
|
||||
}
|
||||
|
||||
QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits);
|
||||
QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION);
|
||||
QString bytesPerEditString = locale.toString(bytesPerEdit);
|
||||
|
||||
statsValue.str("");
|
||||
statsValue <<
|
||||
"" << qPrintable(updatesPerSecondString) << " updates per second / " <<
|
||||
"" << qPrintable(totalTrackedEditsString) << " total updates / " <<
|
||||
"Average Size: " << qPrintable(bytesPerEditString) << " bytes ";
|
||||
|
||||
label->setText(statsValue.str().c_str());
|
||||
|
||||
showAllOctreeServers();
|
||||
|
||||
this->QDialog::paintEvent(event);
|
||||
QDialog::paintEvent(event);
|
||||
}
|
||||
void OctreeStatsDialog::showAllOctreeServers() {
|
||||
int serverCount = 0;
|
||||
|
|
|
@ -63,6 +63,21 @@ private:
|
|||
int _serverElements;
|
||||
int _localElements;
|
||||
int _localElementsMemory;
|
||||
|
||||
int _entityUpdateTime;
|
||||
int _entityUpdates;
|
||||
int _processedPackets;
|
||||
int _processedPacketsElements;
|
||||
int _processedPacketsEntities;
|
||||
int _processedPacketsTiming;
|
||||
|
||||
const int SAMPLES_PER_SECOND = 10;
|
||||
SimpleMovingAverage _averageUpdatesPerSecond;
|
||||
quint64 _lastWindowAt = 0;
|
||||
quint64 _lastKnownTrackedEdits = 0;
|
||||
|
||||
quint64 _lastRefresh = 0;
|
||||
|
||||
int _octreeServerLables[MAX_VOXEL_SERVERS];
|
||||
int _octreeServerLabelsCount;
|
||||
details _extraServerDetails[MAX_VOXEL_SERVERS];
|
||||
|
|
|
@ -136,7 +136,6 @@ void Stats::updateStats() {
|
|||
unsigned long totalPingOctree = 0;
|
||||
int octreeServerCount = 0;
|
||||
int pingOctreeMax = 0;
|
||||
int pingVoxel;
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// TODO: this should also support entities
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
|
@ -147,19 +146,6 @@ void Stats::updateStats() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (octreeServerCount) {
|
||||
pingVoxel = totalPingOctree / octreeServerCount;
|
||||
}
|
||||
|
||||
//STAT_UPDATE(entitiesPing, pingVoxel);
|
||||
//if (_expanded) {
|
||||
// QString voxelMaxPing;
|
||||
// if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
|
||||
// voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
|
||||
// } else {
|
||||
// voxelMaxPing = QString("Voxel max ping: --");
|
||||
// }
|
||||
} else {
|
||||
// -2 causes the QML to hide the ping column
|
||||
STAT_UPDATE(audioPing, -2);
|
||||
|
@ -280,15 +266,15 @@ void Stats::updateStats() {
|
|||
}
|
||||
|
||||
// Server Octree Elements
|
||||
STAT_UPDATE(serverElements, totalNodes);
|
||||
STAT_UPDATE(localElements, OctreeElement::getNodeCount());
|
||||
STAT_UPDATE(serverElements, (int)totalNodes);
|
||||
STAT_UPDATE(localElements, (int)OctreeElement::getNodeCount());
|
||||
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(serverInternal, totalInternal);
|
||||
STAT_UPDATE(serverLeaves, totalLeaves);
|
||||
STAT_UPDATE(serverInternal, (int)totalInternal);
|
||||
STAT_UPDATE(serverLeaves, (int)totalLeaves);
|
||||
// Local Voxels
|
||||
STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount());
|
||||
STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount());
|
||||
STAT_UPDATE(localInternal, (int)OctreeElement::getInternalNodeCount());
|
||||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
}
|
||||
|
|
|
@ -87,37 +87,12 @@ void BillboardOverlay::render(RenderArgs* args) {
|
|||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
batch->setUniformTexture(0, _texture->getGPUTexture());
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
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);
|
||||
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,44 +75,44 @@ void ImageOverlay::render(RenderArgs* args) {
|
|||
glm::vec2 topLeft(left, top);
|
||||
glm::vec2 bottomRight(right, bottom);
|
||||
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
|
||||
// if for some reason our image is not over 0 width or height, don't attempt to render the image
|
||||
if (_renderImage && imageWidth > 0 && imageHeight > 0) {
|
||||
if (_renderImage) {
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
if (imageWidth > 0 && imageHeight > 0) {
|
||||
QRect fromImage;
|
||||
if (_wantClipFromImage) {
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
|
||||
QRect fromImage;
|
||||
if (_wantClipFromImage) {
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
fromImage.setX(scaleX * _fromImage.x());
|
||||
fromImage.setY(scaleY * _fromImage.y());
|
||||
fromImage.setWidth(scaleX * _fromImage.width());
|
||||
fromImage.setHeight(scaleY * _fromImage.height());
|
||||
}
|
||||
else {
|
||||
fromImage.setX(0);
|
||||
fromImage.setY(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
fromImage.setHeight(imageHeight);
|
||||
}
|
||||
|
||||
fromImage.setX(scaleX * _fromImage.x());
|
||||
fromImage.setY(scaleY * _fromImage.y());
|
||||
fromImage.setWidth(scaleX * _fromImage.width());
|
||||
fromImage.setHeight(scaleY * _fromImage.height());
|
||||
float x = fromImage.x() / imageWidth;
|
||||
float y = fromImage.y() / imageHeight;
|
||||
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
|
||||
float h = fromImage.height() / imageHeight;
|
||||
|
||||
glm::vec2 texCoordTopLeft(x, y);
|
||||
glm::vec2 texCoordBottomRight(x + w, y + h);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
|
||||
} else {
|
||||
fromImage.setX(0);
|
||||
fromImage.setY(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
fromImage.setHeight(imageHeight);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
|
||||
}
|
||||
|
||||
float x = fromImage.x() / imageWidth;
|
||||
float y = fromImage.y() / imageHeight;
|
||||
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
|
||||
float h = fromImage.height() / imageHeight;
|
||||
|
||||
glm::vec2 texCoordTopLeft(x, y);
|
||||
glm::vec2 texCoordBottomRight(x + w, y + h);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
|
||||
}
|
||||
|
||||
if (_renderImage) {
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageOverlay::setProperties(const QScriptValue& properties) {
|
||||
|
|
|
@ -32,25 +32,25 @@ void LocalModelsOverlay::update(float deltatime) {
|
|||
|
||||
void LocalModelsOverlay::render(RenderArgs* args) {
|
||||
if (_visible) {
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix(); {
|
||||
Application* app = Application::getInstance();
|
||||
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
|
||||
app->setViewMatrixTranslation(oldTranslation + getPosition());
|
||||
_entityTreeRenderer->render(args);
|
||||
Application::getInstance()->setViewMatrixTranslation(oldTranslation);
|
||||
} glPopMatrix();
|
||||
|
||||
|
||||
auto batch = args ->_batch;
|
||||
Application* app = Application::getInstance();
|
||||
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(oldTranslation + getPosition());
|
||||
batch->setViewTransform(transform);
|
||||
_entityTreeRenderer->render(args);
|
||||
transform.setTranslation(oldTranslation);
|
||||
batch->setViewTransform(transform);
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <Application.h>
|
||||
#include <render/Scene.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
#include "BillboardOverlay.h"
|
||||
#include "Circle3DOverlay.h"
|
||||
|
@ -95,7 +96,12 @@ void Overlays::cleanupOverlaysToDelete() {
|
|||
}
|
||||
|
||||
void Overlays::renderHUD(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
QReadLocker lock(&_lock);
|
||||
gpu::Batch batch;
|
||||
renderArgs->_batch = &batch;
|
||||
|
||||
|
||||
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
|
||||
if (thisOverlay->is3D()) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -109,6 +115,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
thisOverlay->render(renderArgs);
|
||||
}
|
||||
}
|
||||
gpu::GLBackend::renderBatch(batch, true);
|
||||
}
|
||||
|
||||
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
|
||||
|
|
|
@ -62,19 +62,19 @@ namespace render {
|
|||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
|
||||
glPushMatrix();
|
||||
auto batch = args->_batch;
|
||||
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::quat myAvatarRotation = avatar->getOrientation();
|
||||
glm::vec3 myAvatarPosition = avatar->getPosition();
|
||||
float angle = glm::degrees(glm::angle(myAvatarRotation));
|
||||
glm::vec3 axis = glm::axis(myAvatarRotation);
|
||||
float myAvatarScale = avatar->getScale();
|
||||
|
||||
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
|
||||
glRotatef(angle, axis.x, axis.y, axis.z);
|
||||
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(myAvatarPosition);
|
||||
transform.setRotation(glm::angleAxis(angle, axis));
|
||||
transform.setScale(myAvatarScale);
|
||||
batch->setModelTransform(transform);
|
||||
overlay->render(args);
|
||||
glPopMatrix();
|
||||
} else {
|
||||
overlay->render(args);
|
||||
}
|
||||
|
|
|
@ -87,70 +87,6 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
|
|||
geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
//glScalef(dimensions.x, dimensions.y, 1.0f);
|
||||
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line...
|
||||
if (getIsSolid()) {
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, rectangleColor);
|
||||
} else {
|
||||
if (getIsDashedLine()) {
|
||||
|
||||
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
|
||||
geometryCache->renderDashedLine(point1, point2, rectangleColor);
|
||||
geometryCache->renderDashedLine(point2, point3, rectangleColor);
|
||||
geometryCache->renderDashedLine(point3, point4, rectangleColor);
|
||||
geometryCache->renderDashedLine(point4, point1, rectangleColor);
|
||||
|
||||
} else {
|
||||
|
||||
if (halfDimensions != _previousHalfDimensions) {
|
||||
QVector<glm::vec3> border;
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
|
||||
|
||||
_previousHalfDimensions = halfDimensions;
|
||||
|
||||
}
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,35 +41,6 @@ void Sphere3DOverlay::render(RenderArgs* args) {
|
|||
transform.postScale(getDimensions());
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Text3DOverlay.h"
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
||||
|
@ -114,6 +115,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, false, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
|
||||
|
||||
// Same font properties as textSize()
|
||||
|
|
|
@ -84,14 +84,14 @@ void AnimationLoop::setStartAutomatically(bool startAutomatically) {
|
|||
}
|
||||
|
||||
void AnimationLoop::setRunning(bool running) {
|
||||
if (_running == running) {
|
||||
// don't do anything if the new value is the same as the value we already have
|
||||
if (_running != running) {
|
||||
_running = running;
|
||||
|
||||
// If we just set running to true, then also reset the frame to the first frame
|
||||
if (running) {
|
||||
// move back to the beginning
|
||||
_frameIndex = _firstFrame;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((_running = running)) {
|
||||
_frameIndex = _firstFrame;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -804,6 +804,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
|
|||
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
|
||||
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
|
||||
connect(this, &EntityTreeRenderer::collisionWithEntity, entityScriptingInterface, &EntityScriptingInterface::collisionWithEntity);
|
||||
|
||||
connect(&(*DependencyManager::get<SceneScriptingInterface>()), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
|
||||
|
@ -1154,3 +1156,17 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
for (auto entityID : _entityIDsLastInScene) {
|
||||
addingEntity(entityID);
|
||||
}
|
||||
_entityIDsLastInScene.clear();
|
||||
} else {
|
||||
_entityIDsLastInScene = _entitiesInScene.keys();
|
||||
for (auto entityID : _entityIDsLastInScene) {
|
||||
deletingEntity(entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ public:
|
|||
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
|
||||
virtual void errorInLoadingScript(const QUrl& url);
|
||||
|
||||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
|
@ -112,6 +115,7 @@ public slots:
|
|||
void deletingEntity(const EntityItemID& entityID);
|
||||
void entitySciptChanging(const EntityItemID& entityID, const bool reload);
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
void updateEntityRenderStatus(bool shouldRenderEntities);
|
||||
|
||||
// optional slots that can be wired to menu items
|
||||
void setDisplayElementChildProxies(bool value) { _displayElementChildProxies = value; }
|
||||
|
@ -188,6 +192,8 @@ private:
|
|||
int _previousStageDay;
|
||||
|
||||
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
|
||||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID> _entityIDsLastInScene;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -161,12 +161,32 @@ namespace render {
|
|||
template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) {
|
||||
if (args) {
|
||||
if (payload && payload->entity) {
|
||||
PROFILE_RANGE("MetaModelRender");
|
||||
payload->entity->render(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 +197,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 +236,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);
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
|||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
if (textured) {
|
||||
batch.setUniformTexture(0, _texture->getGPUTexture());
|
||||
batch.setResourceTexture(0, _texture->getGPUTexture());
|
||||
}
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, textured);
|
||||
|
|
|
@ -36,10 +36,6 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
|
||||
Transform transformToTopLeft = getTransformToCenter();
|
||||
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
|
||||
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
|
||||
|
||||
// Render background
|
||||
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
|
||||
|
@ -48,15 +44,22 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
// Batch render calls
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
Transform transformToTopLeft = getTransformToCenter();
|
||||
if (getFaceCamera()) {
|
||||
//rotate about vertical to face the camera
|
||||
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
transformToTopLeft.setRotation(orientation);
|
||||
}
|
||||
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
|
||||
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
|
||||
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
|
||||
//rotate about vertical to face the camera
|
||||
if (getFaceCamera()) {
|
||||
transformToTopLeft.postRotate(args->_viewFrustum->getOrientation());
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
}
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false, false, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||
|
||||
float scale = _lineHeight / _textRenderer->getFontSize();
|
||||
|
|
|
@ -17,11 +17,18 @@
|
|||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
vec3 newNormal = normalize(interpolatedNormal.xyz);
|
||||
|
||||
int frontCondition = 1 -int(gl_FrontFacing) * 2;
|
||||
//if (!gl_FrontFacing) {
|
||||
// newNormal = -newNormal;
|
||||
//}
|
||||
|
||||
|
||||
void main(void) {
|
||||
vec3 color = vec3(0.9, 0.2, 0.9);
|
||||
packDeferredFragment(
|
||||
normalize(interpolatedNormal.xyz),
|
||||
newNormal * frontCondition,
|
||||
glowIntensity,
|
||||
gl_Color.rgb,
|
||||
gl_FrontMaterial.specular.rgb,
|
||||
|
|
|
@ -23,11 +23,12 @@ class EntityActionFactoryInterface : public QObject, public Dependency {
|
|||
public:
|
||||
EntityActionFactoryInterface() { }
|
||||
virtual ~EntityActionFactoryInterface() { }
|
||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
||||
EntityActionType type,
|
||||
QUuid id,
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { assert(false); return nullptr; }
|
||||
virtual EntityActionPointer factoryBA(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"
|
||||
|
@ -55,21 +127,21 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
|
|||
qDebug() << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::vec3();
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return glm::vec3();
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
|
||||
ok = false;
|
||||
return glm::vec3();
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
|
@ -83,9 +155,15 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
|
|||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
if (!xOk || !yOk || !zOk) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
|
||||
ok = false;
|
||||
return glm::vec3();
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z) {
|
||||
// at least one of the values is NaN
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
|
@ -109,8 +187,8 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
|
|||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
|
||||
qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
@ -130,12 +208,18 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
|
|||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qDebug() << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys of x, y, z, w and values of type float.";
|
||||
<< "must be a map with keys: x, y, z, and w of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::quat(w, x, y, z);
|
||||
if (x != x || y != y || z != z || w != w) {
|
||||
// at least one of the components is NaN!
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::normalize(glm::quat(w, x, y, z));
|
||||
}
|
||||
|
||||
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
|
@ -152,7 +236,7 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
|
|||
bool vOk = true;
|
||||
float v = vV.toFloat(&vOk);
|
||||
|
||||
if (!vOk) {
|
||||
if (!vOk || v != v) {
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
@ -174,3 +258,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,22 +20,28 @@ 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
|
||||
};
|
||||
|
||||
|
||||
class EntityActionInterface {
|
||||
public:
|
||||
EntityActionInterface() { }
|
||||
EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { }
|
||||
virtual ~EntityActionInterface() { }
|
||||
virtual const QUuid& getID() const = 0;
|
||||
const QUuid& getID() const { return _id; }
|
||||
EntityActionType getType() const { return _type; }
|
||||
|
||||
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() const = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) = 0;
|
||||
|
||||
static EntityActionType actionTypeFromString(QString actionTypeString);
|
||||
static QString actionTypeToString(EntityActionType actionType);
|
||||
|
@ -62,9 +68,14 @@ protected:
|
|||
static QString extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required = true);
|
||||
|
||||
QUuid _id;
|
||||
EntityActionType _type;
|
||||
};
|
||||
|
||||
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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,11 +68,28 @@ 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 ]"
|
||||
|
||||
#if DEBUG
|
||||
#define assertLocked() assert(isLocked())
|
||||
#else
|
||||
#define assertLocked()
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#define assertWriteLocked() assert(isWriteLocked())
|
||||
#else
|
||||
#define assertWriteLocked()
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#define assertUnlocked() assert(isUnlocked())
|
||||
#else
|
||||
#define assertUnlocked()
|
||||
#endif
|
||||
|
||||
/// 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.
|
||||
class EntityItem {
|
||||
class EntityItem : public std::enable_shared_from_this<EntityItem> {
|
||||
// These two classes manage lists of EntityItem pointers and must be able to cleanup pointers when an EntityItem is deleted.
|
||||
// To make the cleanup robust each EntityItem has backpointers to its manager classes (which are only ever set/cleared by
|
||||
// the managers themselves, hence they are fiends) whose NULL status can be used to determine which managers still need to
|
||||
|
@ -92,8 +109,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 +335,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 +381,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,13 +402,25 @@ 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;
|
||||
void deserializeActions();
|
||||
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
|
||||
|
||||
protected:
|
||||
|
||||
const QByteArray getActionDataInternal() const;
|
||||
void setActionDataInternal(QByteArray actionData);
|
||||
|
||||
static bool _sendPhysicsUpdates;
|
||||
EntityTypes::EntityType _type;
|
||||
QUuid _id;
|
||||
|
@ -426,8 +461,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 +491,30 @@ 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);
|
||||
void deserializeActionsInternal();
|
||||
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.
|
||||
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty = false;
|
||||
|
||||
mutable QReadWriteLock _lock;
|
||||
void lockForRead() const;
|
||||
bool tryLockForRead() const;
|
||||
void lockForWrite() const;
|
||||
bool tryLockForWrite() const;
|
||||
void unlock() const;
|
||||
bool isLocked() const;
|
||||
bool isWriteLocked() const;
|
||||
bool isUnlocked() const;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <BufferParser.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "RegisteredMetaTypes.h"
|
||||
|
@ -33,11 +33,8 @@ EntityItemID::EntityItemID(const QUuid& id) : QUuid(id)
|
|||
|
||||
EntityItemID EntityItemID::readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead) {
|
||||
EntityItemID result;
|
||||
|
||||
if (bytesLeftToRead >= NUM_BYTES_RFC4122_UUID) {
|
||||
// id
|
||||
QByteArray encodedID((const char*)data, NUM_BYTES_RFC4122_UUID);
|
||||
result = QUuid::fromRfc4122(encodedID);
|
||||
BufferParser(data, bytesLeftToRead).readUuid(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,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),
|
||||
|
@ -74,7 +74,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),
|
||||
|
@ -101,6 +101,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()),
|
||||
CONSTRUCT_PROPERTY(normals, QVector<glm::vec3>()),
|
||||
CONSTRUCT_PROPERTY(strokeWidths, QVector<float>()),
|
||||
|
||||
|
@ -130,6 +131,10 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sitting
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityItemProperties::animationSettingsChanged() const {
|
||||
return _animationSettingsChanged;
|
||||
}
|
||||
|
||||
void EntityItemProperties::setAnimationSettings(const QString& value) {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
|
@ -185,7 +190,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 {
|
||||
|
@ -291,8 +295,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);
|
||||
|
@ -326,7 +330,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);
|
||||
|
@ -355,6 +359,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);
|
||||
CHECK_PROPERTY_CHANGE(PROP_NORMALS, normals);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STROKE_WIDTHS, strokeWidths);
|
||||
|
||||
|
@ -423,7 +428,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());
|
||||
|
@ -454,6 +459,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);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(normals);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(strokeWidths);
|
||||
|
||||
|
@ -569,6 +575,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);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(normals, qVectorVec3, setNormals);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths,qVectorFloat, setStrokeWidths);
|
||||
|
||||
|
@ -578,7 +585,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);
|
||||
|
@ -713,6 +721,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());
|
||||
|
@ -735,7 +744,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());
|
||||
|
||||
|
@ -829,6 +837,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();
|
||||
|
@ -974,7 +983,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);
|
||||
|
@ -997,7 +1007,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);
|
||||
|
||||
|
@ -1086,6 +1095,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;
|
||||
}
|
||||
|
@ -1120,6 +1130,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
}
|
||||
|
||||
void EntityItemProperties::markAllChanged() {
|
||||
_simulationOwnerChanged = true;
|
||||
_positionChanged = true;
|
||||
_dimensionsChanged = true;
|
||||
_rotationChanged = true;
|
||||
|
@ -1132,7 +1143,6 @@ void EntityItemProperties::markAllChanged() {
|
|||
_frictionChanged = true;
|
||||
_lifetimeChanged = true;
|
||||
_userDataChanged = true;
|
||||
_simulatorIDChanged = true;
|
||||
_scriptChanged = true;
|
||||
_scriptTimestampChanged = true;
|
||||
_collisionSoundURLChanged = true;
|
||||
|
@ -1197,12 +1207,11 @@ void EntityItemProperties::markAllChanged() {
|
|||
|
||||
_hrefChanged = true;
|
||||
_descriptionChanged = true;
|
||||
|
||||
_faceCameraChanged = true;
|
||||
_actionDataChanged = true;
|
||||
|
||||
_normalsChanged = true;
|
||||
_strokeWidthsChanged = true;
|
||||
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
|
@ -1252,3 +1261,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"
|
||||
|
||||
|
@ -121,7 +122,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);
|
||||
|
@ -155,7 +156,7 @@ public:
|
|||
DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool);
|
||||
DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector<glm::vec3>);
|
||||
DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector<float>);
|
||||
|
||||
DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray);
|
||||
static QString getBackgroundModeString(BackgroundMode mode);
|
||||
|
||||
|
||||
|
@ -199,7 +200,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; }
|
||||
|
||||
|
@ -208,6 +209,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;
|
||||
|
@ -287,7 +296,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, "");
|
||||
|
@ -307,7 +316,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,13 +121,16 @@ enum EntityPropertyList {
|
|||
// used by hyperlinks
|
||||
PROP_HREF,
|
||||
PROP_DESCRIPTION,
|
||||
|
||||
|
||||
PROP_FACE_CAMERA,
|
||||
PROP_SCRIPT_TIMESTAMP,
|
||||
|
||||
PROP_ACTION_DATA,
|
||||
|
||||
//Used by quad entity
|
||||
PROP_NORMALS,
|
||||
PROP_STROKE_WIDTHS,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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:
|
||||
|
@ -472,24 +480,21 @@ bool EntityScriptingInterface::setPoints(EntityItemPointer entity, std::function
|
|||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
PolyLineEntityItem* PolyLineEntity = static_cast<PolyLineEntityItem*>(entity.get());
|
||||
_entityTree->lockForWrite();
|
||||
bool success = actor(*PolyLineEntity);
|
||||
entity->setLastEdited(now);
|
||||
entity->setLastBroadcast(now);
|
||||
_entityTree->unlock();
|
||||
|
||||
|
||||
_entityTree->lockForRead();
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
_entityTree->unlock();
|
||||
|
||||
|
||||
properties.setLinePointsDirty();
|
||||
properties.setLastEdited(now);
|
||||
|
||||
|
||||
queueEntityMessage(PacketTypeEntityEdit, entity->getID(), properties);
|
||||
return success;
|
||||
}
|
||||
|
@ -560,7 +565,6 @@ bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& poin
|
|||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -587,7 +591,20 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
|
|||
}
|
||||
|
||||
bool success = actor(simulation, entity);
|
||||
if (success) {
|
||||
_entityTree->entityChanged(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;
|
||||
}
|
||||
|
||||
|
@ -608,7 +625,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(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;
|
||||
|
@ -622,13 +646,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;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,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);
|
||||
|
||||
|
@ -132,6 +132,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);
|
||||
|
@ -167,7 +169,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;
|
||||
|
|
|
@ -146,6 +146,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
|
||||
void EntitySimulation::addEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
entity->deserializeActions();
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
quint64 expiry = entity->getExpiry();
|
||||
|
@ -260,3 +261,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 {
|
||||
|
@ -91,13 +92,11 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
|
|||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (!containingElement) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -107,8 +106,6 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
||||
if (!containingElement) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
||||
<< entity->getEntityItemID();
|
||||
return false;
|
||||
}
|
||||
return updateEntityWithElement(entity, properties, containingElement, senderNode);
|
||||
|
@ -150,23 +147,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 +182,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 +245,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;
|
||||
}
|
||||
}
|
||||
|
@ -636,6 +649,8 @@ 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();
|
||||
}
|
||||
}
|
||||
|
@ -1104,3 +1119,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,6 +185,14 @@ public:
|
|||
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);
|
||||
|
@ -230,6 +238,14 @@ private:
|
|||
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
|
||||
|
|
|
@ -702,6 +702,8 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
int bytesRead = 0;
|
||||
uint16_t numberOfEntities = 0;
|
||||
int expectedBytesPerEntity = EntityItem::expectedBytes();
|
||||
|
||||
args.elementsPerPacket++;
|
||||
|
||||
if (bytesLeftToRead >= (int)sizeof(numberOfEntities)) {
|
||||
// read our entities in....
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue