merged and normals flipping

This commit is contained in:
ericrius1 2015-07-13 09:43:15 -07:00
commit 0ce6737b34
256 changed files with 8859 additions and 6368 deletions

View file

@ -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.

View 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.";
}

View 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

View 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;
}

View 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

View file

@ -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();

View file

@ -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>";

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View 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)

View file

@ -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
View 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
View 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);

View file

@ -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
View 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;

View 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);

View 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);

View file

@ -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
View 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();

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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

View file

@ -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();
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -58,6 +58,7 @@ void GLCanvas::initializeGL() {
}
void GLCanvas::paintGL() {
PROFILE_RANGE(__FUNCTION__);
if (!_throttleRendering && !Application::getInstance()->getWindow()->isMinimized()) {
Application::getInstance()->paintGL();
}

View file

@ -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;
}

View file

@ -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

View file

@ -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");

View file

@ -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";

View file

@ -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!";
}
}
}

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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 {

View file

@ -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;

View file

@ -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);
}
}

View file

@ -47,6 +47,7 @@ public:
static glm::vec3 getLeftEyePosition();
static glm::vec3 getRightEyePosition();
static glm::vec3 getMidEyePosition();
static int getHMDScreen();

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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];

View file

@ -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());
}

View file

@ -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
}
}

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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;
}
}
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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()

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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;
};

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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,

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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,

View file

@ -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;
}

View file

@ -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;

View file

@ -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();
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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