From 33b48947a5930195a15927a93f293867917f8eab Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 23 Jun 2015 17:36:19 -0700 Subject: [PATCH 01/44] Delete Interface.ini.lock file at start-up if it exists Otherwise Interface freezes. --- libraries/shared/src/SettingInterface.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index c14fd33565..b60ffc0891 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include "PathUtils.h" @@ -53,7 +54,15 @@ namespace Setting { privateInstance = new Manager(); Q_CHECK_PTR(privateInstance); - + + // Delete Interface.ini.lock file if it exists, otherwise Interface freezes. + QString settingsLockFilename = privateInstance->fileName() + ".lock"; + QFile settingsLockFile(settingsLockFilename); + if (settingsLockFile.exists()) { + bool deleted = settingsLockFile.remove(); + qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename; + } + QObject::connect(privateInstance, SIGNAL(destroyed()), thread, SLOT(quit())); QObject::connect(thread, SIGNAL(started()), privateInstance, SLOT(startTimer())); QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); From 377979e38019d20a40d55ac7461a1ff14256c8a2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 25 Jun 2015 15:41:10 -0700 Subject: [PATCH 02/44] Make field of view preference change be applied immediately --- interface/src/Application.cpp | 5 +++-- interface/src/Application.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f828f05258..8c8e624dcb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1029,7 +1029,7 @@ void Application::showEditEntitiesHelp() { InfoView::show(INFO_EDIT_ENTITIES_PATH); } -void Application::resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size) { +void Application::resetCameras(Camera& camera, const glm::uvec2& size) { if (OculusManager::isConnected()) { OculusManager::configureCamera(camera); } else if (TV3DManager::isConnected()) { @@ -1052,7 +1052,6 @@ void Application::resizeGL() { if (_renderResolution != toGlm(renderSize)) { _renderResolution = toGlm(renderSize); DependencyManager::get()->setFrameBufferSize(renderSize); - resetCamerasOnResizeGL(_myCamera, _renderResolution); glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? @@ -1060,6 +1059,8 @@ void Application::resizeGL() { glLoadIdentity(); } + resetCameras(_myCamera, _renderResolution); + auto offscreenUi = DependencyManager::get(); auto canvasSize = _glWidget->size(); diff --git a/interface/src/Application.h b/interface/src/Application.h index b126757621..61c029e8cf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -479,7 +479,7 @@ private slots: void setCursorVisible(bool visible); private: - void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size); + void resetCameras(Camera& camera, const glm::uvec2& size); void updateProjectionMatrix(); void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); From f47cff1332da5365a95d2f94393e8dcde21a95f2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 25 Jun 2015 15:41:27 -0700 Subject: [PATCH 03/44] Remove duplicate method call --- interface/src/ui/PreferencesDialog.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index eca250a428..93b3ef8d07 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -221,8 +221,6 @@ void PreferencesDialog::savePreferences() { myAvatar->setLeanScale(ui.leanScaleSpin->value()); myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value()); - Application::getInstance()->resizeGL(); - DependencyManager::get()->getMyAvatar()->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); qApp->setFieldOfView(ui.fieldOfViewSpin->value()); From f753a544941e4bac5f2ebb87bb32ab2ccbce44f6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 13:56:14 -0700 Subject: [PATCH 04/44] Fix web entities incorrect dimensions --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 94f88b8390..e8199fc577 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -174,7 +174,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { Q_ASSERT(getType() == EntityTypes::Web); static const glm::vec2 texMin(0.0f); static const glm::vec2 texMax(1.0f); - glm::vec2 topLeft(-0.5f -0.5f); + glm::vec2 topLeft(-0.5f, -0.5f); glm::vec2 bottomRight(0.5f, 0.5f); Q_ASSERT(args->_batch); From e6cdd4a9ff730a970d826a4f9043496c9ccec719 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 13:56:39 -0700 Subject: [PATCH 05/44] Fix web entities back culled --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index e8199fc577..69ef5682a3 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -185,7 +185,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - DependencyManager::get()->bindSimpleProgram(batch, true); + DependencyManager::get()->bindSimpleProgram(batch, true, false); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f)); DependencyManager::get()->releaseSimpleProgram(batch); } From 3c49e6231e8fa7f1ea671dc7cf07ea686562cf4c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 13:56:59 -0700 Subject: [PATCH 06/44] Sam's fix for pipeline edge case Basically if we bind a uniform texture and the pipeline is not setup correctly on mac --- libraries/gpu/src/gpu/GLBackendPipeline.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index f4449e9ea1..06d9eadd87 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -160,6 +160,10 @@ void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) { GLuint bo = getBufferID(*uniformBuffer); glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize); #else + // because we rely on the program uniform mechanism we need to have + // the program bound, thank you MacOSX Legacy profile. + updatePipeline(); + GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart); glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data); From 078a5a8439d6daccfada32a03f2df5b2fd05c65d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 14:01:21 -0700 Subject: [PATCH 07/44] Formatting --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 69ef5682a3..458d63288f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -172,10 +172,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { Glower glow(0.0f); PerformanceTimer perfTimer("RenderableWebEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Web); - static const glm::vec2 texMin(0.0f); - static const glm::vec2 texMax(1.0f); - glm::vec2 topLeft(-0.5f, -0.5f); - glm::vec2 bottomRight(0.5f, 0.5f); + static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f); Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; From 017b4045e5313c6162d90a2ede88f8a05c5d385f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 14:30:58 -0700 Subject: [PATCH 08/44] Fix web entity texture not displayed --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 458d63288f..89b0791c73 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -178,6 +178,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); if (_texture) { + batch._glActiveTexture(GL_TEXTURE0); batch._glBindTexture(GL_TEXTURE_2D, _texture); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); From efd805bea74243fbe12b4aaac2f4bba1ec0edbdc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 15:55:30 -0700 Subject: [PATCH 09/44] Introduce new emissive simple program Also fixes the shading on web entities using that program --- .../src/RenderableWebEntityItem.cpp | 6 +-- .../src/DeferredLightingEffect.cpp | 38 ++++++++----------- .../render-utils/src/DeferredLightingEffect.h | 9 ++--- .../src/simple_textured_emisive.slf | 33 ++++++++++++++++ 4 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 libraries/render-utils/src/simple_textured_emisive.slf diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 89b0791c73..88cd199976 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -180,12 +180,10 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (_texture) { batch._glActiveTexture(GL_TEXTURE0); batch._glBindTexture(GL_TEXTURE_2D, _texture); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - DependencyManager::get()->bindSimpleProgram(batch, true, false); + static const bool textured = true, culled = false, emmissive = true; + DependencyManager::get()->bindSimpleProgram(batch, textured, culled, emmissive); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f)); - DependencyManager::get()->releaseSimpleProgram(batch); } void RenderableWebEntityItem::setSourceUrl(const QString& value) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index b49d1985bb..a44f1f053c 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -27,8 +27,8 @@ #include "gpu/GLBackend.h" #include "simple_vert.h" -#include "simple_frag.h" #include "simple_textured_frag.h" +#include "simple_textured_emisive_frag.h" #include "deferred_light_vert.h" #include "deferred_light_limited_vert.h" @@ -52,15 +52,15 @@ static const std::string glowIntensityShaderHandle = "glowIntensity"; void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert))); - auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_frag))); - auto PSTextured = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag))); + auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag))); gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); - gpu::ShaderPointer programTextured = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSTextured)); + gpu::ShaderPointer programEmissive = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive)); gpu::Shader::BindingSet slotBindings; gpu::Shader::makeProgram(*program, slotBindings); - gpu::Shader::makeProgram(*programTextured, slotBindings); + gpu::Shader::makeProgram(*programEmissive, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setCullMode(gpu::State::CULL_BACK); @@ -79,8 +79,8 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { _simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); _simpleProgramCullNone = gpu::PipelinePointer(gpu::Pipeline::create(program, stateCullNone)); - _simpleProgramTextured = gpu::PipelinePointer(gpu::Pipeline::create(programTextured, state)); - _simpleProgramTexturedCullNone = gpu::PipelinePointer(gpu::Pipeline::create(programTextured, stateCullNone)); + _simpleProgramEmissive = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, state)); + _simpleProgramEmissiveCullNone = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, stateCullNone)); _viewState = viewState; loadLightProgram(directional_light_frag, false, _directionalLight, _directionalLightLocations); @@ -117,14 +117,12 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET)); } -void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled) { - // DependencyManager::get()->setPrimaryDrawBuffers(batch, true, true, true); - - if (textured) { +void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool emmisive) { + if (emmisive) { if (culled) { - batch.setPipeline(_simpleProgramTextured); + batch.setPipeline(_simpleProgramEmissive); } else { - batch.setPipeline(_simpleProgramTexturedCullNone); + batch.setPipeline(_simpleProgramEmissiveCullNone); } } else { if (culled) { @@ -133,48 +131,42 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, batch.setPipeline(_simpleProgramCullNone); } } -} - -void DeferredLightingEffect::releaseSimpleProgram(gpu::Batch& batch) { - // DependencyManager::get()->setPrimaryDrawBuffers(batch, true, false, false); + if (!textured) { + // If it is not textured, bind white texture and keep using textured pipeline + batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture()); + } } void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { bindSimpleProgram(batch); DependencyManager::get()->renderSphere(batch, radius, slices, stacks, color); - releaseSimpleProgram(batch); } void DeferredLightingEffect::renderWireSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { bindSimpleProgram(batch); DependencyManager::get()->renderSphere(batch, radius, slices, stacks, color, false); - releaseSimpleProgram(batch); } void DeferredLightingEffect::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) { bindSimpleProgram(batch); DependencyManager::get()->renderSolidCube(batch, size, color); - releaseSimpleProgram(batch); } void DeferredLightingEffect::renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color) { bindSimpleProgram(batch); DependencyManager::get()->renderWireCube(batch, size, color); - releaseSimpleProgram(batch); } void DeferredLightingEffect::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color) { bindSimpleProgram(batch); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, color); - releaseSimpleProgram(batch); } void DeferredLightingEffect::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2) { bindSimpleProgram(batch); DependencyManager::get()->renderLine(batch, p1, p2, color1, color2); - releaseSimpleProgram(batch); } void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color, diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 9d66bf08c0..d948f2c305 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -34,10 +34,7 @@ public: void init(AbstractViewStateInterface* viewState); /// Sets up the state necessary to render static untextured geometry with the simple program. - void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true); - - /// Tears down the state necessary to render static untextured geometry with the simple program. - void releaseSimpleProgram(gpu::Batch& batch); + void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, bool emmisive = false); //// Renders a solid sphere with the simple program. void renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color); @@ -101,8 +98,8 @@ private: gpu::PipelinePointer _simpleProgram; gpu::PipelinePointer _simpleProgramCullNone; - gpu::PipelinePointer _simpleProgramTextured; - gpu::PipelinePointer _simpleProgramTexturedCullNone; + gpu::PipelinePointer _simpleProgramEmissive; + gpu::PipelinePointer _simpleProgramEmissiveCullNone; ProgramObject _directionalSkyboxLight; LightLocations _directionalSkyboxLightLocations; diff --git a/libraries/render-utils/src/simple_textured_emisive.slf b/libraries/render-utils/src/simple_textured_emisive.slf new file mode 100644 index 0000000000..643dcde190 --- /dev/null +++ b/libraries/render-utils/src/simple_textured_emisive.slf @@ -0,0 +1,33 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// simple.frag +// fragment shader +// +// Created by ClĂ©ment Brisset on 5/29/15. +// Copyright 2014 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 DeferredBufferWrite.slh@> + +// the diffuse texture +uniform sampler2D originalTexture; + +// the interpolated normal +varying vec4 interpolatedNormal; + +void main(void) { + vec4 texel = texture2D(originalTexture, gl_TexCoord[0].st); + + packDeferredFragmentLightmap( + normalize(interpolatedNormal.xyz), + glowIntensity * texel.a, + gl_Color.rgb, + gl_FrontMaterial.specular.rgb, + gl_FrontMaterial.shininess, + texel.rgb); +} \ No newline at end of file From 27a1a55275ce7195b34e3531ac1e0ec075d5088b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Jun 2015 16:16:27 -0700 Subject: [PATCH 10/44] Don't cull text background/Text is emissive --- .../src/RenderableTextEntityItem.cpp | 3 ++- libraries/render-utils/src/sdf_text3D.slf | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 245cf00a3d..7603187e94 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -56,7 +56,8 @@ void RenderableTextEntityItem::render(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); } - DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); + DependencyManager::get()->bindSimpleProgram(batch, false, false); + DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); float scale = _lineHeight / _textRenderer->getFontSize(); transformToTopLeft.setScale(scale); // Scale to have the correct line height diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index 361f8454ab..e22eba8ff5 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -10,6 +10,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +<@include DeferredBufferWrite.slh@> + uniform sampler2D Font; uniform bool Outline; uniform vec4 Color; @@ -44,9 +46,13 @@ void main() { if (a < 0.01) { discard; } - + // final color - gl_FragData[0] = vec4(Color.rgb, Color.a * a); - gl_FragData[1] = vec4(interpolatedNormal.xyz, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); - gl_FragData[2] = vec4(0.0); + packDeferredFragmentLightmap( + normalize(interpolatedNormal.xyz), + glowIntensity * texel.a, + gl_Color.rgb, + gl_FrontMaterial.specular.rgb, + gl_FrontMaterial.shininess, + Color.rgb); } \ No newline at end of file From 98f165f2ae68d3f9a9c0c4e530325790ada62678 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 26 Jun 2015 16:23:16 -0700 Subject: [PATCH 11/44] Avatar collision sounds. collisionsSoundURL can be set in preferences: Currently defaults to https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav. Can be empty, which means no sound, rather than restoring default. MyAvatar.collisionSoundURL can read/written in scripts. Preloads when set, so that it won't have to fetch on first collision. Plays at start of collision only, with volume proportional to "velocity change"^2. --- interface/src/Application.cpp | 1 + interface/src/avatar/AvatarManager.cpp | 25 +++++++++++ interface/src/avatar/MyAvatar.cpp | 11 +++++ interface/src/avatar/MyAvatar.h | 6 +++ interface/src/ui/PreferencesDialog.cpp | 4 ++ interface/ui/preferencesDialog.ui | 60 ++++++++++++++++++++++++++ 6 files changed, 107 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d9c5589631..0224639cbf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2202,6 +2202,7 @@ void Application::init() { // Make sure any new sounds are loaded as soon as know about them. connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get().data(), &SoundCache::getSound); + connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get().data(), &SoundCache::getSound); } void Application::closeMirrorView() { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 88f550d68c..d39a8522af 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -257,6 +257,31 @@ void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) { void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) { // TODO: expose avatar collision events to JS + for (Collision collision : collisionEvents) { + if (collision.idA.isNull() || collision.idB.isNull()) { + MyAvatar* myAvatar = getMyAvatar(); + const QString& collisionSoundURL = myAvatar->getCollisionSoundURL(); + if (!collisionSoundURL.isEmpty()) { + const float velocityChange = glm::length(collision.velocityChange); + const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01; + const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); + + if (!isSound) { + break; + } + // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. + const float energy = velocityChange * velocityChange; + const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f; + const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); + + // For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object, + // but most avatars are roughly the same size, so let's not be so fancy yet. + const float AVATAR_STRETCH_FACTOR = 1.0f; + + AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition()); + } + } + } } void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4b140e0569..b0e31361c2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -70,6 +70,7 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; const int SCRIPTED_MOTOR_CAMERA_FRAME = 0; const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; +const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav"; const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 10.0f; @@ -90,6 +91,7 @@ MyAvatar::MyAvatar() : _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), + _collisionSoundURL(""), _characterController(this), _lookAtTargetAvatar(), _shouldRender(true), @@ -664,6 +666,7 @@ void MyAvatar::saveData() { settings.endArray(); settings.setValue("displayName", _displayName); + settings.setValue("collisionSoundURL", _collisionSoundURL); settings.endGroup(); } @@ -789,6 +792,7 @@ void MyAvatar::loadData() { settings.endArray(); setDisplayName(settings.value("displayName").toString()); + setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); settings.endGroup(); } @@ -1183,6 +1187,13 @@ void MyAvatar::clearScriptableSettings() { _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE; } +void MyAvatar::setCollisionSoundURL(const QString& url) { + if (!url.isEmpty() && (url != _collisionSoundURL)) { + emit newCollisionSoundURL(QUrl(url)); + } + _collisionSoundURL = url; +} + void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) { if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2fea09ee27..14633e529b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -25,6 +25,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame) + Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) public: @@ -150,6 +151,9 @@ public: void setScriptedMotorTimescale(float timescale); void setScriptedMotorFrame(QString frame); + const QString& getCollisionSoundURL() {return _collisionSoundURL; } + void setCollisionSoundURL(const QString& url); + void clearScriptableSettings(); virtual void attach(const QString& modelURL, const QString& jointName = QString(), @@ -204,6 +208,7 @@ public slots: signals: void transformChanged(); + void newCollisionSoundURL(const QUrl& url); private: @@ -233,6 +238,7 @@ private: float _scriptedMotorTimescale; // timescale for avatar to achieve its target velocity int _scriptedMotorFrame; quint32 _motionBehaviors; + QString _collisionSoundURL; DynamicCharacterController _characterController; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index eca250a428..b230fdfcd7 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -127,6 +127,8 @@ void PreferencesDialog::loadPreferences() { _displayNameString = myAvatar->getDisplayName(); ui.displayNameEdit->setText(_displayNameString); + ui.collisionSoundURLEdit->setText(myAvatar->getCollisionSoundURL()); + ui.sendDataCheckBox->setChecked(!menuInstance->isOptionChecked(MenuOption::DisableActivityLogger)); ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get()); @@ -204,6 +206,8 @@ void PreferencesDialog::savePreferences() { myAvatar->sendIdentityPacket(); } + myAvatar->setCollisionSoundURL(ui.collisionSoundURLEdit->text()); + if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger) != ui.sendDataCheckBox->isChecked()) { Menu::getInstance()->triggerOption(MenuOption::DisableActivityLogger); diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index e74b89075e..78f9f5bf09 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -189,6 +189,66 @@ + + + + + 0 + + + 7 + + + 7 + + + + + + Arial + + + + <html><head/><body><p>Avatar collision sound URL <span style=" color:#909090;">(optional)</span></p></body></html> + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + collisionSoundURLEdit + + + + + + + 4 + + + 5 + + + 4 + + + + + + Arial + + + + Qt::LeftToRight + + + Enter the URL of a sound to play when you bump into something + + + + + + + From fe8d539cf54db84b9e86a143c6761146be15230c Mon Sep 17 00:00:00 2001 From: Niraj Venkat Date: Fri, 26 Jun 2015 16:27:04 -0700 Subject: [PATCH 12/44] changing link icon --- interface/resources/images/link.png | Bin 0 -> 44403 bytes libraries/ui/src/CursorManager.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 interface/resources/images/link.png diff --git a/interface/resources/images/link.png b/interface/resources/images/link.png new file mode 100644 index 0000000000000000000000000000000000000000..67ff4c7b6793a8f2f5107d9710bc553f7ff6d10a GIT binary patch literal 44403 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ó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 Date: Fri, 26 Jun 2015 16:55:48 -0700 Subject: [PATCH 13/44] Fix compile error in text shader --- .../src/RenderableWebEntityItem.cpp | 6 ++++-- libraries/render-utils/src/sdf_text3D.slf | 12 +++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 88cd199976..cf602971c2 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -177,12 +177,14 @@ void RenderableWebEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); + bool textured = false, culled = false, emissive = false; if (_texture) { batch._glActiveTexture(GL_TEXTURE0); batch._glBindTexture(GL_TEXTURE_2D, _texture); + textured = emissive = true; } - static const bool textured = true, culled = false, emmissive = true; - DependencyManager::get()->bindSimpleProgram(batch, textured, culled, emmissive); + + DependencyManager::get()->bindSimpleProgram(batch, textured, culled, emissive); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f)); } diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index e22eba8ff5..d9972417ba 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -10,8 +10,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -<@include DeferredBufferWrite.slh@> - uniform sampler2D Font; uniform bool Outline; uniform vec4 Color; @@ -48,11 +46,7 @@ void main() { } // final color - packDeferredFragmentLightmap( - normalize(interpolatedNormal.xyz), - glowIntensity * texel.a, - gl_Color.rgb, - gl_FrontMaterial.specular.rgb, - gl_FrontMaterial.shininess, - Color.rgb); + gl_FragData[0] = vec4(Color.rgb, Color.a * a); + gl_FragData[1] = vec4(normalize(interpolatedNormal.xyz), 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5); + gl_FragData[2] = vec4(Color.rgb, gl_FrontMaterial.shininess / 128.0); } \ No newline at end of file From bb2c640a6dec2d0b207ac7f24641fd168741a716 Mon Sep 17 00:00:00 2001 From: DaveDubUK Date: Sat, 27 Jun 2015 09:23:18 +0700 Subject: [PATCH 14/44] walk.js disable audio by default --- examples/libraries/walkApi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index ab9f5071b1..8b99ad2a98 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -40,7 +40,7 @@ Avatar = function() { // settings this.headFree = true; this.armsFree = this.hydraCheck(); // automatically sets true to enable Hydra support - temporary fix - this.makesFootStepSounds = true; + this.makesFootStepSounds = false; this.blenderPreRotations = false; // temporary fix this.animationSet = undefined; // currently just one animation set this.setAnimationSet = function(animationSet) { From 0bf4c9dda2d13d117566b974c1c39aadd83fdfc2 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Mon, 29 Jun 2015 09:58:35 -0700 Subject: [PATCH 15/44] correct menu shortcut for mouselook --- examples/mouseLook.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mouseLook.js b/examples/mouseLook.js index e095870ce6..db9bd06a20 100644 --- a/examples/mouseLook.js +++ b/examples/mouseLook.js @@ -171,7 +171,7 @@ var mouseLook = (function () { } function setupMenu() { - Menu.addMenuItem({ menuName: "View", menuItemName: "Mouselook Mode", shortcutKey: "META+M", + Menu.addMenuItem({ menuName: "View", menuItemName: "Mouselook Mode", shortcutKey: "SHIFT+M", afterItem: "Mirror", isCheckable: true, isChecked: false }); } From f93ad34282090acb4569eb3a41332f791d66d7e8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 29 Jun 2015 10:57:09 -0700 Subject: [PATCH 16/44] add spacing above and below tooltipPic --- interface/resources/qml/Tooltip.qml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml index ef4006d681..c4d7492a23 100644 --- a/interface/resources/qml/Tooltip.qml +++ b/interface/resources/qml/Tooltip.qml @@ -22,7 +22,7 @@ Hifi.Tooltip { Rectangle { id: border color: "#7f000000" - width: 322 + width: 330 height: col.height + hifi.layout.spacing * 2 Column { @@ -40,6 +40,7 @@ Hifi.Tooltip { color: "white" anchors.left: parent.left anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter font.pixelSize: hifi.fonts.pixelSize * 2 text: root.title wrapMode: Text.WrapAnywhere @@ -61,6 +62,12 @@ Hifi.Tooltip { anchors.right: parent.right } + Item { + id: firstSpacer + width: col.width + height: 5 + } + Image { id: tooltipPic source: root.imageURL @@ -68,7 +75,12 @@ Hifi.Tooltip { width: 320 anchors.left: parent.left anchors.right: parent.right - verticalAlignment: Image.AlignVCenter + } + + Item { + id: secondSpacer + width: col.width + height: 5 } Text { @@ -78,8 +90,8 @@ Hifi.Tooltip { anchors.left: parent.left anchors.right: parent.right text: root.description - font.pixelSize: hifi.fonts.pixelSize - wrapMode: Text.WrapAnywhere + font.pixelSize: 16 + wrapMode: Text.WordWrap } } } From 3dc3d8b4c1aba747d0777f40a4dd99168936a2fd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 29 Jun 2015 11:05:59 -0700 Subject: [PATCH 17/44] make the tooltip background darker --- interface/resources/qml/Tooltip.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml index c4d7492a23..f3cf1f0760 100644 --- a/interface/resources/qml/Tooltip.qml +++ b/interface/resources/qml/Tooltip.qml @@ -21,7 +21,7 @@ Hifi.Tooltip { Rectangle { id: border - color: "#7f000000" + color: "#BF000000" width: 330 height: col.height + hifi.layout.spacing * 2 From 843ab6d5e3480b3332ad985b1dd4414bac448c27 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 29 Jun 2015 11:08:09 -0700 Subject: [PATCH 18/44] line entities can now be properly rotated using rotation property --- libraries/entities-renderer/src/RenderableLineEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 4b94992d59..6eb575814a 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -45,6 +45,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; Transform transform = Transform(); transform.setTranslation(getPosition()); + transform.setRotation(getRotation()); batch.setModelTransform(transform); batch._glLineWidth(getLineWidth()); From a736d0d901fd200968217575a5be065e8af27b7a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 29 Jun 2015 11:33:55 -0700 Subject: [PATCH 19/44] Initial value false for domain-server wantEditLogging (used by assignment clients). --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e02fa22246..ff2f4cf683 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -484,7 +484,7 @@ "type": "checkbox", "label": "Edit Logging", "help": "Logging of all edits to entities", - "default": true, + "default": false, "advanced": true }, { From c9d205de3ec51c6d799766cd98f11b3da16dd173 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Tue, 30 Jun 2015 13:52:34 -0700 Subject: [PATCH 20/44] quadratic zooming response --- interface/src/avatar/MyAvatar.cpp | 5 +++-- interface/src/ui/UserInputMapper.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6904a1f975..0c349a0159 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -71,7 +71,7 @@ const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; const float MyAvatar::ZOOM_MIN = 0.5f; -const float MyAvatar::ZOOM_MAX = 10.0f; +const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; MyAvatar::MyAvatar() : @@ -1333,7 +1333,8 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe } } - _boomLength += _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN]; + float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN]; + _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); return newLocalVelocity; diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index 3afd09da65..d42498c9a9 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -245,8 +245,8 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit - _actionScales[BOOM_IN] = 1.0f; // 1m per unit - _actionScales[BOOM_OUT] = 1.0f; // 1m per unit + _actionScales[BOOM_IN] = 0.5f; // .5m per unit + _actionScales[BOOM_OUT] = 0.5f; // .5m per unit _actionStates[SHIFT] = 1.0f; // on _actionStates[ACTION1] = 1.0f; // default _actionStates[ACTION2] = 1.0f; // default From 215c260c9e1e1264c003ff91ce9547660a576b43 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 30 Jun 2015 17:08:43 -0700 Subject: [PATCH 21/44] Fix TextOverlay font size setting --- interface/src/ui/overlays/TextOverlay.cpp | 7 +++++++ interface/src/ui/overlays/TextOverlay.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index ccad3bd295..174f8e05dc 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -170,3 +170,10 @@ QSizeF TextOverlay::textSize(const QString& text) const { return QSizeF(extents.x, extents.y); } + +void TextOverlay::setFontSize(int fontSize) { + _fontSize = fontSize; + + delete _textRenderer; + _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); +} diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index de2597cf9a..32786c3220 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -48,7 +48,7 @@ public: void setText(const QString& text) { _text = text; } void setLeftMargin(int margin) { _leftMargin = margin; } void setTopMargin(int margin) { _topMargin = margin; } - void setFontSize(int fontSize) { _fontSize = fontSize; } + void setFontSize(int fontSize); virtual void setProperties(const QScriptValue& properties); virtual TextOverlay* createClone() const; From d4d6f8f5d5c5b4b8b44ccd7bec232b6052306f8e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 10:06:04 -0700 Subject: [PATCH 22/44] Eliminate per-packet locking for processing --- .../src/ReceivedPacketProcessor.cpp | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 894a7b8aa9..706266a903 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -38,19 +38,28 @@ bool ReceivedPacketProcessor::process() { _hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait()); _waitingOnPacketsMutex.unlock(); } + preProcess(); - while (_packets.size() > 0) { - lock(); // lock to make sure nothing changes on us - NetworkPacket& packet = _packets.front(); // get the oldest packet - NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us - _packets.erase(_packets.begin()); // remove the oldest packet - if (!temporary.getNode().isNull()) { - _nodePacketCounts[temporary.getNode()->getUUID()]--; - } - unlock(); // let others add to the packets - processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy + QVector currentPackets; + if (!_packets.size()) { + return isStillRunning(); + } + + lock(); + std::swap(currentPackets, _packets); + unlock(); + + foreach(auto& packet, currentPackets) { + processPacket(packet.getNode(), packet.getByteArray()); midProcess(); } + + lock(); + foreach(auto& packet, currentPackets) { + _nodePacketCounts[packet.getNode()->getUUID()]--; + } + unlock(); + postProcess(); return isStillRunning(); // keep running till they terminate us } From cd63b8a49d6417259811520279de49622511120d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 10:10:02 -0700 Subject: [PATCH 23/44] Fix inverted in/out mbps display on stats --- interface/src/ui/Stats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index d6c61a33bd..e588991f0b 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -122,8 +122,8 @@ void Stats::updateStats() { auto bandwidthRecorder = DependencyManager::get(); STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); - STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); - STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); // Second column: ping if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { From 981ff2d5fa61e04a575613989b1f153c6f8f7cf2 Mon Sep 17 00:00:00 2001 From: Jenkins Slave Access Date: Wed, 1 Jul 2015 14:13:37 -0400 Subject: [PATCH 24/44] Enhancing PR installer titles --- interface/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 00f6d4c3b2..017a9d3c5f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -16,6 +16,8 @@ find_package(Qt5LinguistToolsMacros) if (DEFINED ENV{JOB_ID}) set(BUILD_SEQ $ENV{JOB_ID}) +elseif (DEFINED ENV{ghprbPullId}) + set(BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}") else () set(BUILD_SEQ "dev") endif () From e85eb246d26b5d0126b7c802f45945c66e4ed6a4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 1 Jul 2015 13:19:46 -0700 Subject: [PATCH 25/44] Delete old TextRenderer after new one is in place --- interface/src/ui/overlays/TextOverlay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 174f8e05dc..3f033d9266 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -174,6 +174,7 @@ QSizeF TextOverlay::textSize(const QString& text) const { void TextOverlay::setFontSize(int fontSize) { _fontSize = fontSize; - delete _textRenderer; + auto oldTextRenderer = _textRenderer; _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); + delete oldTextRenderer; } From ce613d59fda3da951cb604cf8a2b779097c42508 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 1 Jul 2015 14:46:37 -0700 Subject: [PATCH 26/44] add inbound edit packet queue depth to entity server --- assignment-client/src/octree/OctreeServer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 43bc922c74..8efe1c39a0 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -627,6 +627,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // display inbound packet stats statsString += QString().sprintf("%s Edit Statistics... [RESET]\r\n", getMyServerName()); + quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount(); quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); @@ -637,6 +638,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed; + statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n") + .arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Total Inbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Inbound Elements: %1 elements\r\n") @@ -1411,6 +1415,8 @@ void OctreeServer::sendStatsPacket() { static QJsonObject statsObject3; + statsObject3[baseName + QString(".3.inbound.data.1.packetQueue")] = + (double)_octreeInboundPacketProcessor->packetsToProcessCount(); statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = From 4c200d75bce6aaf53f24fd28250c7ddcc35b3700 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 14:53:08 -0700 Subject: [PATCH 27/44] Tweaking packet processing locking --- .../networking/src/ReceivedPacketProcessor.cpp | 13 ++++++------- libraries/networking/src/ReceivedPacketProcessor.h | 3 ++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 706266a903..0d8494d712 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -23,7 +23,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi NetworkPacket networkPacket(sendingNode, packet); lock(); - _packets.push_back(networkPacket); + _queuedPackets.push_back(networkPacket); _nodePacketCounts[sendingNode->getUUID()]++; unlock(); @@ -33,29 +33,28 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi bool ReceivedPacketProcessor::process() { - if (_packets.size() == 0) { + if (_queuedPackets.size() == 0) { _waitingOnPacketsMutex.lock(); _hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait()); _waitingOnPacketsMutex.unlock(); } preProcess(); - QVector currentPackets; - if (!_packets.size()) { + if (!_queuedPackets.size()) { return isStillRunning(); } lock(); - std::swap(currentPackets, _packets); + _processingPackets.swap(_queuedPackets); unlock(); - foreach(auto& packet, currentPackets) { + foreach(auto& packet, _processingPackets) { processPacket(packet.getNode(), packet.getByteArray()); midProcess(); } lock(); - foreach(auto& packet, currentPackets) { + foreach(auto& packet, _processingPackets) { _nodePacketCounts[packet.getNode()->getUUID()]--; } unlock(); diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index bcc9f9a1f5..1a621e505a 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -75,7 +75,8 @@ protected: protected: - QVector _packets; + QVector _queuedPackets; + QVector _processingPackets; QHash _nodePacketCounts; QWaitCondition _hasPackets; From 1e1f199fdb2dbefbb9d9f90588656db44abad8bb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 14:54:26 -0700 Subject: [PATCH 28/44] Undoing unwisdom --- .../networking/src/ReceivedPacketProcessor.cpp | 13 +++++++------ libraries/networking/src/ReceivedPacketProcessor.h | 3 +-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 0d8494d712..706266a903 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -23,7 +23,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi NetworkPacket networkPacket(sendingNode, packet); lock(); - _queuedPackets.push_back(networkPacket); + _packets.push_back(networkPacket); _nodePacketCounts[sendingNode->getUUID()]++; unlock(); @@ -33,28 +33,29 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi bool ReceivedPacketProcessor::process() { - if (_queuedPackets.size() == 0) { + if (_packets.size() == 0) { _waitingOnPacketsMutex.lock(); _hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait()); _waitingOnPacketsMutex.unlock(); } preProcess(); - if (!_queuedPackets.size()) { + QVector currentPackets; + if (!_packets.size()) { return isStillRunning(); } lock(); - _processingPackets.swap(_queuedPackets); + std::swap(currentPackets, _packets); unlock(); - foreach(auto& packet, _processingPackets) { + foreach(auto& packet, currentPackets) { processPacket(packet.getNode(), packet.getByteArray()); midProcess(); } lock(); - foreach(auto& packet, _processingPackets) { + foreach(auto& packet, currentPackets) { _nodePacketCounts[packet.getNode()->getUUID()]--; } unlock(); diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 1a621e505a..bcc9f9a1f5 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -75,8 +75,7 @@ protected: protected: - QVector _queuedPackets; - QVector _processingPackets; + QVector _packets; QHash _nodePacketCounts; QWaitCondition _hasPackets; From 1d16d80c0c8a1b553bf555d8caff3b0d7396c90f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 14:55:24 -0700 Subject: [PATCH 29/44] Comments from CR --- libraries/networking/src/ReceivedPacketProcessor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 706266a903..3c4b32b4ec 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -40,13 +40,13 @@ bool ReceivedPacketProcessor::process() { } preProcess(); - QVector currentPackets; if (!_packets.size()) { return isStillRunning(); } lock(); - std::swap(currentPackets, _packets); + QVector currentPackets; + currentPackets.swap(_packets); unlock(); foreach(auto& packet, currentPackets) { From 8b0086417598d831a307a19cd80d6071a321b084 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 1 Jul 2015 15:04:19 -0700 Subject: [PATCH 30/44] hydra hand spheres and lasers are rendering again --- examples/lineRotations.js | 134 +++++++++++++++++++++++++ interface/src/Application.cpp | 2 +- interface/src/avatar/Avatar.cpp | 8 +- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/Hand.cpp | 54 +++++----- interface/src/avatar/Hand.h | 2 +- interface/src/avatar/MyAvatar.cpp | 12 ++- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 2 +- 9 files changed, 173 insertions(+), 45 deletions(-) create mode 100644 examples/lineRotations.js diff --git a/examples/lineRotations.js b/examples/lineRotations.js new file mode 100644 index 0000000000..e60be82770 --- /dev/null +++ b/examples/lineRotations.js @@ -0,0 +1,134 @@ +// +// RenderableQuadEntityItem.cpp +// libraries/entities-renderer/src/ +// +// Created by Eric Levin on 6/22/15 +// 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 + +#include +#include + +#include +#include + +#include "RenderableQuadEntityItem.h" + + + + + + + +EntityItemPointer RenderableQuadEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return EntityItemPointer(new RenderableQuadEntityItem(entityID, properties)); +} + +RenderableQuadEntityItem::RenderableQuadEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : +QuadEntityItem(entityItemID, properties) { + _numVertices = 0; + +} + +gpu::PipelinePointer RenderableQuadEntityItem::_pipeline; +gpu::Stream::FormatPointer RenderableQuadEntityItem::_format; + +void RenderableQuadEntityItem::createPipeline() { + static const int NORMAL_OFFSET = 12; + static const int COLOR_OFFSET = 24; + _format.reset(new gpu::Stream::Format()); + _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); + _format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), COLOR_OFFSET); + + auto VS = DependencyManager::get()->getSimpleVertexShader(); + auto PS = DependencyManager::get()->getSimplePixelShader(); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + //state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); +} +int generateColor() { + float c1 = static_cast (rand()) / static_cast (RAND_MAX); + float c2 = static_cast (rand()) / static_cast (RAND_MAX); + float c3 = static_cast (rand()) / static_cast (RAND_MAX); + return ((int(c1 * 255.0f) & 0xFF)) | + ((int(c2 * 255.0f) & 0xFF) << 8) | + ((int(c3 * 255.0f) & 0xFF) << 16) | + ((int(255.0f) & 0xFF) << 24); +} + +void RenderableQuadEntityItem::updateGeometry() { + QReadLocker lock(&_quadReadWriteLock); + int compactColor = generateColor(); + _numVertices = 0; + _verticesBuffer.reset(new gpu::Buffer()); + int vertexIndex = 0; + for (int i = 0; i < _normals.size(); i++) { + compactColor = generateColor(); + _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); + vertexIndex++; + _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); + _verticesBuffer->append(sizeof(int), (gpu::Byte*)&compactColor); + _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); + vertexIndex++; + _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); + _verticesBuffer->append(sizeof(int), (gpu::Byte*)&compactColor); + + _numVertices +=2; + } + _pointsChanged = false; + + + +} + + + +void RenderableQuadEntityItem::render(RenderArgs* args) { + if (_points.size() < 2 || _vertices.size() != _normals.size() * 2) { + return; + } + + if (!_pipeline) { + createPipeline(); + } + + PerformanceTimer perfTimer("RenderableQuadEntityItem::render"); + Q_ASSERT(getType() == EntityTypes::Quad); + + Q_ASSERT(args->_batch); + if (_pointsChanged) { + updateGeometry(); + } + + + gpu::Batch& batch = *args->_batch; + Transform transform = Transform(); + transform.setTranslation(getPosition()); + transform.setRotation(getRotation()); + batch.setModelTransform(transform); + + + batch.setPipeline(_pipeline); + + batch.setInputFormat(_format); + batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); + + batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); + + RenderableDebugableEntityItem::render(this, args); +}; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2e2722aec2..ce070f4ca9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3547,7 +3547,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } //Render the sixense lasers if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { - _myAvatar->renderLaserPointers(); + _myAvatar->renderLaserPointers(*renderArgs->_batch); } if (!selfAvatarOnly) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d0778481a6..384b2f0c86 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -575,9 +575,9 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool return; } - if (postLighting) { +// if (postLighting) { getHand()->render(renderArgs, false); - } +// } } getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } @@ -1010,7 +1010,7 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) { int Avatar::_jointConesID = GeometryCache::UNKNOWN_ID; // render a makeshift cone section that serves as a body part connecting joint spheres -void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, +void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2, float radius1, float radius2, const glm::vec4& color) { auto geometryCache = DependencyManager::get(); @@ -1057,7 +1057,7 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, // TODO: this is really inefficient constantly recreating these vertices buffers. It would be // better if the avatars cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_jointConesID, points, color); - geometryCache->renderVertices(gpu::TRIANGLES, _jointConesID); + geometryCache->renderVertices(batch, gpu::TRIANGLES, _jointConesID); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 236b04864b..b23059acb0 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -148,7 +148,7 @@ public: virtual int parseDataAtOffset(const QByteArray& packet, int offset); - static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, + static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2, float radius1, float radius2, const glm::vec4& color); virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 74653d9768..7b2968973c 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -103,7 +103,8 @@ void Hand::resolvePenetrations() { } void Hand::render(RenderArgs* renderArgs, bool isMine) { - if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && + gpu::Batch& batch = *renderArgs->_batch; + if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { // draw a green sphere at hand joint location, which is actually near the wrist) for (size_t i = 0; i < getNumPalms(); i++) { @@ -112,31 +113,25 @@ void Hand::render(RenderArgs* renderArgs, bool isMine) { continue; } glm::vec3 position = palm.getPosition(); - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - DependencyManager::get()->renderSphere(PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f)); - glPopMatrix(); + Transform transform = Transform(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSphere(batch, PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10, glm::vec3(0.0f, 1.0f, 0.0f)); } } if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { - renderHandTargets(isMine); + renderHandTargets(renderArgs, isMine); } - glEnable(GL_DEPTH_TEST); - glEnable(GL_RESCALE_NORMAL); -} - -void Hand::renderHandTargets(bool isMine) { - glPushMatrix(); +} +void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { + gpu::Batch& batch = *renderArgs->_batch; const float avatarScale = DependencyManager::get()->getMyAvatar()->getScale(); const float alpha = 1.0f; const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin - - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); if (isMine && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) { for (size_t i = 0; i < getNumPalms(); ++i) { @@ -145,12 +140,12 @@ void Hand::renderHandTargets(bool isMine) { continue; } glm::vec3 targetPosition = palm.getTipPosition(); - glPushMatrix(); - glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z); + Transform transform = Transform(); + transform.setTranslation(targetPosition); + batch.setModelTransform(transform); const float collisionRadius = 0.05f; - DependencyManager::get()->renderSphere(collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false); - glPopMatrix(); + DependencyManager::get()->renderSphere(batch, collisionRadius, 10, 10, glm::vec4(0.5f,0.5f,0.5f, alpha), false); } } @@ -165,22 +160,19 @@ void Hand::renderHandTargets(bool isMine) { if (palm.isActive()) { glm::vec3 tip = palm.getTipPosition(); glm::vec3 root = palm.getPosition(); - - Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); + Transform transform = Transform(); + transform.setTranslation(glm::vec3()); + batch.setModelTransform(transform); + Avatar::renderJointConnectingCone(batch, root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); // Render sphere at palm/finger root glm::vec3 offsetFromPalm = root + palm.getNormal() * PALM_DISK_THICKNESS; - Avatar::renderJointConnectingCone(root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); - glPushMatrix(); - glTranslatef(root.x, root.y, root.z); - DependencyManager::get()->renderSphere(PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); - glPopMatrix(); + Avatar::renderJointConnectingCone(batch, root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); + transform = Transform(); + transform.setTranslation(root); + batch.setModelTransform(transform); + DependencyManager::get()->renderSphere(batch, PALM_BALL_RADIUS, 20.0f, 20.0f, glm::vec4(handColor.r, handColor.g, handColor.b, alpha)); } } - - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); - - glPopMatrix(); } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index cb35497960..f6991c5a55 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -56,7 +56,7 @@ private: Avatar* _owningAvatar; - void renderHandTargets(bool isMine); + void renderHandTargets(RenderArgs* renderArgs, bool isMine); }; #endif // hifi_Hand_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4b140e0569..3c691c49d4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1212,9 +1212,9 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } - if (postLighting) { +// if (postLighting) { getHand()->render(renderArgs, true); - } +// } } void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) { @@ -1587,7 +1587,7 @@ void MyAvatar::updateMotionBehavior() { } //Renders sixense laser pointers for UI selection with controllers -void MyAvatar::renderLaserPointers() { +void MyAvatar::renderLaserPointers(gpu::Batch& batch) { const float PALM_TIP_ROD_RADIUS = 0.002f; //If the Oculus is enabled, we will draw a blue cursor ray @@ -1600,8 +1600,10 @@ void MyAvatar::renderLaserPointers() { //Scale the root vector with the avatar scale scaleVectorRelativeToPosition(root); - - Avatar::renderJointConnectingCone(root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); + Transform transform = Transform(); + transform.setTranslation(glm::vec3()); + batch.setModelTransform(transform); + Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2fea09ee27..ce5d2148a8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -157,7 +157,7 @@ public: bool allowDuplicates = false, bool useSaved = true); /// Renders a laser pointer for UI picking - void renderLaserPointers(); + void renderLaserPointers(gpu::Batch& batch); glm::vec3 getLaserPointerTipPosition(const PalmData* palm); const RecorderPointer getRecorder() const { return _recorder; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2f66e84b50..8095ba9ac4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -805,7 +805,7 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) { // draw a green cylinder between the two points glm::vec3 origin(0.0f); - Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); +// Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); glPopMatrix(); } From 8129d0eb1ff958e86b77de51fd048a1e8965b8ae Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 1 Jul 2015 15:12:37 -0700 Subject: [PATCH 31/44] hydra lasers are now rendering again --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 14 ++++++-------- interface/src/avatar/SkeletonModel.h | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 384b2f0c86..f011e9f5de 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -449,7 +449,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } if (renderBounding && shouldRenderHead(renderArgs)) { - _skeletonModel.renderBoundingCollisionShapes(0.7f); + _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } // If this is the avatar being looked at, render a little ball above their head diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8095ba9ac4..20d458195d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -776,24 +776,24 @@ void SkeletonModel::resetShapePositionsToDefaultPose() { _boundingShape.setRotation(_rotation); } -void SkeletonModel::renderBoundingCollisionShapes(float alpha) { +void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { const int BALL_SUBDIVISIONS = 10; if (_shapes.isEmpty()) { // the bounding shape has not been propery computed // so no need to render it return; } - glPushMatrix(); - Application::getInstance()->loadTranslatedViewMatrix(_translation); // draw a blue sphere at the capsule endpoint glm::vec3 endPoint; _boundingShape.getEndPoint(endPoint); endPoint = endPoint - _translation; - glTranslatef(endPoint.x, endPoint.y, endPoint.z); + Transform transform = Transform(); + transform.setTranslation(endPoint); + batch.setModelTransform(transform); auto geometryCache = DependencyManager::get(); - geometryCache->renderSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); + geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); // draw a yellow sphere at the capsule startpoint glm::vec3 startPoint; @@ -805,9 +805,7 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) { // draw a green cylinder between the two points glm::vec3 origin(0.0f); -// Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); - - glPopMatrix(); + Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } bool SkeletonModel::hasSkeleton() { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index bffdc58659..755aa3dfee 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -101,7 +101,7 @@ public: const glm::vec3& getStandingOffset() const { return _standingOffset; } void computeBoundingShape(const FBXGeometry& geometry); - void renderBoundingCollisionShapes(float alpha); + void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } const CapsuleShape& getBoundingShape() const { return _boundingShape; } const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; } From 50a7332a5a47d0a8956968a509bccf5068759185 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 1 Jul 2015 15:14:53 -0700 Subject: [PATCH 32/44] removed accidental lineRotations.js file commit --- examples/lineRotations.js | 134 -------------------------------------- 1 file changed, 134 deletions(-) delete mode 100644 examples/lineRotations.js diff --git a/examples/lineRotations.js b/examples/lineRotations.js deleted file mode 100644 index e60be82770..0000000000 --- a/examples/lineRotations.js +++ /dev/null @@ -1,134 +0,0 @@ -// -// RenderableQuadEntityItem.cpp -// libraries/entities-renderer/src/ -// -// Created by Eric Levin on 6/22/15 -// 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 - -#include -#include - -#include -#include - -#include "RenderableQuadEntityItem.h" - - - - - - - -EntityItemPointer RenderableQuadEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return EntityItemPointer(new RenderableQuadEntityItem(entityID, properties)); -} - -RenderableQuadEntityItem::RenderableQuadEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : -QuadEntityItem(entityItemID, properties) { - _numVertices = 0; - -} - -gpu::PipelinePointer RenderableQuadEntityItem::_pipeline; -gpu::Stream::FormatPointer RenderableQuadEntityItem::_format; - -void RenderableQuadEntityItem::createPipeline() { - static const int NORMAL_OFFSET = 12; - static const int COLOR_OFFSET = 24; - _format.reset(new gpu::Stream::Format()); - _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); - _format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), COLOR_OFFSET); - - auto VS = DependencyManager::get()->getSimpleVertexShader(); - auto PS = DependencyManager::get()->getSimplePixelShader(); - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); - - gpu::Shader::BindingSet slotBindings; - gpu::Shader::makeProgram(*program, slotBindings); - - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - //state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); -} -int generateColor() { - float c1 = static_cast (rand()) / static_cast (RAND_MAX); - float c2 = static_cast (rand()) / static_cast (RAND_MAX); - float c3 = static_cast (rand()) / static_cast (RAND_MAX); - return ((int(c1 * 255.0f) & 0xFF)) | - ((int(c2 * 255.0f) & 0xFF) << 8) | - ((int(c3 * 255.0f) & 0xFF) << 16) | - ((int(255.0f) & 0xFF) << 24); -} - -void RenderableQuadEntityItem::updateGeometry() { - QReadLocker lock(&_quadReadWriteLock); - int compactColor = generateColor(); - _numVertices = 0; - _verticesBuffer.reset(new gpu::Buffer()); - int vertexIndex = 0; - for (int i = 0; i < _normals.size(); i++) { - compactColor = generateColor(); - _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); - vertexIndex++; - _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)&compactColor); - _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); - vertexIndex++; - _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)&compactColor); - - _numVertices +=2; - } - _pointsChanged = false; - - - -} - - - -void RenderableQuadEntityItem::render(RenderArgs* args) { - if (_points.size() < 2 || _vertices.size() != _normals.size() * 2) { - return; - } - - if (!_pipeline) { - createPipeline(); - } - - PerformanceTimer perfTimer("RenderableQuadEntityItem::render"); - Q_ASSERT(getType() == EntityTypes::Quad); - - Q_ASSERT(args->_batch); - if (_pointsChanged) { - updateGeometry(); - } - - - gpu::Batch& batch = *args->_batch; - Transform transform = Transform(); - transform.setTranslation(getPosition()); - transform.setRotation(getRotation()); - batch.setModelTransform(transform); - - - batch.setPipeline(_pipeline); - - batch.setInputFormat(_format); - batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); - - batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); - - RenderableDebugableEntityItem::render(this, args); -}; From f49f368ec3ca565ea1709f89a90d030589a24c74 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 1 Jul 2015 15:16:34 -0700 Subject: [PATCH 33/44] removed dead commented out code --- interface/src/avatar/MyAvatar.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3c691c49d4..de524656ec 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1212,9 +1212,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } -// if (postLighting) { - getHand()->render(renderArgs, true); -// } + getHand()->render(renderArgs, true); } void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) { From a24df5c02ff2b0983d7a736385951d029816d39b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 1 Jul 2015 15:28:48 -0700 Subject: [PATCH 34/44] added postLighting check back for rendering avatar Hands --- interface/src/avatar/Avatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f011e9f5de..6ff8fb52df 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -575,9 +575,9 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool return; } -// if (postLighting) { + if (postLighting) { getHand()->render(renderArgs, false); -// } + } } getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } From 24b4614703418600bacee900034e3e059e9b6f90 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 1 Jul 2015 16:11:08 -0700 Subject: [PATCH 35/44] add more detailed edit timing to entity server --- assignment-client/src/octree/OctreeServer.cpp | 19 ++++++++++ libraries/entities/src/EntityTree.cpp | 36 ++++++++++++++++--- libraries/entities/src/EntityTree.h | 28 +++++++++++++++ libraries/octree/src/Octree.h | 12 +++++-- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 8efe1c39a0..06fb5c4f47 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -327,6 +327,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url showStats = true; } else if (url.path() == "/resetStats") { _octreeInboundPacketProcessor->resetStats(); + _tree->resetEditStats(); resetSendingStats(); showStats = true; } @@ -636,6 +637,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed(); quint64 totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed(); + quint64 averageDecodeTime = _tree->getAverageDecodeTime(); + quint64 averageLookupTime = _tree->getAverageLookupTime(); + quint64 averageUpdateTime = _tree->getAverageUpdateTime(); + quint64 averageCreateTime = _tree->getAverageCreateTime(); + quint64 averageLoggingTime = _tree->getAverageLoggingTime(); + + float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed; statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n") @@ -658,6 +666,17 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n") .arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Decode Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageDecodeTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Lookup Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageLookupTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Update Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageUpdateTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Create Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageCreateTime).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Average Logging Time: %1 usecs\r\n") + .arg(locale.toString((uint)averageLoggingTime).rightJustified(COLUMN_WIDTH, ' ')); + int senderNumber = 0; NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 752082932b..6a652d609b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -574,41 +574,61 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char case PacketTypeEntityAdd: case PacketTypeEntityEdit: { + quint64 startDecode = 0, endDecode = 0; + quint64 startLookup = 0, endLookup = 0; + quint64 startUpdate = 0, endUpdate = 0; + quint64 startCreate = 0, endCreate = 0; + quint64 startLogging = 0, endLogging = 0; + + _totalEditMessages++; + EntityItemID entityItemID; EntityItemProperties properties; + startDecode = usecTimestampNow(); bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); + endDecode = usecTimestampNow(); // If we got a valid edit packet, then it could be a new entity or it could be an update to // an existing entity... handle appropriately if (validEditPacket) { // search for the entity by EntityItemID + startLookup = usecTimestampNow(); EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID); + endLookup = usecTimestampNow(); if (existingEntity && packetType == PacketTypeEntityEdit) { // if the EntityItem exists, then update it + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; qCDebug(entities) << " properties:" << properties; } + endLogging = usecTimestampNow(); + + startUpdate = usecTimestampNow(); updateEntity(entityItemID, properties, senderNode); existingEntity->markAsChangedOnServer(); + endUpdate = usecTimestampNow(); + _totalUpdates++; } else if (packetType == PacketTypeEntityAdd) { if (senderNode->getCanRez()) { // this is a new entity... assign a new entityID - if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() << "] adding entity."; - qCDebug(entities) << " properties:" << properties; - } properties.setCreated(properties.getLastEdited()); + startCreate = usecTimestampNow(); EntityItemPointer newEntity = addEntity(entityItemID, properties); + endCreate = usecTimestampNow(); + _totalCreates++; if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); + + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" << newEntity->getEntityItemID(); qCDebug(entities) << " properties:" << properties; } + endLogging = usecTimestampNow(); } } else { @@ -619,6 +639,14 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity.get(); } } + + + _totalDecodeTime += endDecode - startDecode; + _totalLookupTime += endLookup - startLookup; + _totalUpdateTime += endUpdate - startUpdate; + _totalCreateTime += endCreate - startCreate; + _totalLoggingTime += endLogging - startLogging; + break; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 9c4be9a86f..fa72cc7691 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -168,6 +168,23 @@ public: float getContentsLargestDimension(); + virtual void resetEditStats() { + _totalEditMessages = 0; + _totalUpdates = 0; + _totalCreates = 0; + _totalDecodeTime = 0; + _totalLookupTime = 0; + _totalUpdateTime = 0; + _totalCreateTime = 0; + _totalLoggingTime = 0; + } + + virtual quint64 getAverageDecodeTime() const { return _totalEditMessages == 0 ? 0 : _totalDecodeTime / _totalEditMessages; } + virtual quint64 getAverageLookupTime() const { return _totalEditMessages == 0 ? 0 : _totalLookupTime / _totalEditMessages; } + virtual quint64 getAverageUpdateTime() const { return _totalUpdates == 0 ? 0 : _totalUpdateTime / _totalUpdates; } + virtual quint64 getAverageCreateTime() const { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; } + virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; } + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); @@ -202,6 +219,17 @@ private: bool _wantEditLogging = false; void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL); + + + // some performance tracking properties - only used in server trees + int _totalEditMessages = 0; + int _totalUpdates = 0; + int _totalCreates = 0; + quint64 _totalDecodeTime = 0; + quint64 _totalLookupTime = 0; + quint64 _totalUpdateTime = 0; + quint64 _totalCreateTime = 0; + quint64 _totalLoggingTime = 0; }; #endif // hifi_EntityTree_h diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index d7fc58699f..6eeb423ddd 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -367,8 +367,16 @@ public: bool getIsClient() const { return !_isServer; } /// Is this a client based tree. Allows guards for certain operations void setIsClient(bool isClient) { _isServer = !isClient; } - virtual void dumpTree() { }; - virtual void pruneTree() { }; + virtual void dumpTree() { } + virtual void pruneTree() { } + + virtual void resetEditStats() { } + virtual quint64 getAverageDecodeTime() const { return 0; } + virtual quint64 getAverageLookupTime() const { return 0; } + virtual quint64 getAverageUpdateTime() const { return 0; } + virtual quint64 getAverageCreateTime() const { return 0; } + virtual quint64 getAverageLoggingTime() const { return 0; } + signals: void importSize(float x, float y, float z); From 0c88972f090abde0b2b91460aa4da37311445c9f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 14:37:23 -0700 Subject: [PATCH 36/44] Instrument the inter-idle time and tweaking the timeout setting --- interface/src/Application.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a08ac61cf2..1f22b31c64 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1784,9 +1784,23 @@ void Application::checkFPS() { DependencyManager::get()->sendDomainServerCheckIn(); } -void Application::idle() { - PerformanceTimer perfTimer("idle"); +static SimpleMovingAverage interIdleDurations; +static uint64_t lastIdleEnd{ 0 }; +void Application::idle() { + if (lastIdleEnd != 0) { + uint64_t now = usecTimestampNow(); + interIdleDurations.updateAverage(now - lastIdleEnd); + static uint64_t lastReportTime = now; + if ((now - lastReportTime) >= (1000 * 1000)) { + int avgIdleDuration = (int)interIdleDurations.getAverage(); + qDebug() << "Average inter-idle time: " << avgIdleDuration << "s for " << interIdleDurations.getSampleCount() << " samples"; + interIdleDurations.reset(); + lastReportTime = now; + } + } + + PerformanceTimer perfTimer("idle"); if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -1830,12 +1844,13 @@ void Application::idle() { } // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. - idleTimer->start(2); } - } + idleTimer->start(_glWidget->isThrottleRendering() ? 10 : 0); + } // check for any requested background downloads. emit checkBackgroundDownloads(); + lastIdleEnd = usecTimestampNow(); } void Application::setFullscreen(bool fullscreen) { From 59027959b8d3a8d7cd4e414f5d049e87b7ef1a85 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 16:30:42 -0700 Subject: [PATCH 37/44] CR comments and fixing the average calculation --- interface/src/Application.cpp | 16 ++++++++------ interface/src/InterfaceLogging.cpp | 1 + interface/src/InterfaceLogging.h | 1 + libraries/shared/src/SimpleAverage.h | 33 ++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 libraries/shared/src/SimpleAverage.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1f22b31c64..5241864100 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ using namespace std; // Starfield information static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; +static uint8_t THROTTLED_IDLE_TIMER_DELAY = 2; const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB @@ -1784,17 +1786,17 @@ void Application::checkFPS() { DependencyManager::get()->sendDomainServerCheckIn(); } -static SimpleMovingAverage interIdleDurations; -static uint64_t lastIdleEnd{ 0 }; - void Application::idle() { + static SimpleAverage interIdleDurations; + static uint64_t lastIdleEnd{ 0 }; + if (lastIdleEnd != 0) { uint64_t now = usecTimestampNow(); - interIdleDurations.updateAverage(now - lastIdleEnd); + interIdleDurations.update(now - lastIdleEnd); static uint64_t lastReportTime = now; - if ((now - lastReportTime) >= (1000 * 1000)) { + if ((now - lastReportTime) >= (USECS_PER_SECOND)) { int avgIdleDuration = (int)interIdleDurations.getAverage(); - qDebug() << "Average inter-idle time: " << avgIdleDuration << "s for " << interIdleDurations.getSampleCount() << " samples"; + qCDebug(interfaceapp_timing) << "Average inter-idle time: " << avgIdleDuration << "s for " << interIdleDurations.getCount() << " samples"; interIdleDurations.reset(); lastReportTime = now; } @@ -1845,7 +1847,7 @@ void Application::idle() { // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. } - idleTimer->start(_glWidget->isThrottleRendering() ? 10 : 0); + idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 0); } // check for any requested background downloads. diff --git a/interface/src/InterfaceLogging.cpp b/interface/src/InterfaceLogging.cpp index 18bc4e58e8..0afcb30c27 100644 --- a/interface/src/InterfaceLogging.cpp +++ b/interface/src/InterfaceLogging.cpp @@ -12,3 +12,4 @@ #include "InterfaceLogging.h" Q_LOGGING_CATEGORY(interfaceapp, "hifi.interface") +Q_LOGGING_CATEGORY(interfaceapp_timing, "hifi.interface.timing") diff --git a/interface/src/InterfaceLogging.h b/interface/src/InterfaceLogging.h index d1d92aa93d..be2ee73fba 100644 --- a/interface/src/InterfaceLogging.h +++ b/interface/src/InterfaceLogging.h @@ -15,5 +15,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(interfaceapp) +Q_DECLARE_LOGGING_CATEGORY(interfaceapp_timing) #endif // hifi_InterfaceLogging_h diff --git a/libraries/shared/src/SimpleAverage.h b/libraries/shared/src/SimpleAverage.h new file mode 100644 index 0000000000..33ed9d84cc --- /dev/null +++ b/libraries/shared/src/SimpleAverage.h @@ -0,0 +1,33 @@ +// +// Created by Bradley Austin Davis on 2015/07/01. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_SimpleAverage_h +#define hifi_SimpleAverage_h + +template +class SimpleAverage { +public: + void update(T sample) { + _sum += sample; + ++_count; + } + void reset() { + _sum = 0; + _count = 0; + } + + int getCount() const { return _count; }; + T getAverage() const { return _sum / (T)_count; }; + +private: + int _count; + T _sum; +}; + +#endif From 4cffa26c01ca8bd7b5978ea2100d6da0945a32a6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 16:53:12 -0700 Subject: [PATCH 38/44] CR comments and fixing the microsecond display --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5241864100..261055dc60 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -179,7 +179,7 @@ using namespace std; // Starfield information static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; -static uint8_t THROTTLED_IDLE_TIMER_DELAY = 2; +static uint8_t THROTTLED_IDLE_TIMER_DELAY = 10; const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB @@ -1795,8 +1795,8 @@ void Application::idle() { interIdleDurations.update(now - lastIdleEnd); static uint64_t lastReportTime = now; if ((now - lastReportTime) >= (USECS_PER_SECOND)) { - int avgIdleDuration = (int)interIdleDurations.getAverage(); - qCDebug(interfaceapp_timing) << "Average inter-idle time: " << avgIdleDuration << "s for " << interIdleDurations.getCount() << " samples"; + static QString LOGLINE("Average inter-idle time: %1 us for %2 samples"); + qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount()); interIdleDurations.reset(); lastReportTime = now; } From 88a733181e73573dcd684dd0ed6a8690e1cad31a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 16:54:59 -0700 Subject: [PATCH 39/44] Fixing comment to reflect code --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 261055dc60..9db6188404 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1845,8 +1845,9 @@ void Application::idle() { _idleLoopStdev.reset(); } - // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. } + // After finishing all of the above work, ensure the idle timer is set to the proper interval, + // depending on whether we're throttling or not idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 0); } From 566842389bccd0ac7e75c965d994116dd50bed6f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 1 Jul 2015 17:04:48 -0700 Subject: [PATCH 40/44] Animation performance test script --- examples/animationPerfTest.js | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 examples/animationPerfTest.js diff --git a/examples/animationPerfTest.js b/examples/animationPerfTest.js new file mode 100644 index 0000000000..6bf310db23 --- /dev/null +++ b/examples/animationPerfTest.js @@ -0,0 +1,91 @@ +// +// Created by Bradley Austin Davis on 2015/07/01 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var NUM_MOONS = 20; +// 1 = 60Hz, 2 = 30Hz, 3 = 20Hz, etc +var UPDATE_FREQUENCY_DIVISOR = 2; + +var MAX_RANGE = 75.0; +var LIFETIME = 600; +var SCALE = 0.1; + +var center = Vec3.sum(MyAvatar.position, + Vec3.multiply(MAX_RANGE * SCALE, Quat.getFront(Camera.getOrientation()))); + +var DEGREES_TO_RADIANS = Math.PI / 180.0; +var PARTICLE_MIN_SIZE = 2.50; +var PARTICLE_MAX_SIZE = 2.50; + + +var planet = Entities.addEntity({ + type: "Sphere", + position: center, + dimensions: { x: 10 * SCALE, y: 10 * SCALE, z: 10 * SCALE }, + color: { red: 0, green: 0, blue: 255 }, + ignoreCollisions: true, + collisionsWillMove: false, + lifetime: LIFETIME +}); + +var moons = []; + +// Create initial test particles that will move according to gravity from the planets +for (var i = 0; i < NUM_MOONS; i++) { + var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE; + radius *= SCALE; + var gray = Math.random() * 155; + var position = { x: 10 , y: i * 3, z: 0 }; + var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray }; + if (i == 0) { + color = { red: 255, green: 0, blue: 0 }; + radius = 6 * SCALE + } + moons.push(Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(center, position), + dimensions: { x: radius, y: radius, z: radius }, + color: color, + ignoreCollisions: true, + lifetime: LIFETIME, + collisionsWillMove: false + })); +} + +Script.update.connect(update); + +function scriptEnding() { + Entities.deleteEntity(planet); + for (var i = 0; i < moons.length; i++) { + Entities.deleteEntity(moons[i]); + } +} + +var totalTime = 0.0; +var updateCount = 0; +function update(deltaTime) { + // Apply gravitational force from planets + totalTime += deltaTime; + updateCount++; + if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) { + return; + } + + var planetProperties = Entities.getEntityProperties(planet); + var center = planetProperties.position; + var particlePos = Entities.getEntityProperties(moons[0]).position; + var relativePos = Vec3.subtract(particlePos.position, center); + for (var t = 0; t < moons.length; t++) { + var thetaDelta = (Math.PI * 2.0 / NUM_MOONS) * t; + var y = Math.sin(totalTime + thetaDelta) * 10.0 * SCALE; + var x = Math.cos(totalTime + thetaDelta) * 10.0 * SCALE; + var newBasePos = Vec3.sum({ x: 0, y: y, z: x }, center); + Entities.editEntity(moons[t], { position: newBasePos}); + } +} + +Script.scriptEnding.connect(scriptEnding); From 0df9ebeda399715fa99849af90f7e831feac8f48 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 1 Jul 2015 22:45:13 -0700 Subject: [PATCH 41/44] Fix rendering of look-at avatar sphere postLighting is never true. --- interface/src/avatar/Avatar.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6ff8fb52df..63b08b693c 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -451,24 +451,24 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo if (renderBounding && shouldRenderHead(renderArgs)) { _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } + } - // If this is the avatar being looked at, render a little ball above their head - if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { - const float LOOK_AT_INDICATOR_RADIUS = 0.03f; - const float LOOK_AT_INDICATOR_OFFSET = 0.22f; - const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; - glm::vec3 position; - if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { - position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z); - } else { - position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z); - } - Transform transform; - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS - , 15, 15, LOOK_AT_INDICATOR_COLOR); + // If this is the avatar being looked at, render a little ball above their head + if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { + const float LOOK_AT_INDICATOR_RADIUS = 0.03f; + const float LOOK_AT_INDICATOR_OFFSET = 0.22f; + const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; + glm::vec3 position; + if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { + position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z); + } else { + position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z); } + Transform transform; + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS + , 15, 15, LOOK_AT_INDICATOR_COLOR); } // quick check before falling into the code below: From 925fd71262700becbbc8bc3166a6b4848300703d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 1 Jul 2015 22:45:47 -0700 Subject: [PATCH 42/44] Fix rendering of look-at vectors --- interface/src/avatar/Head.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 02d16a1ca4..ed7d84dd24 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -9,9 +9,11 @@ // #include +#include +#include #include -#include +#include #include #include "Application.h" @@ -293,10 +295,8 @@ void Head::relaxLean(float deltaTime) { } void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) { - if (postLighting) { - if (_renderLookatVectors) { + if (_renderLookatVectors) { renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); - } } } @@ -380,17 +380,19 @@ void Head::addLeanDeltas(float sideways, float forward) { } void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { + auto& batch = *renderArgs->_batch; + auto transform = Transform{}; + batch.setModelTransform(transform); + batch._glLineWidth(2.0f); + + auto deferredLighting = DependencyManager::get(); + deferredLighting->bindSimpleProgram(batch); + auto geometryCache = DependencyManager::get(); - DependencyManager::get()->begin(renderArgs); - - glLineWidth(2.0); - glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f); glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f); - geometryCache->renderLine(leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); - geometryCache->renderLine(rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); - - DependencyManager::get()->end(renderArgs); + geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); + geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); } From d0675c7f22cb05ba4fa2a829ed07b0e6082e9a58 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 2 Jul 2015 09:56:02 -0500 Subject: [PATCH 43/44] Side-effect new value before emit. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b0e31361c2..6daa78457f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1188,10 +1188,10 @@ void MyAvatar::clearScriptableSettings() { } void MyAvatar::setCollisionSoundURL(const QString& url) { + _collisionSoundURL = url; if (!url.isEmpty() && (url != _collisionSoundURL)) { emit newCollisionSoundURL(QUrl(url)); } - _collisionSoundURL = url; } void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, From 1f3a1f6ac8b8e0eb053b1a4bdc004ecbd2ecab2d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 2 Jul 2015 10:07:14 -0500 Subject: [PATCH 44/44] Add comments re upcoming physics changes. --- interface/src/avatar/AvatarManager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6aa976966c..dbd46cbfbd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -258,6 +258,10 @@ void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) { void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) { // TODO: expose avatar collision events to JS for (Collision collision : collisionEvents) { + // TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR, + // and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one + // if the ids will be null), and the behavior for other avatars is not specified. This has to be fleshed + // out as soon as we use the new motionstates. if (collision.idA.isNull() || collision.idB.isNull()) { MyAvatar* myAvatar = getMyAvatar(); const QString& collisionSoundURL = myAvatar->getCollisionSoundURL(); @@ -267,7 +271,9 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) { const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); if (!isSound) { - break; + // TODO: When the new motion states are used, we'll probably break from the whole loop as soon as we hit our own avatar + // (regardless of isSound), because other users should inject for their own avatars. + continue; } // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. const float energy = velocityChange * velocityChange;