Merge remote-tracking branch 'upstream/master' into homereset

This commit is contained in:
James B. Pollack 2016-04-01 08:02:19 -07:00
commit 30c8a44411
145 changed files with 2335 additions and 1047 deletions

View file

@ -11,7 +11,7 @@
#include "AssignmentParentFinder.h"
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const {
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
SpatiallyNestableWeakPointer parent;
if (parentID.isNull()) {
@ -20,7 +20,11 @@ SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool&
}
// search entities
parent = _tree->findEntityByEntityItemID(parentID);
if (entityTree) {
parent = entityTree->findByID(parentID);
} else {
parent = _tree->findEntityByEntityItemID(parentID);
}
if (parent.expired()) {
success = false;
} else {

View file

@ -25,7 +25,7 @@ class AssignmentParentFinder : public SpatialParentFinder {
public:
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
virtual ~AssignmentParentFinder() { }
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const;
protected:
EntityTreePointer _tree;

View file

@ -12,19 +12,16 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
# 0.5 public
# URL http://static.oculus.com/sdk-downloads/ovr_sdk_win_0.5.0.1.zip
# URL_MD5 d3fc4c02db9be5ff08af4ef4c97b32f9
# 0.6 public
# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
# 0.8 public
# URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
# URL_MD5 54944b03b95149d6010f84eb701b9647
# 1.3 public
# URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.3.0_public.zip
# URL_MD5 4d26faba0c1f35ff80bf674c96ed9259
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip
URL_MD5 54944b03b95149d6010f84eb701b9647
URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.3.0_public.zip
URL_MD5 a2dcf695e0f03a70fdd1ed7480585e82
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
@ -33,14 +30,16 @@ if (WIN32)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include CACHE TYPE INTERNAL)
set(LIBOVR_DIR ${SOURCE_DIR}/OculusSDK/LibOVR)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/x64/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL)
set(LIBOVR_LIB_DIR ${LIBOVR_DIR}/Lib/Windows/x64/Release/VS2013 CACHE TYPE INTERNAL)
else()
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/Win32/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL)
set(LIBOVR_LIB_DIR ${LIBOVR_DIR}/Lib/Windows/Win32/Release/VS2013 CACHE TYPE INTERNAL)
endif()
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${LIBOVR_DIR}/Include CACHE TYPE INTERNAL)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${LIBOVR_LIB_DIR}/LibOVR.lib CACHE TYPE INTERNAL)
elseif(APPLE)
ExternalProject_Add(

View file

@ -85,8 +85,8 @@ macro(GENERATE_INSTALLERS)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Client")
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Server")
cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Interface")
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Sandbox")
include(CPack)
endmacro()

View file

@ -53,7 +53,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(CONSOLE_EXEC_NAME "Server Console.app")
set(CONSOLE_EXEC_NAME "Sandbox.app")
set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}")
set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents")
@ -84,12 +84,19 @@ macro(SET_PACKAGING_PARAMETERS)
# shortcut names
if (PRODUCTION_BUILD)
set(INTERFACE_SHORTCUT_NAME "High Fidelity")
set(CONSOLE_SHORTCUT_NAME "Server Console")
set(INTERFACE_SHORTCUT_NAME "Interface")
set(CONSOLE_SHORTCUT_NAME "Sandbox")
else ()
set(INTERFACE_SHORTCUT_NAME "High Fidelity - ${BUILD_VERSION}")
set(CONSOLE_SHORTCUT_NAME "Server Console - ${BUILD_VERSION}")
set(INTERFACE_SHORTCUT_NAME "Interface - ${BUILD_VERSION}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION}")
endif ()
set(INTERFACE_HF_SHORTCUT_NAME "High Fidelity ${INTERFACE_SHORTCUT_NAME}")
set(CONSOLE_HF_SHORTCUT_NAME "High Fidelity ${CONSOLE_SHORTCUT_NAME}")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "High Fidelity")
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "Server Console")
# check if we need to find signtool
if (PRODUCTION_BUILD OR PR_BUILD)
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")

View file

@ -10,10 +10,14 @@
#
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@")
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@")
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")

View file

@ -387,7 +387,7 @@ Function PostInstallOptionsPage
StrCpy $OffsetUnits u
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_SHORTCUT_NAME@"
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_HF_SHORTCUT_NAME@"
Pop $DesktopClientCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
@ -396,7 +396,7 @@ Function PostInstallOptionsPage
${EndIf}
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for High Fidelity @CONSOLE_SHORTCUT_NAME@"
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@"
Pop $DesktopServerCheckbox
; set the checkbox state depending on what is present in the registry
@ -404,7 +404,7 @@ Function PostInstallOptionsPage
IntOp $CurrentOffset $CurrentOffset + 15
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch High Fidelity @CONSOLE_SHORTCUT_NAME@ on startup"
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
Pop $ServerStartupCheckbox
; set the checkbox state depending on what is present in the registry
@ -414,9 +414,9 @@ Function PostInstallOptionsPage
${EndIf}
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch High Fidelity Server Console after install"
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ after install"
${Else}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch High Fidelity after install"
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install"
${EndIf}
Pop $LaunchNowCheckbox
@ -465,10 +465,10 @@ Function ReadPostInstallOptions
${EndIf}
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
; check if the user asked for a desktop shortcut to Server Console
; check if the user asked for a desktop shortcut to Sandbox
${NSD_GetState} $DesktopServerCheckbox $DesktopServerState
; check if the user asked to have Server Console launched every startup
; check if the user asked to have Sandbox launched every startup
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
${EndIf}
@ -485,7 +485,7 @@ Function HandlePostInstallOptions
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
; check if the user asked for a desktop shortcut to High Fidelity
${If} $DesktopClientState == ${BST_CHECKED}
CreateShortCut "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
!insertmacro WritePostInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES
${Else}
!insertmacro WritePostInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO
@ -494,23 +494,23 @@ Function HandlePostInstallOptions
${EndIf}
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
; check if the user asked for a desktop shortcut to Server Console
; check if the user asked for a desktop shortcut to Sandbox
${If} $DesktopServerState == ${BST_CHECKED}
CreateShortCut "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
${Else}
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
; check if the user asked to have Server Console launched every startup
; check if the user asked to have Sandbox launched every startup
${If} $ServerStartupState == ${BST_CHECKED}
; in case we added a shortcut in the global context, pull that now
SetShellVarContext all
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMSTARTUP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
; make a startup shortcut in this user's current context
SetShellVarContext current
CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
CreateShortCut "$SMSTARTUP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; reset the shell var context back
SetShellVarContext all
@ -589,6 +589,19 @@ Section "-Core installation"
Delete "$INSTDIR\version"
Delete "$INSTDIR\xinput1_3.dll"
; Delete old desktop shortcuts before they were renamed during Sandbox rename
Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
; Delete old Start Menu shortcuts before Sandbox rename
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
; Delete old startup item for Server Console before Sandbox rename
SetShellVarContext current
Delete "$SMSTARTUP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
SetShellVarContext all
; Rename the incorrectly cased Raleway font
Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml"
@ -724,8 +737,8 @@ SectionEnd
!macroend
!macro CheckForRunningApplications action prompter
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "High Fidelity client" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "Server Console" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
!macroend
@ -869,12 +882,12 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
; if it exists, delete the startup shortcut for the current user
SetShellVarContext current
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMSTARTUP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
SetShellVarContext all
@CPACK_NSIS_DELETE_ICONS@

View file

@ -0,0 +1,26 @@
//
// changeColorOnEnterLeave.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 3/31/16.
// Copyright 2016 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
//
(function(){
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
this.enterEntity = function(myID) {
print("enterEntity() myID:" + myID);
Entities.editEntity(myID, { color: { red: getRandomInt(128,255), green: getRandomInt(128,255), blue: getRandomInt(128,255)} });
};
this.leaveEntity = function(myID) {
print("leaveEntity() myID:" + myID);
Entities.editEntity(myID, { color: { red: getRandomInt(128,255), green: getRandomInt(128,255), blue: getRandomInt(128,255)} });
};
})

View file

@ -120,21 +120,25 @@ function menuItemEvent(menuItem) {
if (menuItem.endsWith(" for Output")) {
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Output");
print("output audio selection..." + selectedDevice);
Menu.menuItemEvent.disconnect(menuItemEvent);
Menu.setIsOptionChecked(selectedOutputMenu, false);
selectedOutputMenu = menuItem;
Menu.setIsOptionChecked(selectedOutputMenu, true);
if (AudioDevice.setOutputDevice(selectedDevice)) {
Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice);
}
Menu.menuItemEvent.connect(menuItemEvent);
} else if (menuItem.endsWith(" for Input")) {
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Input");
print("input audio selection..." + selectedDevice);
Menu.menuItemEvent.disconnect(menuItemEvent);
Menu.setIsOptionChecked(selectedInputMenu, false);
selectedInputMenu = menuItem;
Menu.setIsOptionChecked(selectedInputMenu, true);
if (AudioDevice.setInputDevice(selectedDevice)) {
Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice);
}
Menu.menuItemEvent.connect(menuItemEvent);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View file

@ -0,0 +1,41 @@
float aspect(vec2 v) {
return v.x / v.y;
}
vec3 aspectCorrectedTexture() {
vec2 uv = _position.xy;
uv += 0.5;
uv.y = 1.0 - uv.y;
float targetAspect = iWorldScale.x / iWorldScale.y;
float sourceAspect = aspect(iChannelResolution[0].xy);
float aspectCorrection = sourceAspect / targetAspect;
if (aspectCorrection > 1.0) {
float offset = aspectCorrection - 1.0;
float halfOffset = offset / 2.0;
uv.y -= halfOffset;
uv.y *= aspectCorrection;
} else {
float offset = 1.0 - aspectCorrection;
float halfOffset = offset / 2.0;
uv.x -= halfOffset;
uv.x /= aspectCorrection;
}
if (any(lessThan(uv, vec2(0.0)))) {
return vec3(0.0);
}
if (any(greaterThan(uv, vec2(1.0)))) {
return vec3(0.0);
}
vec4 color = texture(iChannel0, uv);
return color.rgb * max(0.5, sourceAspect) * max(0.9, fract(iWorldPosition.x));
}
float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) {
specular = aspectCorrectedTexture();
return 1.0;
}

View file

@ -0,0 +1,67 @@
Script.include("https://s3.amazonaws.com/DreamingContent/scripts/Austin.js");
var ENTITY_SPAWN_LIMIT = 500;
var ENTITY_LIFETIME = 600;
var RADIUS = 1.0; // Spawn within this radius (square)
var TEST_ENTITY_NAME = "EntitySpawnTest";
var entities = [];
var textureIndex = 0;
var texture = Script.resolvePath('cube_texture.png');
var shader = Script.resolvePath('textureStress.fs');
var qml = Script.resolvePath('textureStress.qml');
qmlWindow = new OverlayWindow({
title: 'Test Qml',
source: qml,
height: 240,
width: 320,
toolWindow: false,
visible: true
});
function deleteItems(count) {
if (!count) {
var ids = Entities.findEntities(MyAvatar.position, 50);
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id, ["name"]);
if (properties.name === TEST_ENTITY_NAME) {
Entities.deleteEntity(id);
}
}, this);
entities = [];
return;
} else {
// FIXME... implement
}
}
function createItems(count) {
for (var i = 0; i < count; ++i) {
var newEntity = Entities.addEntity({
type: "Box",
name: TEST_ENTITY_NAME,
position: AUSTIN.avatarRelativePosition(AUSTIN.randomPositionXZ({ x: 0, y: 0, z: -2 }, RADIUS)),
color: { r: 255, g: 255, b: 255 },
dimensions: AUSTIN.randomDimensions(),
lifetime: ENTITY_LIFETIME,
userData: JSON.stringify({
ProceduralEntity: {
version: 2,
shaderUrl: shader,
channels: [ texture + "?" + textureIndex++ ]
}
})
});
entities.push(newEntity);
}
}
qmlWindow.fromQml.connect(function(message){
print(message);
if (message[0] === "create") {
var count = message[1] || 1;
createItems(message[1] || 1);
} else if (message[0] === "delete") {
deleteItems(message[1]);
}
});

View file

@ -0,0 +1,69 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
Rectangle {
id: root
width: parent ? parent.width : 100
height: parent ? parent.height : 100
signal sendToScript(var message);
Text {
id: label
text: "GPU Texture Usage: "
}
Text {
id: usage
anchors.left: label.right
anchors.leftMargin: 8
text: "N/A"
Timer {
repeat: true
running: true
interval: 500
onTriggered: {
usage.text = Render.getConfig("Stats")["textureGPUMemoryUsage"];
}
}
}
Column {
anchors { left: parent.left; right: parent.right; top: label.bottom; topMargin: 8; bottom: parent.bottom }
spacing: 8
Button {
text: "Add 1"
onClicked: root.sendToScript(["create", 1]);
}
Button {
text: "Add 10"
onClicked: root.sendToScript(["create", 10]);
}
Button {
text: "Add 100"
onClicked: root.sendToScript(["create", 100]);
}
/*
Button {
text: "Delete 1"
onClicked: root.sendToScript(["delete", 1]);
}
Button {
text: "Delete 10"
onClicked: root.sendToScript(["delete", 10]);
}
Button {
text: "Delete 100"
onClicked: root.sendToScript(["delete", 100]);
}
*/
Button {
text: "Delete All"
onClicked: root.sendToScript(["delete", 0]);
}
}
}

View file

@ -0,0 +1 @@
ConfigSlider 1.0 ConfigSlider.qml

View file

@ -0,0 +1,114 @@
//
// culling.qml
// examples/utilities/render
//
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "configSlider"
Column {
id: root
spacing: 8
property var sceneOctree: Render.getConfig("DrawSceneOctree");
property var itemSelection: Render.getConfig("DrawItemSelection");
Component.onCompleted: {
sceneOctree.enabled = true;
itemSelection.enabled = true;
sceneOctree.showVisibleCells = false;
sceneOctree.showEmptyCells = false;
itemSelection.showInsideItems = false;
itemSelection.showInsideSubcellItems = false;
itemSelection.showPartialItems = false;
itemSelection.showPartialSubcellItems = false;
}
Component.onDestruction: {
sceneOctree.enabled = false;
itemSelection.enabled = false;
Render.getConfig("FetchSceneSelection").freezeFrustum = false;
Render.getConfig("CullSceneSelection").freezeFrustum = false;
}
GroupBox {
title: "Culling"
Row {
spacing: 8
Column {
spacing: 8
CheckBox {
text: "Freeze Culling Frustum"
checked: false
onCheckedChanged: {
Render.getConfig("FetchSceneSelection").freezeFrustum = checked;
Render.getConfig("CullSceneSelection").freezeFrustum = checked;
}
}
Label {
text: "Octree"
}
CheckBox {
text: "Visible Cells"
checked: root.sceneOctree.showVisibleCells
onCheckedChanged: { root.sceneOctree.showVisibleCells = checked }
}
CheckBox {
text: "Empty Cells"
checked: false
onCheckedChanged: { root.sceneOctree.showEmptyCells = checked }
}
}
Column {
spacing: 8
Label {
text: "Frustum Items"
}
CheckBox {
text: "Inside Items"
checked: false
onCheckedChanged: { root.itemSelection.showInsideItems = checked }
}
CheckBox {
text: "Inside Sub-cell Items"
checked: false
onCheckedChanged: { root.itemSelection.showInsideSubcellItems = checked }
}
CheckBox {
text: "Partial Items"
checked: false
onCheckedChanged: { root.itemSelection.showPartialItems = checked }
}
CheckBox {
text: "Partial Sub-cell Items"
checked: false
onCheckedChanged: { root.itemSelection.showPartialSubcellItems = checked }
}
}
}
}
GroupBox {
title: "Render Items"
Column{
Repeater {
model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred", "Light:DrawLight",
"Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ]
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: true
config: Render.getConfig(modelData.split(":")[1])
property: "maxDrawn"
max: config.numDrawn
min: -1
}
}
}
}
}

View file

@ -0,0 +1,21 @@
//
// debugRender.js
// examples/utilities/render
//
// Sam Gateau, created on 3/22/2016.
// Copyright 2016 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
//
// Set up the qml ui
var qml = Script.resolvePath('culling.qml');
var window = new OverlayWindow({
title: 'Render Draws',
source: qml,
width: 300,
height: 200
});
window.setPosition(200, 50);
window.closed.connect(function() { Script.stop(); });

View file

@ -10,6 +10,7 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "configSlider"
Column {
id: root

View file

@ -1,6 +1,6 @@
//
// PlotPerf.qml
// examples/utilities/tools/render
// examples/utilities/render/plotperf
//
// Created by Sam Gateau on 3//2016
// Copyright 2016 High Fidelity, Inc.
@ -15,40 +15,64 @@ Item {
id: root
width: parent.width
height: 100
// The title of the graph
property string title
property var config
property string parameters
// THis is my hack to get the name of the first property and assign it to a trigger var in order to get
// THe object used as the default source object for the prop plots
property var object
// THis is my hack to get a property and assign it to a trigger var in order to get
// a signal called whenever the value changed
property var trigger: config[parameters.split(":")[3].split("-")[0]]
property var trigger
// Plots is an array of plot descriptor
// a default plot descriptor expects the following object:
// prop: [ {
// object: {} // Optional: this is the object from which the prop will be fetched,
// if nothing than the object from root is used
// prop:"bufferCPUCount", // Needed the name of the property from the object to feed the plot
// label: "CPU", // Optional: Label as displayed on the plot
// color: "#00B4EF" // Optional: Color of the curve
// unit: "km/h" // Optional: Unit added to the value displayed, if nothing then the default unit is used
// scale: 1 // Optional: Extra scaling used to represent the value, this scale is combined with the global scale.
// },
property var plots
// Default value scale used to define the max value of the chart
property var valueScale: 1
// Default value unit appended to the value displayed
property var valueUnit: ""
// Default number of digits displayed
property var valueNumDigits: 0
property var inputs: parameters.split(":")
property var valueScale: +inputs[0]
property var valueUnit: inputs[1]
property var valueNumDigits: inputs[2]
property var input_VALUE_OFFSET: 3
property var valueMax : 1
property var _values : new Array()
property var tick : 0
function createValues() {
if (inputs.length > input_VALUE_OFFSET) {
for (var i = input_VALUE_OFFSET; i < inputs.length; i++) {
var varProps = inputs[i].split("-")
_values.push( {
value: varProps[0],
valueMax: 1,
numSamplesConstantMax: 0,
valueHistory: new Array(),
label: varProps[1],
color: varProps[2],
scale: (varProps.length > 3 ? varProps[3] : 1),
unit: (varProps.length > 4 ? varProps[4] : valueUnit)
})
}
}
print("trigger is: " + JSON.stringify(trigger))
if (Array.isArray(plots)) {
for (var i =0; i < plots.length; i++) {
var plot = plots[i];
print(" a pnew Plot:" + JSON.stringify(plot));
_values.push( {
object: (plot["object"] !== undefined ? plot["object"] : root.object),
value: plot["prop"],
valueMax: 1,
numSamplesConstantMax: 0,
valueHistory: new Array(),
label: (plot["label"] !== undefined ? plot["label"] : ""),
color: (plot["color"] !== undefined ? plot["color"] : "white"),
scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
})
}
}
print("in creator" + JSON.stringify(_values));
}
@ -69,7 +93,8 @@ Item {
var currentValueMax = 0
for (var i = 0; i < _values.length; i++) {
var currentVal = config[_values[i].value] * _values[i].scale;
var currentVal = _values[i].object[_values[i].value] * _values[i].scale;
_values[i].valueHistory.push(currentVal)
_values[i].numSamplesConstantMax++;

View file

@ -0,0 +1,195 @@
//
// stats.qml
// examples/utilities/render
//
// Created by Zach Pomerantz on 2/8/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "plotperf"
Item {
id: statsUI
anchors.fill:parent
Column {
id: stats
spacing: 8
anchors.fill:parent
property var config: Render.getConfig("Stats")
function evalEvenHeight() {
// Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
return (height - spacing * (children.length - 1)) / children.length
}
PlotPerf {
title: "Num Buffers"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["bufferCPUCount"]
plots: [
{
prop: "bufferCPUCount",
label: "CPU",
color: "#00B4EF"
},
{
prop: "bufferGPUCount",
label: "GPU",
color: "#1AC567"
}
]
}
PlotPerf {
title: "gpu::Buffer Memory"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["bufferCPUMemoryUsage"]
valueScale: 1048576
valueUnit: "Mb"
valueNumDigits: "1"
plots: [
{
prop: "bufferCPUMemoryUsage",
label: "CPU",
color: "#00B4EF"
},
{
prop: "bufferGPUMemoryUsage",
label: "GPU",
color: "#1AC567"
}
]
}
PlotPerf {
title: "Num Textures"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["textureCPUCount"]
plots: [
{
prop: "textureCPUCount",
label: "CPU",
color: "#00B4EF"
},
{
prop: "textureGPUCount",
label: "GPU",
color: "#1AC567"
},
{
prop: "frameTextureCount",
label: "Frame",
color: "#E2334D"
}
]
}
PlotPerf {
title: "gpu::Texture Memory"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["textureCPUMemoryUsage"]
valueScale: 1048576
valueUnit: "Mb"
valueNumDigits: "1"
plots: [
{
prop: "textureCPUMemoryUsage",
label: "CPU",
color: "#00B4EF"
},
{
prop: "textureGPUMemoryUsage",
label: "GPU",
color: "#1AC567"
}
]
}
PlotPerf {
title: "Triangles"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["frameTriangleCount"]
valueScale: 1000
valueUnit: "K"
plots: [
{
prop: "frameTriangleCount",
label: "Triangles",
color: "#1AC567"
},
{
prop: "frameTriangleRate",
label: "rate",
color: "#E2334D",
scale: 0.001,
unit: "MT/s"
}
]
}
PlotPerf {
title: "Drawcalls"
height: parent.evalEvenHeight()
object: stats.config
trigger: stats.config["frameDrawcallCount"]
plots: [
{
prop: "frameAPIDrawcallCount",
label: "API Drawcalls",
color: "#00B4EF"
},
{
prop: "frameDrawcallCount",
label: "GPU Drawcalls",
color: "#1AC567"
},
{
prop: "frameDrawcallRate",
label: "rate",
color: "#E2334D",
scale: 0.001,
unit: "K/s"
}
]
}
property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
property var drawLightConfig: Render.getConfig("DrawLight")
PlotPerf {
title: "Items"
height: parent.evalEvenHeight()
object: parent.drawOpaqueConfig
trigger: Render.getConfig("DrawOpaqueDeferred")["numDrawn"]
plots: [
{
object: Render.getConfig("DrawOpaqueDeferred"),
prop: "numDrawn",
label: "Opaques",
color: "#1AC567"
},
{
object: Render.getConfig("DrawTransparentDeferred"),
prop: "numDrawn",
label: "Translucents",
color: "#00B4EF"
},
{
object: Render.getConfig("DrawLight"),
prop: "numDrawn",
label: "Lights",
color: "#E2334D"
}
]
}
}
}

View file

@ -1,99 +0,0 @@
//
// debugRenderOctree.js
// examples/utilities/tools
//
// Sam Gateau
// Copyright 2016 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
//
Script.include("cookies.js");
var panel = new Panel(10, 300);
var drawOctree = Render.RenderDeferredTask.DrawSceneOctree;
Render.RenderDeferredTask.DrawSceneOctree.enabled = true;
Render.RenderDeferredTask.DrawItemSelection.enabled = true;
panel.newCheckbox("Show Octree Cells",
function(value) { Render.RenderDeferredTask.DrawSceneOctree.showVisibleCells = value; },
function() { return (Render.RenderDeferredTask.DrawSceneOctree.showVisibleCells); },
function(value) { return (value); }
);
panel.newCheckbox("Show Empty Cells",
function(value) { Render.RenderDeferredTask.DrawSceneOctree.showEmptyCells = value; },
function() { return (Render.RenderDeferredTask.DrawSceneOctree.showEmptyCells); },
function(value) { return (value); }
);
panel.newCheckbox("Freeze Frustum",
function(value) { Render.RenderDeferredTask.FetchSceneSelection.freezeFrustum = value; Render.RenderDeferredTask.CullSceneSelection.freezeFrustum = value; },
function() { return (Render.RenderDeferredTask.FetchSceneSelection.freezeFrustum); },
function(value) { return (value); }
);
panel.newCheckbox("Show Inside Items",
function(value) { Render.RenderDeferredTask.DrawItemSelection.showInsideItems = value; },
function() { return (Render.RenderDeferredTask.DrawItemSelection.showInsideItems); },
function(value) { return (value); }
);
panel.newCheckbox("Show Inside Subcell Items",
function(value) { Render.RenderDeferredTask.DrawItemSelection.showInsideSubcellItems = value; },
function() { return (Render.RenderDeferredTask.DrawItemSelection.showInsideSubcellItems); },
function(value) { return (value); }
);
panel.newCheckbox("Show Partial Items",
function(value) { Render.RenderDeferredTask.DrawItemSelection.showPartialItems = value; },
function() { return (Render.RenderDeferredTask.DrawItemSelection.showPartialItems); },
function(value) { return (value); }
);
panel.newCheckbox("Show Partial Subcell Items",
function(value) { Render.RenderDeferredTask.DrawItemSelection.showPartialSubcellItems = value; },
function() { return (Render.RenderDeferredTask.DrawItemSelection.showPartialSubcellItems); },
function(value) { return (value); }
);
/*
panel.newSlider('Cells Free / Allocated', -1, 1,
function(value) { value; }, // setter
function() { return Render.RenderDeferredTask.DrawSceneOctree.numFreeCells; }, // getter
function(value) { return value; });
this.update = function () {
var numFree = Render.RenderDeferredTask.DrawSceneOctree.numFreeCells;
var numAlloc = Render.RenderDeferredTask.DrawSceneOctree.numAllocatedCells;
var title = [
' ' + name,
numFree + ' / ' + numAlloc
].join('\t');
widget.editTitle({ text: title });
slider.setMaxValue(numAlloc);
};
*/
function mouseMoveEvent(event) {
panel.mouseMoveEvent(event);
}
function mousePressEvent(event) {
panel.mousePressEvent(event);
}
function mouseReleaseEvent(event) {
panel.mouseReleaseEvent(event);
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
function scriptEnding() {
panel.destroy();
Render.RenderDeferredTask.DrawSceneOctree.enabled = false;
Render.RenderDeferredTask.DrawItemSelection.enabled = false;
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -1,69 +0,0 @@
//
// stats.qml
// examples/utilities/tools/render
//
// Created by Zach Pomerantz on 2/8/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "plotperf"
Item {
id: statsUI
anchors.fill:parent
Column {
id: stats
spacing: 8
anchors.fill:parent
property var config: Render.getConfig("Stats")
function evalEvenHeight() {
// Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
return (height - spacing * (children.length - 1)) / children.length
}
PlotPerf {
title: "Num Buffers"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:bufferCPUCount-CPU-#00B4EF:bufferGPUCount-GPU-#1AC567"
}
PlotPerf {
title: "gpu::Buffer Memory"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1048576:Mb:1:bufferCPUMemoryUsage-CPU-#00B4EF:bufferGPUMemoryUsage-GPU-#1AC567"
}
PlotPerf {
title: "Num Textures"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:textureCPUCount-CPU-#00B4EF:textureGPUCount-GPU-#1AC567:frameTextureCount-Frame-#E2334D"
}
PlotPerf {
title: "gpu::Texture Memory"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1048576:Mb:1:textureCPUMemoryUsage-CPU-#00B4EF:textureGPUMemoryUsage-GPU-#1AC567"
}
PlotPerf {
title: "Drawcalls"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:frameDrawcallCount-frame-#E2334D:frameDrawcallRate-rate-#1AC567-0.001-K/s"
}
PlotPerf {
title: "Triangles"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1000:K:0:frameTriangleCount-frame-#E2334D:frameTriangleRate-rate-#1AC567-0.001-MT/s"
}
}
}

View file

@ -19,6 +19,7 @@
#include <QtNetwork/QNetworkRequest>
#include <LimitedNodeList.h>
#include <NetworkAccessManager.h>
#include <NetworkingConstants.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
@ -33,7 +34,8 @@ IceServer::IceServer(int argc, char* argv[]) :
_id(QUuid::createUuid()),
_serverSocket(),
_activePeers(),
_httpManager(QHostAddress::AnyIPv4, ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this)
_httpManager(QHostAddress::AnyIPv4, ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this),
_lastInactiveCheckTimestamp(QDateTime::currentMSecsSinceEpoch())
{
// start the ice-server socket
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
@ -68,8 +70,6 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) {
void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
_lastPacketTimestamp = QDateTime::currentMSecsSinceEpoch();
auto nlPacket = NLPacket::fromBase(std::move(packet));
// make sure that this packet at least looks like something we can read
@ -201,8 +201,8 @@ bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& pla
void IceServer::requestDomainPublicKey(const QUuid& domainID) {
// send a request to the metaverse API for the public key for this domain
QNetworkAccessManager* manager = new QNetworkAccessManager { this };
connect(manager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
auto& networkAccessManager = NetworkAccessManager::getInstance();
connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
@ -213,7 +213,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
qDebug() << "Requesting public key for domain with ID" << domainID;
manager->get(publicKeyRequest);
networkAccessManager.get(publicKeyRequest);
}
void IceServer::publicKeyReplyFinished(QNetworkReply* reply) {
@ -281,6 +281,8 @@ void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSoc
void IceServer::clearInactivePeers() {
NetworkPeerHash::iterator peerItem = _activePeers.begin();
_lastInactiveCheckTimestamp = QDateTime::currentMSecsSinceEpoch();
while (peerItem != _activePeers.end()) {
SharedNetworkPeer peer = peerItem.value();
@ -309,11 +311,14 @@ bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, b
const quint64 MAX_PACKET_GAP_MS_FOR_STUCK_SOCKET = 10 * 1000;
int statusNumber = (QDateTime::currentMSecsSinceEpoch() - _lastPacketTimestamp > MAX_PACKET_GAP_MS_FOR_STUCK_SOCKET)
? 1 : 0;
auto sinceLastInactiveCheck = QDateTime::currentMSecsSinceEpoch() - _lastInactiveCheckTimestamp;
int statusNumber = (sinceLastInactiveCheck > MAX_PACKET_GAP_MS_FOR_STUCK_SOCKET) ? 1 : 0;
connection->respond(HTTPConnection::StatusCode200, QByteArray::number(statusNumber));
return true;
}
}
return true;
return false;
}

View file

@ -58,7 +58,7 @@ private:
using DomainPublicKeyHash = std::unordered_map<QUuid, RSAUniquePtr>;
DomainPublicKeyHash _domainPublicKeys;
quint64 _lastPacketTimestamp;
quint64 _lastInactiveCheckTimestamp;
};
#endif // hifi_IceServer_h

View file

@ -224,7 +224,6 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
#endif
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
@ -598,8 +597,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
audioThread->setObjectName("Audio Thread");
auto audioIO = DependencyManager::get<AudioClient>();
audioIO->setPositionGetter([this]{ return getMyAvatar()->getPositionForAudio(); });
audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); });
audioIO->setPositionGetter([]{
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;
});
audioIO->setOrientationGetter([]{
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
});
audioIO->moveToThread(audioThread);
recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
@ -1480,11 +1489,15 @@ void Application::paintGL() {
// FIXME not needed anymore?
_offscreenContext->makeCurrent();
displayPlugin->updateHeadPose(_frameCount);
displayPlugin->beginFrameRender(_frameCount);
// update the avatar with a fresh HMD pose
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
// update sensorToWorldMatrix for camera and hand controllers
getMyAvatar()->updateSensorToWorldMatrix();
auto lodManager = DependencyManager::get<LODManager>();
@ -1997,6 +2010,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_Y:
if (isShifted && isMeta) {
getActiveDisplayPlugin()->cycleDebugOutput();
}
break;
case Qt::Key_B:
if (isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -2562,11 +2581,6 @@ void Application::idle(uint64_t now) {
return; // bail early, nothing to do here.
}
checkChangeCursor();
Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update();
// These tasks need to be done on our first idle, because we don't want the showing of
// overlay subwindows to do a showDesktop() until after the first time through
static bool firstIdle = true;
@ -2615,6 +2629,11 @@ void Application::idle(uint64_t now) {
// We're going to execute idle processing, so restart the last idle timer
_lastTimeUpdated.start();
checkChangeCursor();
Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update();
{
static uint64_t lastIdleStart{ now };
uint64_t idleStartToStartDuration = now - lastIdleStart;
@ -2790,43 +2809,50 @@ void Application::calibrateEyeTracker5Points() {
}
#endif
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
QVector<EntityItemPointer> entities;
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset) {
QHash<EntityItemID, EntityItemPointer> entities;
auto entityTree = getEntities()->getTree();
auto exportTree = std::make_shared<EntityTree>();
exportTree->createRootElement();
glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
for (auto entityID : entityIDs) {
for (auto entityID : entityIDs) { // Gather entities and properties.
auto entityItem = entityTree->findEntityByEntityItemID(entityID);
if (!entityItem) {
qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene.";
continue;
}
auto properties = entityItem->getProperties();
auto position = properties.getPosition();
root.x = glm::min(root.x, position.x);
root.y = glm::min(root.y, position.y);
root.z = glm::min(root.z, position.z);
entities << entityItem;
if (!givenOffset) {
EntityItemID parentID = entityItem->getParentID();
if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) {
auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties.
root.x = glm::min(root.x, position.x);
root.y = glm::min(root.y, position.y);
root.z = glm::min(root.z, position.z);
}
}
entities[entityID] = entityItem;
}
if (entities.size() == 0) {
return false;
}
for (auto entityItem : entities) {
auto properties = entityItem->getProperties();
properties.setPosition(properties.getPosition() - root);
exportTree->addEntity(entityItem->getEntityItemID(), properties);
if (givenOffset) {
root = *givenOffset;
}
for (EntityItemPointer& entityDatum : entities) {
auto properties = entityDatum->getProperties();
EntityItemID parentID = properties.getParentID();
if (parentID.isInvalidID()) {
properties.setPosition(properties.getPosition() - root);
} else if (!entities.contains(parentID)) {
entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root);
} // else valid parent -- don't offset
exportTree->addEntity(entityDatum->getEntityItemID(), properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
@ -2836,33 +2862,14 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
}
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
glm::vec3 offset(x, y, z);
QVector<EntityItemPointer> entities;
getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
if (entities.size() > 0) {
glm::vec3 root(x, y, z);
auto exportTree = std::make_shared<EntityTree>();
exportTree->createRootElement();
for (int i = 0; i < entities.size(); i++) {
EntityItemProperties properties = entities.at(i)->getProperties();
EntityItemID id = entities.at(i)->getEntityItemID();
properties.setPosition(properties.getPosition() - root);
exportTree->addEntity(id, properties);
}
// remap IDs on export so that we aren't publishing the IDs of entities in our domain
exportTree->remapIDs();
exportTree->writeToSVOFile(filename.toLocal8Bit().constData());
} else {
qCDebug(interfaceapp) << "No models were selected";
return false;
QVector<EntityItemID> ids;
getEntities()->getTree()->findEntities(AACube(offset, scale), entities);
foreach(EntityItemPointer entity, entities) {
ids << entity->getEntityItemID();
}
// restore the main window's active state
_window->activateWindow();
return true;
return exportEntities(filename, ids, &offset);
}
void Application::loadSettings() {
@ -2895,7 +2902,6 @@ bool Application::importEntities(const QString& urlOrFilename) {
bool success = _entityClipboard->readFromURL(urlOrFilename);
if (success) {
_entityClipboard->remapIDs();
_entityClipboard->reaverageOctreeElements();
}
return success;
@ -2978,6 +2984,11 @@ void Application::updateLOD() {
}
}
void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
_preRenderLambdas[key] = func;
}
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
// (Maybe this code should be moved there?)
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
@ -3346,9 +3357,10 @@ void Application::update(float deltaTime) {
}
{
PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("havestChanges");
PerformanceTimer perfTimer("harvestChanges");
if (_physicsEngine->hasOutgoingChanges()) {
getEntities()->getTree()->withWriteLock([&] {
PerformanceTimer perfTimer("handleOutgoingChanges");
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
_entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID());
avatarManager->handleOutgoingChanges(outgoingChanges);
@ -3364,6 +3376,7 @@ void Application::update(float deltaTime) {
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation.handleCollisionEvents(collisionEvents);
// NOTE: the getEntities()->update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
getEntities()->update(); // update the models...
@ -3387,9 +3400,6 @@ void Application::update(float deltaTime) {
qApp->updateMyAvatarLookAtPosition();
// update sensorToWorldMatrix for camera and hand controllers
myAvatar->updateSensorToWorldMatrix();
{
PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
avatarManager->updateMyAvatar(deltaTime);
@ -3454,6 +3464,16 @@ void Application::update(float deltaTime) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
}
}
{
PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
for (auto& iter : _preRenderLambdas) {
iter.second();
}
_preRenderLambdas.clear();
}
}

View file

@ -211,6 +211,8 @@ public:
render::EnginePointer getRenderEngine() override { return _renderEngine; }
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
void updateMyAvatarLookAtPosition();
@ -233,7 +235,7 @@ signals:
public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& url);
@ -510,6 +512,9 @@ private:
bool _cursorNeedsChanging { false };
QThread* _deadlockWatchdogThread;
std::map<void*, std::function<void()>> _preRenderLambdas;
std::mutex _preRenderLambdasLock;
};
#endif // hifi_Application_h

View file

@ -16,7 +16,7 @@
#include "InterfaceParentFinder.h"
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success) const {
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
SpatiallyNestableWeakPointer parent;
if (parentID.isNull()) {
@ -25,9 +25,13 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s
}
// search entities
EntityTreeRenderer* treeRenderer = qApp->getEntities();
EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr;
parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr;
if (entityTree) {
parent = entityTree->findByID(parentID);
} else {
EntityTreeRenderer* treeRenderer = qApp->getEntities();
EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr;
parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr;
}
if (!parent.expired()) {
success = true;
return parent;

View file

@ -21,7 +21,7 @@ class InterfaceParentFinder : public SpatialParentFinder {
public:
InterfaceParentFinder() { }
virtual ~InterfaceParentFinder() { }
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const;
};
#endif // hifi_InterfaceParentFinder_h

View file

@ -859,7 +859,11 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
_skeletonModel->setURL(_skeletonModelURL);
if (QThread::currentThread() == thread()) {
_skeletonModel->setURL(_skeletonModelURL);
} else {
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, _skeletonModelURL));
}
}
// create new model, can return an instance of a SoftAttachmentModel rather then Model

View file

@ -373,32 +373,34 @@ void MyAvatar::simulate(float deltaTime) {
EntityTreeRenderer* entityTreeRenderer = qApp->getEntities();
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
if (entityTree) {
auto now = usecTimestampNow();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
MovingEntitiesOperator moveOperator(entityTree);
forEachDescendant([&](SpatiallyNestablePointer object) {
// if the queryBox has changed, tell the entity-server
if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success) {
moveOperator.addEntityToMoveList(entity, newCube);
}
if (packetSender) {
EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty();
properties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);
entity->setLastBroadcast(usecTimestampNow());
entityTree->withWriteLock([&] {
auto now = usecTimestampNow();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
MovingEntitiesOperator moveOperator(entityTree);
forEachDescendant([&](SpatiallyNestablePointer object) {
// if the queryBox has changed, tell the entity-server
if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success) {
moveOperator.addEntityToMoveList(entity, newCube);
}
if (packetSender) {
EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty();
properties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);
entity->setLastBroadcast(usecTimestampNow());
}
}
});
// also update the position of children in our local octree
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
entityTree->recurseTreeWithOperator(&moveOperator);
}
});
// also update the position of children in our local octree
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
entityTree->recurseTreeWithOperator(&moveOperator);
}
}
}

View file

@ -97,3 +97,11 @@ bool HMDScriptingInterface::isMounted() const{
auto displayPlugin = qApp->getActiveDisplayPlugin();
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
}
QString HMDScriptingInterface::preferredAudioInput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
}
QString HMDScriptingInterface::preferredAudioOutput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}

View file

@ -34,6 +34,8 @@ public:
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
Q_INVOKABLE QString preferredAudioInput() const;
Q_INVOKABLE QString preferredAudioOutput() const;
public:
HMDScriptingInterface();

View file

@ -18,22 +18,22 @@
QString const ModelOverlay::TYPE = "model";
ModelOverlay::ModelOverlay()
: _model(std::make_shared<Rig>()),
: _model(std::make_shared<Model>(std::make_shared<Rig>())),
_modelTextures(QVariantMap()),
_updateModel(false)
{
_model.init();
_model->init();
_isLoaded = false;
}
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
Volume3DOverlay(modelOverlay),
_model(std::make_shared<Rig>()),
_model(std::make_shared<Model>(std::make_shared<Rig>())),
_modelTextures(QVariantMap()),
_url(modelOverlay->_url),
_updateModel(false)
{
_model.init();
_model->init();
if (_url.isValid()) {
_updateModel = true;
_isLoaded = false;
@ -44,27 +44,27 @@ void ModelOverlay::update(float deltatime) {
if (_updateModel) {
_updateModel = false;
_model.setSnapModelToCenter(true);
_model.setScale(getDimensions());
_model.setRotation(getRotation());
_model.setTranslation(getPosition());
_model.setURL(_url);
_model.simulate(deltatime, true);
_model->setSnapModelToCenter(true);
_model->setScale(getDimensions());
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
_model->setURL(_url);
_model->simulate(deltatime, true);
} else {
_model.simulate(deltatime);
_model->simulate(deltatime);
}
_isLoaded = _model.isActive();
_isLoaded = _model->isActive();
}
bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
Volume3DOverlay::addToScene(overlay, scene, pendingChanges);
_model.addToScene(scene, pendingChanges);
_model->addToScene(scene, pendingChanges);
return true;
}
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges);
_model.removeFromScene(scene, pendingChanges);
_model->removeFromScene(scene, pendingChanges);
}
void ModelOverlay::render(RenderArgs* args) {
@ -73,9 +73,9 @@ void ModelOverlay::render(RenderArgs* args) {
// fix them up in the scene
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
if (_model.needsFixupInScene()) {
_model.removeFromScene(scene, pendingChanges);
_model.addToScene(scene, pendingChanges);
if (_model->needsFixupInScene()) {
_model->removeFromScene(scene, pendingChanges);
_model->addToScene(scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
@ -100,7 +100,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) {
setDimensions(scale);
} else {
_model.setScaleToFit(true, getDimensions());
_model->setScaleToFit(true, getDimensions());
_updateModel = true;
}
}
@ -120,7 +120,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
QUrl newTextureURL = textureMap[key].toUrl();
qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;
QMetaObject::invokeMethod(&_model, "setTextureWithNameToURL", Qt::AutoConnection,
QMetaObject::invokeMethod(_model.get(), "setTextureWithNameToURL", Qt::AutoConnection,
Q_ARG(const QString&, key),
Q_ARG(const QUrl&, newTextureURL));
@ -134,7 +134,7 @@ QVariant ModelOverlay::getProperty(const QString& property) {
return _url.toString();
}
if (property == "dimensions" || property == "scale" || property == "size") {
return vec3toVariant(_model.getScaleToFitDimensions());
return vec3toVariant(_model->getScaleToFitDimensions());
}
if (property == "textures") {
if (_modelTextures.size() > 0) {
@ -155,13 +155,13 @@ bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3&
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
QString subMeshNameTemp;
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp);
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp);
}
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) {
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo);
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo);
}
ModelOverlay* ModelOverlay::createClone() const {

View file

@ -41,11 +41,11 @@ public:
private:
Model _model;
ModelPointer _model;
QVariantMap _modelTextures;
QUrl _url;
bool _updateModel;
};
#endif // hifi_ModelOverlay_h
#endif // hifi_ModelOverlay_h

View file

@ -36,7 +36,7 @@ AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared);
return QSharedPointer<Resource>(new Animation(url), &Resource::deleter);
}
Animation::Animation(const QUrl& url) : Resource(url) {}

View file

@ -1056,7 +1056,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
// limit rotation
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) {
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
}
// directly set absolutePose rotation
_internalPoseSet._absolutePoses[index].rot = deltaQuat * headQuat;

View file

@ -221,6 +221,8 @@ public:
void setEnableInverseKinematics(bool enable);
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
protected:
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
void updateAnimationStateHandlers();

View file

@ -50,6 +50,9 @@
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; };
static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; };
Setting::Handle<bool> dynamicJitterBuffers("dynamicJitterBuffers", DEFAULT_DYNAMIC_JITTER_BUFFERS);
Setting::Handle<int> maxFramesOverDesired("maxFramesOverDesired", DEFAULT_MAX_FRAMES_OVER_DESIRED);
Setting::Handle<int> staticDesiredJitterBufferFrames("staticDesiredJitterBufferFrames",
@ -103,7 +106,9 @@ AudioClient::AudioClient() :
_outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_receivedAudioStream, this),
_stats(&_receivedAudioStream),
_inputGate()
_inputGate(),
_positionGetter(DEFAULT_POSITION_GETTER),
_orientationGetter(DEFAULT_ORIENTATION_GETTER)
{
// clear the array of locally injected samples
memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
@ -170,6 +175,50 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio
return (numSourceSamples * ratio) + 0.5f;
}
#ifdef Q_OS_WIN
QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) {
QString deviceName;
IPropertyStore* pPropertyStore;
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
pEndpoint->Release();
pEndpoint = NULL;
PROPVARIANT pv;
PropVariantInit(&pv);
HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
pPropertyStore->Release();
pPropertyStore = NULL;
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
if (!IsWindows8OrGreater()) {
// Windows 7 provides only the 31 first characters of the device name.
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
}
PropVariantClear(&pv);
return deviceName;
}
QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) {
QString deviceName;
HRESULT hr = S_OK;
CoInitialize(NULL);
IMMDeviceEnumerator* pMMDeviceEnumerator = NULL;
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
IMMDevice* pEndpoint;
hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint);
if (hr == E_NOTFOUND) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
} else {
deviceName = ::friendlyNameForAudioDevice(pEndpoint);
}
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = NULL;
CoUninitialize();
return deviceName;
}
#endif
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
#ifdef __APPLE__
if (QAudioDeviceInfo::availableDevices(mode).size() > 1) {
@ -243,23 +292,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
printf("Audio Error: device not found\n");
deviceName = QString("NONE");
} else {
IPropertyStore* pPropertyStore;
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
pEndpoint->Release();
pEndpoint = NULL;
PROPVARIANT pv;
PropVariantInit(&pv);
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
pPropertyStore->Release();
pPropertyStore = NULL;
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
if (!IsWindows8OrGreater()) {
// Windows 7 provides only the 31 first characters of the device name.
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
}
qCDebug(audioclient) << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName;
PropVariantClear(&pv);
deviceName = friendlyNameForAudioDevice(pEndpoint);
}
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = NULL;
@ -550,7 +583,7 @@ void AudioClient::configureReverb() {
p.wetDryMix = 100.0f;
p.preDelay = 0.0f;
p.earlyGain = -96.0f; // disable ER
p.lateGain -= 12.0f; // quieter than listener reverb
p.lateGain += _reverbOptions->getWetDryMix() * (24.0f/100.0f) - 24.0f; // -0dB to -24dB, based on wetDryMix
p.lateMixLeft = 0.0f;
p.lateMixRight = 0.0f;

View file

@ -16,6 +16,7 @@
#include <memory>
#include <vector>
#include <QtCore/qsystemdetection.h>
#include <QtCore/QByteArray>
#include <QtCore/QElapsedTimer>
#include <QtCore/QObject>
@ -126,6 +127,10 @@ public:
static const float CALLBACK_ACCELERATOR_RATIO;
#ifdef Q_OS_WIN
static QString friendlyNameForAudioDevice(wchar_t* guid);
#endif
public slots:
void start();
void stop();

View file

@ -36,5 +36,5 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
qCDebug(audio) << "Requesting sound at" << url.toString();
return QSharedPointer<Resource>(new Sound(url), &Resource::allReferencesCleared);
return QSharedPointer<Resource>(new Sound(url), &Resource::deleter);
}

View file

@ -81,6 +81,9 @@ namespace controller {
// Triggers
LT,
RT,
// Grips (Oculus touch squeeze)
LG,
RG,
NUM_STANDARD_AXES,
LZ = LT,
RZ = RT

View file

@ -552,9 +552,9 @@ float OpenGLDisplayPlugin::presentRate() {
{
Lock lock(_mutex);
result = _usecsPerFrame.getAverage();
result = 1.0f / result;
result *= USECS_PER_SECOND;
}
result = 1.0f / result;
result *= USECS_PER_SECOND;
return result;
}

View file

@ -17,9 +17,9 @@
#include <GLMHelpers.h>
#include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
#define THREADED_PRESENT 1
#include <gl/GLEscrow.h>
class OpenGLDisplayPlugin : public DisplayPlugin {
protected:

View file

@ -69,10 +69,11 @@ void HmdDisplayPlugin::compositeOverlay() {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
auto eyePoses = _currentPresentFrameInfo.eyePoses;
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
auto modelView = glm::inverse(eyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
@ -95,10 +96,10 @@ void HmdDisplayPlugin::compositePointer() {
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
auto headPosition = vec3(_currentPresentFrameInfo.headPose[3]);
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
auto reticleTransform = compositorHelper->getReticleTransform(_currentPresentFrameInfo.eyePoses[eye], headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
@ -160,15 +161,28 @@ void HmdDisplayPlugin::internalPresent() {
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
Lock lock(_mutex);
_renderEyePoses[frameIndex][eye] = pose;
FrameInfo& frame = _frameInfos[frameIndex];
frame.eyePoses[eye] = pose;
}
void HmdDisplayPlugin::updateFrameData() {
// Check if we have old frame data to discard
{
Lock lock(_mutex);
auto itr = _frameInfos.find(_currentRenderFrameIndex);
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
}
Parent::updateFrameData();
Lock lock(_mutex);
_currentRenderEyePoses = _renderEyePoses[_currentRenderFrameIndex];
{
Lock lock(_mutex);
_currentPresentFrameInfo = _frameInfos[_currentRenderFrameIndex];
}
}
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
return _headPoseCache.get();
return _currentRenderFrameInfo.get().headPose;
}

View file

@ -28,6 +28,16 @@ public:
virtual glm::mat4 getHeadPose() const override;
using EyePoses = std::array<glm::mat4, 2>;
struct FrameInfo {
EyePoses eyePoses;
glm::mat4 headPose;
double sensorSampleTime { 0 };
double predictedDisplayTime { 0 };
};
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
@ -46,10 +56,10 @@ protected:
glm::mat4 _cullingProjection;
glm::uvec2 _renderTargetSize;
float _ipd { 0.064f };
using EyePoses = std::array<glm::mat4, 2>;
QMap<uint32_t, EyePoses> _renderEyePoses;
EyePoses _currentRenderEyePoses;
ThreadSafeValueCache<glm::mat4> _headPoseCache { glm::mat4() };
QMap<uint32_t, FrameInfo> _frameInfos;
FrameInfo _currentPresentFrameInfo;
ThreadSafeValueCache<FrameInfo> _currentRenderFrameInfo;
private:
bool _enablePreview { false };

View file

@ -130,6 +130,7 @@ void EntityTreeRenderer::setTree(OctreePointer newTree) {
}
void EntityTreeRenderer::update() {
PerformanceTimer perfTimer("ETRupdate");
if (_tree && !_shuttingDown) {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
tree->update();
@ -159,12 +160,14 @@ void EntityTreeRenderer::update() {
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar) {
bool didUpdate = false;
float radius = 1.0f; // for now, assume 1 meter radius
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
QVector<EntityItemPointer> foundEntities;
// find the entities near us
// don't let someone else change our tree while we search
_tree->withReadLock([&] {
// FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster
std::static_pointer_cast<EntityTree>(_tree)->findEntities(avatarPosition, radius, foundEntities);
// Whenever you're in an intersection between zones, we will always choose the smallest zone.
@ -173,36 +176,37 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
_bestZoneVolume = std::numeric_limits<float>::max();
// create a list of entities that actually contain the avatar's position
foreach(EntityItemPointer entity, foundEntities) {
if (entity->contains(avatarPosition)) {
if (entitiesContainingAvatar) {
*entitiesContainingAvatar << entity->getEntityItemID();
}
for (auto& entity : foundEntities) {
auto isZone = entity->getType() == EntityTypes::Zone;
auto hasScript = !entity->getScript().isEmpty();
// if this entity is a zone, use this time to determine the bestZone
if (entity->getType() == EntityTypes::Zone) {
if (!entity->getVisible()) {
#ifdef WANT_DEBUG
qCDebug(entitiesrenderer) << "not visible";
#endif
} else {
// only consider entities that are zones or have scripts, all other entities can
// be ignored because they can have events fired on them.
// FIXME - this could be optimized further by determining if the script is loaded
// and if it has either an enterEntity or leaveEntity method
if (isZone || hasScript) {
// now check to see if the point contains our entity, this can be expensive if
// the entity has a collision hull
if (entity->contains(avatarPosition)) {
if (entitiesContainingAvatar) {
*entitiesContainingAvatar << entity->getEntityItemID();
}
// if this entity is a zone and visible, determine if it is the bestZone
if (isZone && entity->getVisible()) {
float entityVolumeEstimate = entity->getVolumeEstimate();
if (entityVolumeEstimate < _bestZoneVolume) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
}
else if (entityVolumeEstimate == _bestZoneVolume) {
} else if (entityVolumeEstimate == _bestZoneVolume) {
// in the case of the volume being equal, we will use the
// EntityItemID to deterministically pick one entity over the other
if (!_bestZone) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
}
else {
// in the case of the volume being equal, we will use the
// EntityItemID to deterministically pick one entity over the other
if (entity->getEntityItemID() < _bestZone->getEntityItemID()) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
}
} else if (entity->getEntityItemID() < _bestZone->getEntityItemID()) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
}
}
}
@ -217,13 +221,24 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
});
return didUpdate;
}
bool EntityTreeRenderer::checkEnterLeaveEntities() {
PerformanceTimer perfTimer("checkEnterLeaveEntities");
auto now = usecTimestampNow();
bool didUpdate = false;
if (_tree && !_shuttingDown) {
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
if (avatarPosition != _lastAvatarPosition) {
// we want to check our enter/leave state if we've moved a significant amount, or
// if some amount of time has elapsed since we last checked. We check the time
// elapsed because zones or entities might have been created "around us" while we've
// been stationary
auto movedEnough = glm::distance(avatarPosition, _lastAvatarPosition) > ZONE_CHECK_DISTANCE;
auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL;
if (movedEnough || enoughTimeElapsed) {
_lastZoneCheck = now;
QVector<EntityItemID> entitiesContainingAvatar;
didUpdate = findBestZoneAndMaybeContainingEntities(avatarPosition, &entitiesContainingAvatar);
@ -248,8 +263,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
}
_currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition;
} else {
didUpdate = findBestZoneAndMaybeContainingEntities(avatarPosition, nullptr);
}
}
return didUpdate;

View file

@ -178,6 +178,10 @@ private:
std::shared_ptr<ZoneEntityItem> _bestZone;
float _bestZoneVolume;
quint64 _lastZoneCheck { 0 };
const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz
const float ZONE_CHECK_DISTANCE = 0.001f;
glm::vec3 _previousKeyLightColor;
float _previousKeyLightIntensity;
float _previousKeyLightAmbientIntensity;

View file

@ -105,7 +105,7 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c
QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
// If textures are unset, revert to original textures
if (textures == "") {
if (textures.isEmpty()) {
return _originalTextures;
}
@ -116,11 +116,19 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
QJsonParseError error;
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
// If textures are invalid, revert to original textures
if (error.error != QJsonParseError::NoError) {
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures;
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures;
return _originalTextures;
}
return texturesJson.object().toVariantMap();
QVariantMap texturesMap = texturesJson.toVariant().toMap();
// If textures are unset, revert to original textures
if (texturesMap.isEmpty()) {
return _originalTextures;
}
return texturesJson.toVariant().toMap();
}
void RenderableModelEntityItem::remapTextures() {
@ -132,21 +140,25 @@ void RenderableModelEntityItem::remapTextures() {
return; // nothing to do if the model has not yet loaded
}
auto& geometry = _model->getGeometry()->getGeometry();
if (!_originalTexturesRead) {
_originalTextures = geometry->getTextures();
_originalTextures = _model->getTextures();
_originalTexturesRead = true;
// Default to _originalTextures to avoid remapping immediately and lagging on load
_currentTextures = _originalTextures;
}
auto textures = parseTexturesToMap(_textures);
auto textures = getTextures();
if (textures == _lastTextures) {
return;
}
if (textures != _currentTextures) {
geometry->setTextures(textures);
_currentTextures = textures;
_lastTextures = textures;
auto newTextures = parseTexturesToMap(textures);
if (newTextures != _currentTextures) {
_model->setTextures(newTextures);
_currentTextures = newTextures;
}
}
@ -359,41 +371,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
assert(getType() == EntityTypes::Model);
if (hasModel()) {
if (_model) {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges;
_model->removeFromScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
scene->enqueuePendingChanges(pendingChanges);
}
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
// the renderable item. As it stands now the model checks it's visible/invisible state
// so most of the time we don't do anything in this function.
_model->setVisibleInScene(getVisible(), scene);
}
remapTextures();
// Prepare the current frame
{
// float alpha = getLocalRenderAlpha();
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
getModel(renderer);
// Remap textures immediately after loading to avoid flicker
remapTextures();
}
if (_model) {
@ -424,15 +411,40 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
}
});
updateModelBounds();
}
}
// Check if the URL has changed
// Do this last as the getModel is queued for the next frame,
// and we need to keep state directing the model to reinitialize
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
// Defer setting the url to the render thread
getModel(_myRenderer);
}
// Enqueue updates for the next frame
if (_model) {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
// the renderable item. As it stands now the model checks it's visible/invisible state
// so most of the time we don't do anything in this function.
_model->setVisibleInScene(getVisible(), scene);
// Remap textures for the next frame to avoid flicker
remapTextures();
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges;
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
scene->enqueuePendingChanges(pendingChanges);
}
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
// Defer setting the url to the render thread
getModel(_myRenderer);
}
}
} else {

View file

@ -90,6 +90,7 @@ private:
bool _needsInitialSimulation = true;
bool _needsModelReload = true;
EntityTreeRenderer* _myRenderer = nullptr;
QString _lastTextures;
QVariantMap _currentTextures;
QVariantMap _originalTextures;
bool _originalTexturesRead = false;

View file

@ -55,8 +55,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
qWarning() << "Too many concurrent web views to create new view";
return false;
}
qDebug() << "Building web surface";
++_currentWebCount;
// Save the original GL context, because creating a QML surface will create a new context
QOpenGLContext * currentContext = QOpenGLContext::currentContext();

View file

@ -1010,6 +1010,10 @@ EntityTreePointer EntityItem::getTree() const {
return tree;
}
SpatialParentTree* EntityItem::getParentTree() const {
return getTree().get();
}
bool EntityItem::wantTerseEditLogging() const {
EntityTreePointer tree = getTree();
return tree ? tree->wantTerseEditLogging() : false;
@ -1978,3 +1982,25 @@ void EntityItem::dimensionsChanged() {
requiresRecalcBoxes();
SpatiallyNestable::dimensionsChanged(); // Do what you have to do
}
void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const {
bool success;
auto globalPosition = getPosition(success);
if (success) {
properties.setPosition(globalPosition + offset);
properties.setRotation(getRotation());
properties.setDimensions(getDimensions());
// Should we do velocities and accelerations, too? This could end up being quite involved, which is why the method exists.
} else {
properties.setPosition(getQueryAACube().calcCenter() + offset); // best we can do
}
if (!messageTemplate.isEmpty()) {
QString name = properties.getName();
if (name.isEmpty()) {
name = EntityTypes::getEntityTypeName(properties.getType());
}
qCWarning(entities) << messageTemplate.arg(getEntityItemID().toString()).arg(name).arg(properties.getParentID().toString());
}
QUuid empty;
properties.setParentID(empty);
}

View file

@ -86,6 +86,8 @@ public:
/// returns true if something changed
virtual bool setProperties(const EntityItemProperties& properties);
// Update properties with empty parent id and globalized/absolute values (applying offset), and apply (non-empty) log template to args id, name-or-type, parent id.
void globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate = QString(), const glm::vec3& offset = glm::vec3(0.0f)) const;
/// Override this in your derived class if you'd like to be informed when something about the state of the entity
/// has changed. This will be called with properties change or when new data is loaded from a stream
@ -359,6 +361,7 @@ public:
void setPhysicsInfo(void* data) { _physicsInfo = data; }
EntityTreeElementPointer getElement() const { return _element; }
EntityTreePointer getTree() const;
virtual SpatialParentTree* getParentTree() const;
bool wantTerseEditLogging() const;
glm::mat4 getEntityToWorldMatrix() const;

View file

@ -24,7 +24,6 @@
#include "EntitiesLogging.h"
#include "RecurseOctreeToMapOperator.h"
#include "LogHandler.h"
#include "RemapIDOperator.h"
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
@ -1317,42 +1316,59 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
float x, float y, float z) {
SendEntitiesOperationArgs args;
args.packetSender = packetSender;
args.localTree = localTree;
args.ourTree = this;
args.otherTree = localTree;
args.root = glm::vec3(x, y, z);
QVector<EntityItemID> newEntityIDs;
args.newEntityIDs = &newEntityIDs;
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we use new identifiers.
// We need to keep a map so that we can map parent identifiers correctly.
QHash<EntityItemID, EntityItemID> map;
args.map = &map;
recurseTreeWithOperation(sendEntitiesOperation, &args);
packetSender->releaseQueuedMessages();
return newEntityIDs;
return map.values().toVector();
}
bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) {
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
EntityItemID newID(QUuid::createUuid());
args->newEntityIDs->append(newID);
EntityItemProperties properties = entityItem->getProperties();
properties.setPosition(properties.getPosition() + args->root);
std::function<const EntityItemID(EntityItemPointer&)> getMapped = [&](EntityItemPointer& item) -> const EntityItemID {
EntityItemID oldID = item->getEntityItemID();
if (args->map->contains(oldID)) { // Already been handled (e.g., as a parent of somebody that we've processed).
return args->map->value(oldID);
}
EntityItemID newID = QUuid::createUuid();
EntityItemProperties properties = item->getProperties();
EntityItemID oldParentID = properties.getParentID();
if (oldParentID.isInvalidID()) { // no parent
properties.setPosition(properties.getPosition() + args->root);
} else {
EntityItemPointer parentEntity = args->ourTree->findEntityByEntityItemID(oldParentID);
if (parentEntity) { // map the parent
// Warning: (non-tail) recursion of getMapped could blow the call stack if the parent hierarchy is VERY deep.
properties.setParentID(getMapped(parentEntity));
// But do not add root offset in this case.
} else { // Should not happen, but let's try to be helpful...
item->globalizeProperties(properties, "Cannot find %3 parent of %2 %1", args->root);
}
}
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
// queue the packet to send to the server
args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties);
// also update the local tree instantly (note: this is not our tree, but an alternate tree)
if (args->localTree) {
args->localTree->withWriteLock([&] {
args->localTree->addEntity(newID, properties);
if (args->otherTree) {
args->otherTree->withWriteLock([&] {
args->otherTree->addEntity(newID, properties);
});
}
});
return true;
}
args->map->insert(oldID, newID);
return newID;
};
void EntityTree::remapIDs() {
RemapIDOperator theOperator;
recurseTreeWithOperator(&theOperator);
entityTreeElement->forEachEntity(getMapped);
return true;
}
bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,
@ -1393,7 +1409,6 @@ bool EntityTree::readFromMap(QVariantMap& map) {
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
}
}
return true;
}

View file

@ -16,6 +16,7 @@
#include <QVector>
#include <Octree.h>
#include <SpatialParentFinder.h>
class EntityTree;
typedef std::shared_ptr<EntityTree> EntityTreePointer;
@ -46,13 +47,14 @@ public:
class SendEntitiesOperationArgs {
public:
glm::vec3 root;
EntityTreePointer localTree;
EntityTree* ourTree;
EntityTreePointer otherTree;
EntityEditPacketSender* packetSender;
QVector<EntityItemID>* newEntityIDs;
QHash<EntityItemID, EntityItemID>* map;
};
class EntityTree : public Octree {
class EntityTree : public Octree, public SpatialParentTree {
Q_OBJECT
public:
EntityTree(bool shouldReaverage = false);
@ -125,6 +127,7 @@ public:
EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius);
EntityItemPointer findEntityByID(const QUuid& id);
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID);
virtual SpatiallyNestablePointer findByID(const QUuid& id) { return findEntityByID(id); }
EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID
@ -200,8 +203,6 @@ public:
bool wantTerseEditLogging() const { return _wantTerseEditLogging; }
void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; }
void remapIDs();
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,
bool skipThoseWithBadParents) override;
virtual bool readFromMap(QVariantMap& entityDescription) override;

View file

@ -42,6 +42,17 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(
_color[0] = _color[1] = _color[2] = 0;
}
const QString ModelEntityItem::getTextures() const {
QReadLocker locker(&_texturesLock);
auto textures = _textures;
return textures;
}
void ModelEntityItem::setTextures(const QString& textures) {
QWriteLocker locker(&_texturesLock);
_textures = textures;
}
EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);

View file

@ -110,8 +110,8 @@ public:
float getAnimationFPS() const { return _animationLoop.getFPS(); }
static const QString DEFAULT_TEXTURES;
const QString& getTextures() const { return _textures; }
void setTextures(const QString& textures) { _textures = textures; }
const QString getTextures() const;
void setTextures(const QString& textures);
virtual bool shouldBePhysical() const;
@ -159,7 +159,9 @@ protected:
AnimationPropertyGroup _animationProperties;
AnimationLoop _animationLoop;
mutable QReadWriteLock _texturesLock;
QString _textures;
ShapeType _shapeType = SHAPE_TYPE_NONE;
// used on client side

View file

@ -561,6 +561,12 @@ bool ParticleEffectEntityItem::needsToCallUpdate() const {
void ParticleEffectEntityItem::update(const quint64& now) {
// we check for 'now' in the past in case users set their clock backward
if (now < _lastSimulated) {
_lastSimulated = now;
return;
}
float deltaTime = (float)(now - _lastSimulated) / (float)USECS_PER_SECOND;
_lastSimulated = now;

View file

@ -1,33 +0,0 @@
//
// RemapIDOperator.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-12-6.
// 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 "EntityTree.h"
#include "RemapIDOperator.h"
QUuid RemapIDOperator::remap(const QUuid& oldID) {
if (oldID.isNull()) {
return oldID;
}
if (!_oldToNew.contains(oldID)) {
_oldToNew[oldID] = QUuid::createUuid();
}
return _oldToNew[oldID];
}
bool RemapIDOperator::postRecursion(OctreeElementPointer element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
entityItem->setID(remap(entityItem->getID()));
entityItem->setParentID(remap(entityItem->getParentID()));
});
return true;
}

View file

@ -1,30 +0,0 @@
//
// RemapIDOperator.h
// libraries/entities/src
//
// Created by Seth Alves on 2015-12-6.
// 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_RemapIDOperator_h
#define hifi_RemapIDOperator_h
#include "Octree.h"
// this will change all the IDs in an EntityTree. Parent/Child relationships are maintained.
class RemapIDOperator : public RecurseOctreeOperator {
public:
RemapIDOperator() : RecurseOctreeOperator() {}
~RemapIDOperator() {}
virtual bool preRecursion(OctreeElementPointer element) { return true; }
virtual bool postRecursion(OctreeElementPointer element);
private:
QUuid remap(const QUuid& oldID);
QHash<QUuid, QUuid> _oldToNew;
};
#endif // hifi_RemapIDOperator_h

View file

@ -38,11 +38,13 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
_context->setShareContext(sharedContext);
}
_context->setFormat(getDefaultOpenGLSurfaceFormat());
if (_context->create()) {
_offscreenSurface->setFormat(_context->format());
_offscreenSurface->create();
return true;
return _offscreenSurface->isValid();
}
qWarning("Failed to create OffscreenGLCanvas context");
return false;
}

View file

@ -70,7 +70,7 @@ public:
virtual bool event(QEvent *e) override;
protected:
class Queue : public QQueue<QEvent*> {
class Queue : private QQueue<QEvent*> {
public:
void add(QEvent::Type type);
QEvent* take();
@ -134,10 +134,11 @@ QEvent* OffscreenQmlRenderThread::Queue::take() {
}
OffscreenQmlRenderThread::OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) {
qDebug() << "Building QML Renderer";
if (!_canvas.create(shareContext)) {
static const char* error = "Failed to create OffscreenGLCanvas";
qWarning() << error;
throw error;
qWarning("Failed to create OffscreenGLCanvas");
_quit = true;
return;
};
_renderControl = new QMyQuickRenderControl();
@ -160,6 +161,8 @@ OffscreenQmlRenderThread::OffscreenQmlRenderThread(OffscreenQmlSurface* surface,
}
void OffscreenQmlRenderThread::run() {
qDebug() << "Starting QML Renderer thread";
while (!_quit) {
QEvent* e = _queue.take();
event(e);
@ -208,12 +211,14 @@ void OffscreenQmlRenderThread::setupFbo() {
}
void OffscreenQmlRenderThread::init() {
qDebug() << "Initializing QML Renderer";
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
if (!_canvas.makeCurrent()) {
// Failed to make GL context current, this OffscreenQmlSurface is basically dead
qWarning("Failed to make context current on QML Renderer Thread");
_quit = true;
return;
}
@ -360,6 +365,8 @@ void OffscreenQmlSurface::onAboutToQuit() {
}
void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
qDebug() << "Building QML surface";
_renderer = new OffscreenQmlRenderThread(this, shareContext);
_renderer->moveToThread(_renderer);
_renderer->setObjectName("QML Renderer Thread");

View file

@ -35,6 +35,7 @@ public:
int _RSNumTextureBounded = 0;
int _DSNumAPIDrawcalls = 0;
int _DSNumDrawcalls = 0;
int _DSNumTriangles = 0;

View file

@ -326,6 +326,7 @@ void GLBackend::do_draw(Batch& batch, size_t paramOffset) {
glDrawArrays(mode, startVertex, numVertices);
_stats._DSNumTriangles += numVertices / 3;
_stats._DSNumDrawcalls++;
_stats._DSNumAPIDrawcalls++;
(void)CHECK_GL_ERROR();
}
@ -344,6 +345,7 @@ void GLBackend::do_drawIndexed(Batch& batch, size_t paramOffset) {
glDrawElements(mode, numIndices, glType, indexBufferByteOffset);
_stats._DSNumTriangles += numIndices / 3;
_stats._DSNumDrawcalls++;
_stats._DSNumAPIDrawcalls++;
(void) CHECK_GL_ERROR();
}
@ -358,6 +360,7 @@ void GLBackend::do_drawInstanced(Batch& batch, size_t paramOffset) {
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
_stats._DSNumTriangles += (numInstances * numVertices) / 3;
_stats._DSNumDrawcalls += numInstances;
_stats._DSNumAPIDrawcalls++;
(void) CHECK_GL_ERROR();
}
@ -383,6 +386,7 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) {
#endif
_stats._DSNumTriangles += (numInstances * numIndices) / 3;
_stats._DSNumDrawcalls += numInstances;
_stats._DSNumAPIDrawcalls++;
(void)CHECK_GL_ERROR();
}
@ -395,6 +399,8 @@ void GLBackend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) {
glMultiDrawArraysIndirect(mode, reinterpret_cast<GLvoid*>(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride);
_stats._DSNumDrawcalls += commandCount;
_stats._DSNumAPIDrawcalls++;
#else
// FIXME implement the slow path
#endif
@ -410,7 +416,7 @@ void GLBackend::do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) {
glMultiDrawElementsIndirect(mode, indexType, reinterpret_cast<GLvoid*>(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride);
_stats._DSNumDrawcalls += commandCount;
_stats._DSNumAPIDrawcalls++;
#else
// FIXME implement the slow path
#endif

View file

@ -158,7 +158,6 @@ public:
~GLState();
// The state commands to reset to default,
// WARNING depending on the order of the State::Field enum
static const Commands _resetStateCommands;
friend class GLBackend;

View file

@ -35,6 +35,7 @@ const GLBackend::GLState::Commands makeResetStateCommands();
const GLBackend::GLState::Commands GLBackend::GLState::_resetStateCommands = makeResetStateCommands();
// NOTE: This must stay in sync with the ordering of the State::Field enum
const GLBackend::GLState::Commands makeResetStateCommands() {
// Since State::DEFAULT is a static defined in another .cpp the initialisation order is random
// and we have a 50/50 chance that State::DEFAULT is not yet initialized.
@ -69,9 +70,9 @@ const GLBackend::GLState::Commands makeResetStateCommands() {
CommandPointer(stencilCommand),
CommandPointer(stencilCommand),
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable),
std::make_shared<Command1U>(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask),
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable),
std::make_shared<CommandBlend>(&GLBackend::do_setStateBlend, DEFAULT.blendFunction),
@ -609,6 +610,8 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) {
glDisable(GL_POLYGON_OFFSET_LINE);
glDisable(GL_POLYGON_OFFSET_POINT);
}
(void) CHECK_GL_ERROR();
_pipeline._stateCache.depthBias = bias.x;
_pipeline._stateCache.depthBiasSlopeScale = bias.y;
}
@ -689,6 +692,7 @@ void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) {
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
(void) CHECK_GL_ERROR();
_pipeline._stateCache.alphaToCoverageEnable = enable;
}
}
@ -702,6 +706,7 @@ void GLBackend::do_setStateSampleMask(uint32 mask) {
glEnable(GL_SAMPLE_MASK);
glSampleMaski(0, mask);
}
(void) CHECK_GL_ERROR();
#endif
_pipeline._stateCache.sampleMask = mask;
}
@ -742,10 +747,10 @@ void GLBackend::do_setStateBlend(State::BlendFunction function) {
glBlendFuncSeparate(BLEND_ARGS[function.getSourceColor()], BLEND_ARGS[function.getDestinationColor()],
BLEND_ARGS[function.getSourceAlpha()], BLEND_ARGS[function.getDestinationAlpha()]);
(void) CHECK_GL_ERROR();
} else {
glDisable(GL_BLEND);
}
(void) CHECK_GL_ERROR();
_pipeline._stateCache.blendFunction = function;
}
@ -757,6 +762,7 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) {
mask & State::ColorMask::WRITE_GREEN,
mask & State::ColorMask::WRITE_BLUE,
mask & State::ColorMask::WRITE_ALPHA );
(void) CHECK_GL_ERROR();
_pipeline._stateCache.colorWriteMask = mask;
}
@ -764,7 +770,6 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) {
void GLBackend::do_setStateBlendFactor(Batch& batch, size_t paramOffset) {
Vec4 factor(batch._params[paramOffset + 0]._float,
batch._params[paramOffset + 1]._float,
batch._params[paramOffset + 2]._float,

View file

@ -225,6 +225,12 @@ public:
}
break;
case gpu::R11G11B10:
texel.format = GL_RGB;
// the type should be float
texel.internalFormat = GL_R11F_G11F_B10F;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
texel.internalFormat = GL_DEPTH_COMPONENT;
@ -302,11 +308,6 @@ public:
case gpu::SRGBA:
texel.internalFormat = GL_SRGB; // standard 2.2 gamma correction color
break;
case gpu::R11G11B10: {
// the type should be float
texel.internalFormat = GL_R11F_G11F_B10F;
break;
}
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}

View file

@ -345,6 +345,7 @@ public:
uint8 getColorWriteMask() const { return _values.colorWriteMask; }
// All the possible fields
// NOTE: If you change this, you must update GLBackend::GLState::_resetStateCommands
enum Field {
FILL_MODE,
CULL_MODE,
@ -364,6 +365,7 @@ public:
STENCIL_TEST_BACK,
SAMPLE_MASK,
ALPHA_TO_COVERAGE_ENABLE,
BLEND_FUNCTION,

View file

@ -285,7 +285,7 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt
}
// Here the Texture has been fully defined from the gpu point of view (size and format)
_defined = true;
_defined = true;
} else {
_stamp++;
}
@ -457,6 +457,14 @@ uint32 Texture::getStoredMipSize(uint16 level) const {
return 0;
}
gpu::Resource::Size Texture::getStoredSize() const {
auto size = 0;
for (int level = 0; level < evalNumMips(); ++level) {
size += getStoredMipSize(level);
}
return size;
}
uint16 Texture::evalNumSamplesUsed(uint16 numSamplesTried) {
uint16 sample = numSamplesTried;
if (numSamplesTried <= 1)

View file

@ -288,9 +288,12 @@ public:
Stamp getStamp() const { return _stamp; }
Stamp getDataStamp() const { return _storage->getStamp(); }
// The size in bytes of data stored in the texture
// The theoretical size in bytes of data stored in the texture
Size getSize() const { return _size; }
// The actual size in bytes of data stored in the texture
Size getStoredSize() const;
// Resize, unless auto mips mode would destroy all the sub mips
Size resize1D(uint16 width, uint16 numSamples);
Size resize2D(uint16 width, uint16 height, uint16 numSamples);

View file

@ -66,16 +66,15 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
GeometryExtra extra{ mapping, textureBaseUrl };
// Get the raw GeometryResource, not the wrapped NetworkGeometry
_geometryResource = modelCache->getResource(url, QUrl(), true, &extra).staticCast<GeometryResource>();
_geometryResource = modelCache->getResource(url, QUrl(), false, &extra).staticCast<GeometryResource>();
// Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false;
if (_geometryResource->isLoaded()) {
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
} else {
connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded);
}
// Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false;
}
}
@ -86,6 +85,10 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
_meshes = _geometryResource->_meshes;
_materials = _geometryResource->_materials;
}
// Avoid holding onto extra references
_geometryResource.reset();
finishedLoading(success);
}
@ -157,7 +160,7 @@ class GeometryDefinitionResource : public GeometryResource {
Q_OBJECT
public:
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {}
GeometryResource(url, textureBaseUrl.isValid() ? textureBaseUrl : url), _mapping(mapping) {}
virtual void downloadFinished(const QByteArray& data) override;
@ -166,7 +169,6 @@ protected:
private:
QVariantHash _mapping;
QUrl _textureBaseUrl;
};
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
@ -220,13 +222,20 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl);
}
return QSharedPointer<Resource>(resource, &Resource::allReferencesCleared);
return QSharedPointer<Resource>(resource, &Resource::deleter);
}
std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
return std::make_shared<NetworkGeometry>(resource);
if (resource) {
if (resource->isLoaded() && !resource->hasTextures()) {
resource->setTextures();
}
return std::make_shared<NetworkGeometry>(resource);
} else {
return NetworkGeometry::Pointer();
}
}
const QVariantMap Geometry::getTextures() const {
@ -270,6 +279,9 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
material->setTextures(textureMap);
_areTexturesLoaded = false;
// If we only use cached textures, they should all be loaded
areTexturesLoaded();
}
}
} else {
@ -279,8 +291,6 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
bool Geometry::areTexturesLoaded() const {
if (!_areTexturesLoaded) {
_hasTransparentTextures = false;
for (auto& material : _materials) {
// Check if material textures are loaded
if (std::any_of(material->_textures.cbegin(), material->_textures.cend(),
@ -293,8 +303,6 @@ bool Geometry::areTexturesLoaded() const {
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) {
material->resetOpacityMap();
_hasTransparentTextures |= material->getKey().isTranslucent();
}
}
@ -313,6 +321,21 @@ const std::shared_ptr<const NetworkMaterial> Geometry::getShapeMaterial(int shap
return nullptr;
}
void GeometryResource::deleter() {
resetTextures();
Resource::deleter();
}
void GeometryResource::setTextures() {
for (const FBXMaterial& material : _geometry->materials) {
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
}
}
void GeometryResource::resetTextures() {
_materials.clear();
}
NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) {
connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished);
connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed);

View file

@ -74,9 +74,6 @@ public:
void setTextures(const QVariantMap& textureMap);
virtual bool areTexturesLoaded() const;
// Returns true if any albedo texture has a non-masking alpha channel.
// This can only be known after areTexturesLoaded().
bool hasTransparentTextures() const { return _hasTransparentTextures; }
protected:
friend class GeometryMappingResource;
@ -91,7 +88,6 @@ protected:
private:
mutable bool _areTexturesLoaded { false };
mutable bool _hasTransparentTextures { false };
};
/// A geometry loaded from the network.
@ -99,15 +95,24 @@ class GeometryResource : public Resource, public Geometry {
public:
using Pointer = QSharedPointer<GeometryResource>;
GeometryResource(const QUrl& url) : Resource(url) {}
GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) : Resource(url) {}
virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); }
virtual void deleter() override;
protected:
friend class ModelCache;
friend class GeometryMappingResource;
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
// Geometries may not hold onto textures while cached - that is for the texture cache
bool hasTextures() const { return !_materials.empty(); }
void setTextures();
void resetTextures();
QUrl _textureBaseUrl;
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
bool _isCacheable { true };
};

View file

@ -28,6 +28,6 @@ NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
}
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared);
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::deleter);
}

View file

@ -166,12 +166,11 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path) {
return texture;
}
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content),
&Resource::allReferencesCleared);
&Resource::deleter);
}
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
@ -339,10 +338,13 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth,
if (gpuTexture) {
_width = gpuTexture->getWidth();
_height = gpuTexture->getHeight();
setBytes(gpuTexture->getStoredSize());
} else {
// FIXME: If !gpuTexture, we failed to load!
_width = _height = 0;
qWarning() << "Texture did not load";
}
finishedLoading(true);
emit networkTextureCreated(qWeakPointerCast<NetworkTexture, Resource> (_self));

View file

@ -111,7 +111,7 @@ Box Mesh::evalPartBound(int partNum) const {
return box;
}
Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
Box Mesh::evalPartsBound(int partStart, int partEnd) const {
Box totalBound;
auto part = _partBuffer.cbegin<Part>() + partStart;
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;

View file

@ -108,9 +108,9 @@ public:
// evaluate the bounding box of A part
Box evalPartBound(int partNum) const;
// evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter
// the returned box is the bounding box of ALL the evaluated part bounds.
Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
// evaluate the bounding boxes of the parts in the range [start, end]
// the returned box is the bounding box of ALL the evaluated parts bound.
Box evalPartsBound(int partStart, int partEnd) const;
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }

View file

@ -228,7 +228,8 @@ bool AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end
nodeList->sendPacket(std::move(packet), *assetServer);
_pendingRequests[assetServer][messageID] = { callback, progressCallback };
_pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback };
return true;
} else {
@ -326,6 +327,9 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
if (requestIt != messageCallbackMap.end()) {
auto& callbacks = requestIt->second;
// Store message in case we need to disconnect from it later.
callbacks.message = message;
if (message->isComplete()) {
callbacks.completeCallback(true, error, message->readAll());
} else {
@ -550,6 +554,12 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) {
auto messageMapIt = _pendingRequests.find(node);
if (messageMapIt != _pendingRequests.end()) {
for (const auto& value : messageMapIt->second) {
auto& message = value.second.message;
if (message) {
// Disconnect from all signals emitting from the pending message
disconnect(message.data(), nullptr, this, nullptr);
}
value.second.completeCallback(false, AssetServerError::NoError, QByteArray());
}
messageMapIt->second.clear();

View file

@ -86,14 +86,15 @@ private:
ReceivedAssetCallback callback, ProgressCallback progressCallback);
bool uploadAsset(const QByteArray& data, UploadResultCallback callback);
struct GetAssetCallbacks {
struct GetAssetRequestData {
QSharedPointer<ReceivedMessage> message;
ReceivedAssetCallback completeCallback;
ProgressCallback progressCallback;
};
static MessageID _currentID;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, MappingOperationCallback>> _pendingMappingRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetAssetCallbacks>> _pendingRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetAssetRequestData>> _pendingRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetInfoCallback>> _pendingInfoRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, UploadResultCallback>> _pendingUploads;

View file

@ -117,22 +117,22 @@ void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
}
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
if (resource->getBytesTotal() > _unusedResourcesMaxSize) {
// If it doesn't fit anyway, let's leave whatever is already in the cache.
// If it doesn't fit or its size is unknown, leave the cache alone.
if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) {
resource->setCache(nullptr);
return;
}
reserveUnusedResource(resource->getBytesTotal());
reserveUnusedResource(resource->getBytes());
resource->setLRUKey(++_lastLRUKey);
_unusedResources.insert(resource->getLRUKey(), resource);
_unusedResourcesSize += resource->getBytesTotal();
_unusedResourcesSize += resource->getBytes();
}
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
if (_unusedResources.contains(resource->getLRUKey())) {
_unusedResources.remove(resource->getLRUKey());
_unusedResourcesSize -= resource->getBytesTotal();
_unusedResourcesSize -= resource->getBytes();
}
}
@ -142,7 +142,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
// unload the oldest resource
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
_unusedResourcesSize -= it.value()->getBytesTotal();
_unusedResourcesSize -= it.value()->getBytes();
it.value()->setCache(nullptr);
_unusedResources.erase(it);
}
@ -399,7 +399,7 @@ void Resource::makeRequest() {
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
_bytesReceived = _bytesTotal = 0;
_bytesReceived = _bytesTotal = _bytes = 0;
_request->send();
}
@ -412,6 +412,8 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
void Resource::handleReplyFinished() {
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
_bytes = _bytesTotal;
if (!_request || _request != sender()) {
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"

View file

@ -181,6 +181,9 @@ public:
/// For loading resources, returns the number of total bytes (<= zero if unknown).
qint64 getBytesTotal() const { return _bytesTotal; }
/// For loaded resources, returns the number of actual bytes (defaults to total bytes if not explicitly set).
qint64 getBytes() const { return _bytes; }
/// For loading resources, returns the load progress.
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
@ -191,7 +194,7 @@ public:
void setCache(ResourceCache* cache) { _cache = cache; }
Q_INVOKABLE void allReferencesCleared();
virtual void deleter() { allReferencesCleared(); }
const QUrl& getURL() const { return _url; }
@ -222,10 +225,15 @@ protected:
/// This should be overridden by subclasses that need to process the data once it is downloaded.
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
/// Called when the download is finished and processed, sets the number of actual bytes.
void setBytes(qint64 bytes) { _bytes = bytes; }
/// Called when the download is finished and processed.
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
Q_INVOKABLE void finishedLoading(bool success);
Q_INVOKABLE void allReferencesCleared();
QUrl _url;
QUrl _activeUrl;
bool _startedLoading = false;
@ -253,6 +261,7 @@ private:
QTimer* _replyTimer = nullptr;
qint64 _bytesReceived = 0;
qint64 _bytesTotal = 0;
qint64 _bytes = 0;
int _attempts = 0;
};

View file

@ -74,6 +74,9 @@ public:
/// whether the HMD is being worn
virtual bool isDisplayVisible() const { return false; }
virtual QString getPreferredAudioInDevice() const { return QString(); }
virtual QString getPreferredAudioOutDevice() const { return QString(); }
// Rendering support
/**
@ -122,7 +125,7 @@ public:
}
// will query the underlying hmd api to compute the most recent head pose
virtual void updateHeadPose(uint32_t frameIndex) {}
virtual void beginFrameRender(uint32_t frameIndex) {}
// returns a copy of the most recent head pose, computed via updateHeadPose
virtual glm::mat4 getHeadPose() const {
@ -142,6 +145,8 @@ public:
virtual float presentRate() { return -1.0f; }
uint32_t presentCount() const { return _presentedFrameIndex; }
virtual void cycleDebugOutput() {}
static const QString& MENU_PATH();
signals:

View file

@ -36,6 +36,6 @@ NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
}
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::allReferencesCleared);
return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::deleter);
}

View file

@ -46,6 +46,8 @@ public:
virtual render::ScenePointer getMain3DScene() = 0;
virtual render::EnginePointer getRenderEngine() = 0;
virtual void pushPreRenderLambda(void* key, std::function<void()> func) = 0;
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
static AbstractViewStateInterface* instance();
static void setInstance(AbstractViewStateInterface* instance);

View file

@ -435,8 +435,9 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo
float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
// TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,
// we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working...
const float OVER_CONSERVATIVE_SCALE = 1.1f;
if ((eyeHalfPlaneDistance > -nearRadius) &&
(glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) {
(glm::distance(eyePoint, glm::vec3(light->getPosition())) < (expandedRadius * OVER_CONSERVATIVE_SCALE) + nearRadius)) {
coneParam.w = 0.0f;
batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam));
@ -452,6 +453,7 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo
batch.setProjectionTransform( projMats[side]);
batch.setViewTransform(viewTransforms[side]);
} else {
light->setShowContour(false);
coneParam.w = 1.0f;
batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam));

View file

@ -97,7 +97,7 @@ void FramebufferCache::createPrimaryFramebuffer() {
// FIXME: Decide on the proper one, let s stick to R11G11B10 for now
//_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, defaultSampler));
_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::R11G11B10), width, height, defaultSampler));
_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler));
//_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::HALF, gpu::RGBA), width, height, defaultSampler));
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);

View file

@ -58,7 +58,6 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr<const model::Mesh>& d
auto vertexFormat = _drawMesh->getVertexFormat();
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
_localBound = _drawMesh->evalPartBound(partIndex);
}
}
@ -352,7 +351,23 @@ void ModelMeshPartPayload::initCache() {
void ModelMeshPartPayload::notifyLocationChanged() {
_model->_needsUpdateClusterMatrices = true;
}
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices) {
ModelMeshPartPayload::updateTransform(transform, offsetTransform);
if (clusterMatrices.size() > 0) {
_worldBound = AABox();
for (auto& clusterMatrix : clusterMatrices) {
AABox clusterBound = _localBound;
clusterBound.transform(clusterMatrix);
_worldBound += clusterBound;
}
// clusterMatrix has world rotation but not world translation.
_worldBound.translate(transform.getTranslation());
}
}
ItemKey ModelMeshPartPayload::getKey() const {
@ -377,12 +392,6 @@ ItemKey ModelMeshPartPayload::getKey() const {
return builder.build();
}
Item::Bound ModelMeshPartPayload::getBound() const {
// NOTE: we can't cache this bounds because we need to handle the case of a moving
// entity or mesh part.
return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation());
}
ShapeKey ModelMeshPartPayload::getShapeKey() const {
assert(_model->isLoaded());
const FBXGeometry& geometry = _model->getFBXGeometry();

View file

@ -74,15 +74,15 @@ namespace render {
class ModelMeshPartPayload : public MeshPartPayload {
public:
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
typedef render::Payload<ModelMeshPartPayload> Payload;
typedef Payload::DataPointer Pointer;
void notifyLocationChanged() override;
void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
// Render Item interface
render::ItemKey getKey() const override;
render::Item::Bound getBound() const override;
render::ShapeKey getShapeKey() const override; // shape interface
void render(RenderArgs* args) const override;
@ -101,4 +101,11 @@ public:
bool _isBlendShaped{ false };
};
namespace render {
template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload);
template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload);
template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload);
template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args);
}
#endif // hifi_MeshPartPayload_h

View file

@ -76,14 +76,9 @@ AbstractViewStateInterface* Model::_viewState = NULL;
bool Model::needsFixupInScene() const {
if (readyToAddToScene()) {
// Once textures are loaded, fixup if they are now transparent
if (_needsUpdateTransparentTextures && _geometry->getGeometry()->areTexturesLoaded()) {
_needsUpdateTransparentTextures = false;
bool hasTransparentTextures = _geometry->getGeometry()->hasTransparentTextures();
if (_hasTransparentTextures != hasTransparentTextures) {
_hasTransparentTextures = hasTransparentTextures;
return true;
}
if (_needsUpdateTextures && _geometry->getGeometry()->areTexturesLoaded()) {
_needsUpdateTextures = false;
return true;
}
if (!_readyWhenAdded) {
return true;
@ -130,25 +125,64 @@ void Model::setOffset(const glm::vec3& offset) {
}
void Model::enqueueLocationChange() {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
Transform transform;
transform.setTranslation(_translation);
transform.setRotation(_rotation);
_needsUpdateClusterMatrices = true;
Transform offset;
offset.setScale(_scale);
offset.postTranslate(_offset);
// queue up this work for later processing, at the end of update and just before rendering.
// the application will ensure only the last lambda is actually invoked.
void* key = (void*)this;
std::weak_ptr<Model> weakSelf = shared_from_this();
AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() {
render::PendingChanges pendingChanges;
foreach (auto itemID, _renderItems.keys()) {
pendingChanges.updateItem<MeshPartPayload>(itemID, [transform, offset](MeshPartPayload& data) {
data.updateTransform(transform, offset);
data.notifyLocationChanged();
});
}
// do nothing, if the model has already been destroyed.
auto self = weakSelf.lock();
if (!self) {
return;
}
scene->enqueuePendingChanges(pendingChanges);
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
Transform modelTransform;
modelTransform.setScale(self->_scale);
modelTransform.setTranslation(self->_translation);
modelTransform.setRotation(self->_rotation);
Transform modelMeshOffset;
if (self->isLoaded()) {
// includes model offset and unitScale.
modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform());
} else {
modelMeshOffset.postTranslate(self->_offset);
}
// only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh.
Transform collisionMeshOffset;
collisionMeshOffset.postTranslate(self->_offset);
render::PendingChanges pendingChanges;
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) {
// Ensure the model geometry was not reset between frames
if (data._model->isLoaded()) {
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
// update the model transform and bounding box for this render item.
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices);
}
});
}
foreach (auto itemID, self->_collisionRenderItems.keys()) {
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
// update the model transform for this render item.
data.updateTransform(modelTransform, collisionMeshOffset);
});
}
scene->enqueuePendingChanges(pendingChanges);
});
}
void Model::initJointTransforms() {
@ -497,39 +531,16 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
_isVisible = newValue;
render::PendingChanges pendingChanges;
foreach (auto item, _renderItems.keys()) {
pendingChanges.resetItem(item, _renderItems[item]);
foreach (auto item, _modelMeshRenderItems.keys()) {
pendingChanges.resetItem(item, _modelMeshRenderItems[item]);
}
foreach (auto item, _collisionRenderItems.keys()) {
pendingChanges.resetItem(item, _collisionRenderItems[item]);
}
scene->enqueuePendingChanges(pendingChanges);
}
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, bool showCollisionHull) {
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
_showCollisionHull = showCollisionHull;
segregateMeshGroups();
}
bool somethingAdded = false;
foreach (auto renderItem, _renderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
_renderItems.insert(item, renderPayload);
somethingAdded = true;
}
_readyWhenAdded = readyToAddToScene();
return somethingAdded;
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters,
@ -541,16 +552,48 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
bool somethingAdded = false;
foreach (auto renderItem, _renderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
_renderItems.insert(item, renderPayload);
somethingAdded = true;
if (_modelMeshRenderItems.size()) {
for (auto item : _modelMeshRenderItems.keys()) {
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
}
} else {
for (auto renderItem : _modelMeshRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
if (statusGetters.size()) {
renderPayload->addStatusGetters(statusGetters);
}
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
_modelMeshRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
}
if (_collisionRenderItems.size()) {
for (auto item : _collisionRenderItems.keys()) {
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
}
} else {
for (auto renderItem : _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
if (statusGetters.size()) {
renderPayload->addStatusGetters(statusGetters);
}
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
data.notifyLocationChanged();
});
_collisionRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
}
_readyWhenAdded = readyToAddToScene();
@ -559,11 +602,16 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
}
void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
foreach (auto item, _renderItems.keys()) {
foreach (auto item, _modelMeshRenderItems.keys()) {
pendingChanges.removeItem(item);
}
_renderItems.clear();
_renderItemsSet.clear();
_modelMeshRenderItems.clear();
_modelMeshRenderItemsSet.clear();
foreach (auto item, _collisionRenderItems.keys()) {
pendingChanges.removeItem(item);
}
_collisionRenderItems.clear();
_collisionRenderItemsSet.clear();
_meshGroupsKnown = false;
_readyWhenAdded = false;
}
@ -721,6 +769,13 @@ int Model::getLastFreeJointIndex(int jointIndex) const {
return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1;
}
void Model::setTextures(const QVariantMap& textures) {
if (isLoaded()) {
_needsUpdateTextures = true;
_geometry->getGeometry()->setTextures(textures);
}
}
void Model::setURL(const QUrl& url) {
// don't recreate the geometry if it's the same URL
if (_url == url && _geometry && _geometry->getURL() == url) {
@ -737,8 +792,7 @@ void Model::setURL(const QUrl& url) {
}
_needsReload = true;
_needsUpdateTransparentTextures = true;
_hasTransparentTextures = false;
_needsUpdateTextures = true;
_meshGroupsKnown = false;
invalidCalculatedMeshBoxes();
deleteGeometry();
@ -993,7 +1047,7 @@ void Model::simulateInternal(float deltaTime) {
void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
PerformanceTimer perfTimer("Model::updateClusterMatrices");
if (!_needsUpdateClusterMatrices) {
if (!_needsUpdateClusterMatrices || !isLoaded()) {
return;
}
_needsUpdateClusterMatrices = false;
@ -1175,10 +1229,14 @@ void Model::segregateMeshGroups() {
return;
}
Q_ASSERT(_renderItems.isEmpty()); // We should not have any existing renderItems if we enter this section of code
Q_ASSERT(_renderItemsSet.isEmpty()); // We should not have any existing renderItemsSet if we enter this section of code
// We should not have any existing renderItems if we enter this section of code
Q_ASSERT(_modelMeshRenderItems.isEmpty());
Q_ASSERT(_modelMeshRenderItemsSet.isEmpty());
Q_ASSERT(_collisionRenderItems.isEmpty());
Q_ASSERT(_collisionRenderItemsSet.isEmpty());
_renderItemsSet.clear();
_modelMeshRenderItemsSet.clear();
_collisionRenderItemsSet.clear();
Transform transform;
transform.setTranslation(_translation);
@ -1204,9 +1262,9 @@ void Model::segregateMeshGroups() {
_collisionHullMaterial->setMetallic(0.02f);
_collisionHullMaterial->setRoughness(0.5f);
}
_renderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterial, transform, offset);
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterial, transform, offset);
} else {
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
}
shapeID++;
@ -1229,13 +1287,21 @@ bool Model::initWhenReady(render::ScenePointer scene) {
offset.setScale(_scale);
offset.postTranslate(_offset);
foreach (auto renderItem, _renderItemsSet) {
foreach (auto renderItem, _modelMeshRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
_modelMeshRenderItems.insert(item, renderPayload);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
data.notifyLocationChanged();
});
}
foreach (auto renderItem, _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
_renderItems.insert(item, renderPayload);
_collisionRenderItems.insert(item, renderPayload);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [transform,offset](MeshPartPayload& data) {
data.updateTransform(transform, offset);
pendingChanges.updateItem<MeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
data.notifyLocationChanged();
});
}

View file

@ -44,6 +44,7 @@ namespace render {
typedef unsigned int ItemID;
}
class MeshPartPayload;
class ModelMeshPartPayload;
class ModelRenderLocations;
inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
@ -86,7 +87,10 @@ public:
bool initWhenReady(render::ScenePointer scene);
bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
bool showCollisionHull = false);
bool showCollisionHull = false) {
auto getters = render::Item::Status::Getters(0);
return addToScene(scene, pendingChanges, getters, showCollisionHull);
}
bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters,
@ -128,6 +132,9 @@ public:
/// Returns a reference to the shared collision geometry.
const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }
const QVariantMap getTextures() const { assert(isLoaded()); return _geometry->getGeometry()->getTextures(); }
void setTextures(const QVariantMap& textures);
/// Provided as a convenience, will crash if !isLoaded()
// And so that getGeometry() isn't chained everywhere
const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); }
@ -375,14 +382,17 @@ protected:
bool _renderCollisionHull;
QSet<std::shared_ptr<MeshPartPayload>> _renderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _renderItems;
QSet<std::shared_ptr<MeshPartPayload>> _collisionRenderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _collisionRenderItems;
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;
bool _readyWhenAdded { false };
bool _needsReload { true };
bool _needsUpdateClusterMatrices { true };
mutable bool _needsUpdateTransparentTextures { true };
mutable bool _hasTransparentTextures { false };
bool _showCollisionHull { false };
mutable bool _needsUpdateTextures { true };
friend class ModelMeshPartPayload;
RigPointer _rig;

View file

@ -32,9 +32,10 @@ public:
class RenderDeferred {
public:
using JobModel = render::Job::Model<RenderDeferred>;
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
using JobModel = render::Job::Model<RenderDeferred>;
};
class DrawConfig : public render::Job::Config {

View file

@ -25,6 +25,7 @@ in vec4 _texCoord0;
out vec4 _fragColor;
void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
// Grab the fragment data from the uv

View file

@ -20,17 +20,14 @@ namespace render {
class DrawSceneOctreeConfig : public Job::Config {
Q_OBJECT
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty())
Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells)
Q_PROPERTY(bool showEmptyCells MEMBER showEmptyCells WRITE setShowEmptyCells)
Q_PROPERTY(bool showVisibleCells READ getShowVisibleCells WRITE setShowVisibleCells NOTIFY dirty())
Q_PROPERTY(bool showEmptyCells READ getShowEmptyCells WRITE setShowEmptyCells NOTIFY dirty())
Q_PROPERTY(int numAllocatedCells READ getNumAllocatedCells)
Q_PROPERTY(int numFreeCells READ getNumFreeCells)
public:
DrawSceneOctreeConfig() : Job::Config(false) {}
bool showVisibleCells{ true };
bool showEmptyCells{ false };
int numAllocatedCells{ 0 };
int numFreeCells{ 0 };
@ -38,6 +35,12 @@ namespace render {
int getNumAllocatedCells() const { return numAllocatedCells; }
int getNumFreeCells() const { return numFreeCells; }
bool showVisibleCells{ true };
bool showEmptyCells{ false };
bool getShowVisibleCells() { return showVisibleCells; }
bool getShowEmptyCells() { return showEmptyCells; }
public slots:
void setShowVisibleCells(bool show) { showVisibleCells = show; emit dirty(); }
void setShowEmptyCells(bool show) { showEmptyCells = show; emit dirty(); }
@ -79,10 +82,10 @@ namespace render {
class DrawItemSelectionConfig : public Job::Config {
Q_OBJECT
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty())
Q_PROPERTY(bool showInsideItems MEMBER showInsideItems WRITE setShowInsideItems)
Q_PROPERTY(bool showInsideSubcellItems MEMBER showInsideSubcellItems WRITE setShowInsideSubcellItems)
Q_PROPERTY(bool showPartialItems MEMBER showPartialItems WRITE setShowPartialItems)
Q_PROPERTY(bool showPartialSubcellItems MEMBER showPartialSubcellItems WRITE setShowPartialSubcellItems)
Q_PROPERTY(bool showInsideItems READ getShowInsideItems WRITE setShowInsideItems NOTIFY dirty())
Q_PROPERTY(bool showInsideSubcellItems READ getShowInsideSubcellItems WRITE setShowInsideSubcellItems NOTIFY dirty())
Q_PROPERTY(bool showPartialItems READ getShowPartialItems WRITE setShowPartialItems NOTIFY dirty())
Q_PROPERTY(bool showPartialSubcellItems READ getShowPartialSubcellItems WRITE setShowPartialSubcellItems NOTIFY dirty())
public:
DrawItemSelectionConfig() : Job::Config(false) {}
@ -92,7 +95,12 @@ namespace render {
bool showPartialItems{ true };
bool showPartialSubcellItems{ true };
public slots:
bool getShowInsideItems() const { return showInsideItems; };
bool getShowInsideSubcellItems() const { return showInsideSubcellItems; };
bool getShowPartialItems() const { return showPartialItems; };
bool getShowPartialSubcellItems() const { return showPartialSubcellItems; };
public slots:
void setShowInsideItems(bool show) { showInsideItems = show; emit dirty(); }
void setShowInsideSubcellItems(bool show) { showInsideSubcellItems = show; emit dirty(); }
void setShowPartialItems(bool show) { showPartialItems = show; emit dirty(); }

View file

@ -20,12 +20,16 @@
using namespace render;
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
for (const auto& itemDetails : inItems) {
auto& item = scene->getItem(itemDetails.id);
int numItemsToDraw = (int)inItems.size();
if (maxDrawnItems != -1) {
numItemsToDraw = glm::min(numItemsToDraw, maxDrawnItems);
}
for (auto i = 0; i < numItemsToDraw; ++i) {
auto& item = scene->getItem(inItems[i].id);
item.render(args);
}
}
@ -69,7 +73,10 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
// render lights
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
renderItems(sceneContext, renderContext, inLights);
renderItems(sceneContext, renderContext, inLights, _maxDrawn);
args->_batch = nullptr;
});
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setNumDrawn((int)inLights.size());
}

View file

@ -16,15 +16,37 @@
namespace render {
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems);
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems = -1);
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
class DrawLightConfig : public Job::Config {
Q_OBJECT
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
public:
int getNumDrawn() { return numDrawn; }
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
int maxDrawn{ -1 };
signals:
void numDrawnChanged();
void dirty();
protected:
int numDrawn{ 0 };
};
class DrawLight {
public:
using JobModel = Job::ModelI<DrawLight, ItemBounds>;
using Config = DrawLightConfig;
using JobModel = Job::ModelI<DrawLight, ItemBounds, Config>;
void configure(const Config& config) { _maxDrawn = config.maxDrawn; }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights);
protected:
int _maxDrawn; // initialized by Config
};
}

Some files were not shown because too many files have changed in this diff Show more