diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake
index 6dc4899d29..f51455e9d4 100755
--- a/cmake/macros/AutoScribeShader.cmake
+++ b/cmake/macros/AutoScribeShader.cmake
@@ -46,6 +46,8 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
set(SHADER_TARGET ${SHADER_TARGET}_frag.h)
endif()
+ set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
+
# Target dependant Custom rule on the SHADER_FILE
if (APPLE)
set(GLPROFILE MAC_GL)
@@ -87,10 +89,14 @@ macro(AUTOSCRIBE_SHADER_LIB)
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf)
+ #make the shader folder
+ set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
+ file(MAKE_DIRECTORY ${SHADERS_DIR})
+
#message(${SHADER_INCLUDE_FILES})
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
- file(TO_CMAKE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
+ file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
endforeach()
#message(${AUTOSCRIBE_SHADER_SRC})
@@ -105,4 +111,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
+ # Link library shaders, if they exist
+ include_directories("${SHADERS_DIR}")
+
endmacro()
diff --git a/cmake/macros/LinkHifiLibraries.cmake b/cmake/macros/LinkHifiLibraries.cmake
index ed37256b10..3767dc7131 100644
--- a/cmake/macros/LinkHifiLibraries.cmake
+++ b/cmake/macros/LinkHifiLibraries.cmake
@@ -19,20 +19,14 @@ macro(LINK_HIFI_LIBRARIES)
endif ()
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
+ include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
# link the actual library - it is static so don't bubble it up
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
-
- # ask the library what its include dependencies are and link them
- get_target_property(LINKED_TARGET_DEPENDENCY_INCLUDES ${HIFI_LIBRARY} DEPENDENCY_INCLUDES)
-
- if(LINKED_TARGET_DEPENDENCY_INCLUDES)
- list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES})
- endif()
endforeach()
setup_memory_debugger()
-endmacro(LINK_HIFI_LIBRARIES)
\ No newline at end of file
+endmacro(LINK_HIFI_LIBRARIES)
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index 7c229075fd..1101a08acb 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -1237,7 +1237,7 @@
diff --git a/examples/tests/rapidProceduralChange/rapidProceduralChangeTest.js b/examples/tests/rapidProceduralChange/rapidProceduralChangeTest.js
new file mode 100644
index 0000000000..6897a1b70f
--- /dev/null
+++ b/examples/tests/rapidProceduralChange/rapidProceduralChangeTest.js
@@ -0,0 +1,91 @@
+// rapidProceduralChangeTest.js
+// examples/tests/rapidProceduralChange
+//
+// Created by Eric Levin on 3/9/2016.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This test creates primitives with fragment shaders and rapidly updates its uniforms, as well as a skybox.
+// For the test to pass:
+// - The primitives (cube and sphere) should update at rate of update loop, cycling through red values.
+// - The skymap should do the same, although its periodicity may be different.
+//
+// Under the hood, the primitives are driven by a uniform, while the skymap is driven by a timer.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var orientation = Camera.getOrientation();
+orientation = Quat.safeEulerAngles(orientation);
+orientation.x = 0;
+orientation = Quat.fromVec3Degrees(orientation);
+
+var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
+centerUp.y += 0.5;
+var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
+centerDown.y -= 0.5;
+
+var ENTITY_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/uniformTest.fs";
+var SKYBOX_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/timerTest.fs";
+
+var entityData = {
+ ProceduralEntity: {
+ shaderUrl: ENTITY_SHADER_URL,
+ uniforms: { red: 0.0 }
+ }
+};
+var skyboxData = {
+ ProceduralEntity: {
+ shaderUrl: SKYBOX_SHADER_URL,
+ uniforms: { red: 0.0 }
+ }
+};
+
+var testBox = Entities.addEntity({
+ type: "Box",
+ dimensions: { x: 0.5, y: 0.5, z: 0.5 },
+ position: centerUp,
+ userData: JSON.stringify(entityData)
+});
+var testSphere = Entities.addEntity({
+ type: "Sphere",
+ dimensions: { x: 0.5, y: 0.5, z: 0.5 },
+ position: centerDown,
+ userData: JSON.stringify(entityData)
+});
+var testZone = Entities.addEntity({
+ type: "Zone",
+ dimensions: { x: 50, y: 50, z: 50 },
+ position: MyAvatar.position,
+ userData: JSON.stringify(skyboxData),
+ backgroundMode: "skybox",
+ skybox: { url: "http://kyoub.googlecode.com/svn/trunk/KYouB/textures/skybox_test.png" }
+});
+
+
+var currentTime = 0;
+
+function update(deltaTime) {
+ var red = (Math.sin(currentTime) + 1) / 2;
+ entityData.ProceduralEntity.uniforms.red = red;
+ skyboxData.ProceduralEntity.uniforms.red = red;
+ entityEdit = { userData: JSON.stringify(entityData) };
+ skyboxEdit = { userData: JSON.stringify(skyboxData) };
+
+ Entities.editEntity(testBox, entityEdit);
+ Entities.editEntity(testSphere, entityEdit);
+ Entities.editEntity(testZone, skyboxEdit);
+
+ currentTime += deltaTime;
+}
+
+Script.update.connect(update);
+
+Script.scriptEnding.connect(cleanup);
+
+function cleanup() {
+ Entities.deleteEntity(testBox);
+ Entities.deleteEntity(testSphere);
+ Entities.deleteEntity(testZone);
+}
+
diff --git a/examples/tests/rapidProceduralChange/timerTest.fs b/examples/tests/rapidProceduralChange/timerTest.fs
new file mode 100644
index 0000000000..e0fcf9d1d6
--- /dev/null
+++ b/examples/tests/rapidProceduralChange/timerTest.fs
@@ -0,0 +1,21 @@
+//
+// timerTest.fs
+// examples/tests/rapidProceduralChange
+//
+// Created by Eric Levin on 3/9/16.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This fragment shader is designed to test the rapid changing of a uniform on the timer.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+uniform float red;
+
+vec3 getSkyboxColor() {
+ float blue = red;
+ blue = (cos(iGlobalTime) + 1) / 2;
+ return vec3(1.0, 0.0, blue);
+}
+
diff --git a/examples/tests/rapidProceduralChange/uniformTest.fs b/examples/tests/rapidProceduralChange/uniformTest.fs
new file mode 100644
index 0000000000..fe0ad6a1ea
--- /dev/null
+++ b/examples/tests/rapidProceduralChange/uniformTest.fs
@@ -0,0 +1,27 @@
+//
+// uniformTest.fs
+// examples/tests/rapidProceduralChange
+//
+// Created by Eric Levin on 3/9/16.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This fragment shader is designed to test the rapid changing of a uniform.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+uniform float red;
+
+void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ fragColor = vec4(red, 0.0, 1.0, 1.0);
+}
+
+vec4 getProceduralColor() {
+ vec4 result;
+ vec2 position = _position.xz;
+ position += 0.5;
+ mainImage(result, position * iWorldScale.xz);
+ return result;
+}
+
diff --git a/examples/tests/skybox/px.fs b/examples/tests/skybox/px.fs
new file mode 100644
index 0000000000..8b6bdc0e5c
--- /dev/null
+++ b/examples/tests/skybox/px.fs
@@ -0,0 +1,18 @@
+//
+// px.fs
+// examples/tests/skybox
+//
+// Created by Zach Pomerantz on 3/10/2016
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+vec3 getSkyboxColor() {
+ float red = (cos(iGlobalTime) + 1) / 2;
+ vec3 color = vec3(red, 1.0, 1.0);
+
+ return color;
+}
+
diff --git a/examples/tests/skybox/px_rgba.fs b/examples/tests/skybox/px_rgba.fs
new file mode 100644
index 0000000000..59bac7d963
--- /dev/null
+++ b/examples/tests/skybox/px_rgba.fs
@@ -0,0 +1,20 @@
+//
+// px_rgba.fs
+// examples/tests/skybox
+//
+// Created by Zach Pomerantz on 3/10/2016
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+vec3 getSkyboxColor() {
+ float red = (cos(iGlobalTime) + 1) / 2;
+ vec3 color = vec3(red, 1.0, 1.0);
+
+ color *= skybox.color.rgb;
+
+ return color;
+}
+
diff --git a/examples/tests/skybox/px_tex.fs b/examples/tests/skybox/px_tex.fs
new file mode 100644
index 0000000000..9d9da7ba10
--- /dev/null
+++ b/examples/tests/skybox/px_tex.fs
@@ -0,0 +1,22 @@
+//
+// px_rgba.fs
+// examples/tests/skybox
+//
+// Created by Zach Pomerantz on 3/10/2016
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+vec3 getSkyboxColor() {
+ float red = (cos(iGlobalTime) + 1) / 2;
+ vec3 color = vec3(red, 1.0, 1.0);
+
+ vec3 coord = normalize(_normal);
+ vec3 texel = texture(cubeMap, coord).rgb;
+ color *= texel;
+
+ return color;
+}
+
diff --git a/examples/tests/skybox/px_tex_rgba.fs b/examples/tests/skybox/px_tex_rgba.fs
new file mode 100644
index 0000000000..d44a32e33e
--- /dev/null
+++ b/examples/tests/skybox/px_tex_rgba.fs
@@ -0,0 +1,24 @@
+//
+// px_rgba.fs
+// examples/tests/skybox
+//
+// Created by Zach Pomerantz on 3/10/2016
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+
+vec3 getSkyboxColor() {
+ float red = (cos(iGlobalTime) + 1) / 2;
+ vec3 color = vec3(red, 1.0, 1.0);
+
+ vec3 coord = normalize(_normal);
+ vec3 texel = texture(cubeMap, coord).rgb;
+ color *= texel;
+
+ color *= skybox.color.rgb;
+
+ return color;
+}
+
diff --git a/examples/tests/skybox/skyboxTest.js b/examples/tests/skybox/skyboxTest.js
new file mode 100644
index 0000000000..76d80ab153
--- /dev/null
+++ b/examples/tests/skybox/skyboxTest.js
@@ -0,0 +1,83 @@
+// skyboxTest.js
+// examples/tests/skybox
+//
+// Created by Zach Pomerantz on 3/10/2016.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This test cycles through different variations on the skybox with a mouseclick.
+// For the test to pass, you should observe the following cycle:
+// - Procedural skybox (no texture, no color)
+// - Procedural skybox (no texture, with color)
+// - Procedural skybox (with texture, no color)
+// - Procedural skybox (with texture, with color)
+// - Color skybox (no texture)
+// - Color skybox (with texture)
+// - Texture skybox (no color)
+//
+// As you run the test, descriptions of the expected rendered skybox will appear as overlays.
+//
+// NOTE: This does not test uniforms/textures applied to a procedural shader through userData.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var PX_URL = Script.resolvePath('px.fs');
+var PX_RGBA_URL = Script.resolvePath('px_rgba.fs');
+var PX_TEX_URL = Script.resolvePath('px_tex.fs');
+var PX_TEX_RGBA_URL = Script.resolvePath('px_tex_rgba.fs');
+
+var TEX_URL = 'https://hifi-public.s3.amazonaws.com/alan/Playa/Skies/Test-Sky_out.png';
+var NO_TEX = '';
+
+var COLOR = { red: 255, green: 0, blue: 255 };
+var NO_COLOR = { red: 0, green: 0, blue: 0 };
+
+var data = { ProceduralEntity: { shaderUrl: PX_URL } };
+
+var zone = Entities.addEntity({
+ type: 'Zone',
+ dimensions: { x: 50, y: 50, z: 50 },
+ position: MyAvatar.position,
+ backgroundMode: 'skybox'
+});
+var text = Overlays.addOverlay('text', {
+ text: 'Click this box to advance tests; note that red value cycling means white->light blue',
+ x: Window.innerWidth / 2 - 250, y: Window.innerHeight / 2 - 25,
+ width: 500, height: 50
+});
+
+print('Zone:', zone);
+print('Text:', text);
+
+var edits = [
+ ['Red value should cycle', getEdit(PX_URL, NO_TEX, NO_COLOR)],
+ ['Red value should cycle, no green', getEdit(PX_RGBA_URL, NO_TEX, COLOR)],
+ ['Red value should cycle, each face tinted differently', getEdit(PX_TEX_URL, TEX_URL, NO_COLOR)],
+ ['Red value should cycle, each face tinted differently, no green', getEdit(PX_TEX_RGBA_URL, TEX_URL, COLOR)],
+ ['No green', getEdit(null, NO_TEX, COLOR)],
+ ['Each face colored differently, no green', getEdit(null, TEX_URL, COLOR)],
+ ['Each face colored differently', getEdit(null, TEX_URL, NO_COLOR)],
+];
+
+Controller.mousePressEvent.connect(function(e) { if (Overlays.getOverlayAtPoint(e) === text) next(); });
+
+Script.scriptEnding.connect(function() {
+ Overlays.deleteOverlay(text);
+ Entities.deleteEntity(zone);
+});
+
+var i = 0;
+function next() {
+ var edit = edits[i];
+ Overlays.editOverlay(text, { text: edit[0] });
+ Entities.editEntity(zone, edit[1]);
+ i++;
+ i %= edits.length;
+}
+
+function getEdit(px, url, color) {
+ return { userData: px ? getUserData(px) : '', backgroundMode: 'skybox', skybox: { url: url, color: color } }
+}
+function getUserData(px) { return JSON.stringify({ ProceduralEntity: { shaderUrl: px } }); }
+
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 2e4cb1db0f..9bd4b00246 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3758,19 +3758,19 @@ namespace render {
switch (backgroundMode) {
case model::SunSkyStage::SKY_BOX: {
auto skybox = skyStage->getSkybox();
- if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {
+ if (skybox) {
PerformanceTimer perfTimer("skybox");
skybox->render(batch, *(args->_viewFrustum));
break;
}
- // If no skybox texture is available, render the SKY_DOME while it loads
}
- // fall through to next case
+
+ // Fall through: if no skybox is available, render the SKY_DOME
case model::SunSkyStage::SKY_DOME: {
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
PerformanceTimer perfTimer("stars");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
- "Application::payloadRender
() ... stars...");
+ "Application::payloadRender() ... My god, it's full of stars...");
// should be the first rendering pass - w/o depth buffer / lighting
static const float alpha = 1.0f;
@@ -3778,6 +3778,7 @@ namespace render {
}
}
break;
+
case model::SunSkyStage::NO_BACKGROUND:
default:
// this line intentionally left blank
@@ -4881,7 +4882,10 @@ void Application::updateDisplayMode() {
}
}
emit activeDisplayPluginChanged();
- resetSensors();
+
+ // reset the avatar, to set head and hand palms back to a resonable default pose.
+ getMyAvatar()->reset(false);
+
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
}
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index ab1b7238df..42401010b0 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -464,6 +464,8 @@ Menu::Menu() {
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
avatar, SLOT(setEnableInverseKinematics(bool)));
+ addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false,
+ avatar, SLOT(setEnableDebugDrawSensorToWorldMatrix(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 7130dd59e8..bc718bf78e 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -144,6 +144,7 @@ namespace MenuOption {
const QString RenderResolutionHalf = "1/2";
const QString RenderResolutionThird = "1/3";
const QString RenderResolutionQuarter = "1/4";
+ const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts...";
diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp
index 3305cc4f3b..a0283f0efd 100644
--- a/interface/src/Stars.cpp
+++ b/interface/src/Stars.cpp
@@ -22,11 +22,11 @@
#include
#include
-#include
-#include
+#include
+#include
-#include
-#include
+#include
+#include
//static const float TILT = 0.23f;
static const float TILT = 0.0f;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 8be8b601a4..9302c3b47d 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -438,6 +438,10 @@ void MyAvatar::updateSensorToWorldMatrix() {
lateUpdatePalms();
+ if (_enableDebugDrawSensorToWorldMatrix) {
+ DebugDraw::getInstance().addMarker("sensorToWorldMatrix", glmExtractRotation(_sensorToWorldMatrix), extractTranslation(_sensorToWorldMatrix), glm::vec4(1));
+ }
+
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
}
@@ -695,12 +699,21 @@ void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) {
void MyAvatar::setEnableDebugDrawHandControllers(bool isEnabled) {
_enableDebugDrawHandControllers = isEnabled;
+
if (!isEnabled) {
DebugDraw::getInstance().removeMarker("leftHandController");
DebugDraw::getInstance().removeMarker("rightHandController");
}
}
+void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) {
+ _enableDebugDrawSensorToWorldMatrix = isEnabled;
+
+ if (!isEnabled) {
+ DebugDraw::getInstance().removeMarker("sensorToWorldMatrix");
+ }
+}
+
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
render::ScenePointer scene = qApp->getMain3DScene();
_skeletonModel.setVisibleInScene(isEnabled, scene);
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 2301e37a5b..f834a627b2 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -278,6 +278,7 @@ public slots:
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableDebugDrawPosition(bool isEnabled);
void setEnableDebugDrawHandControllers(bool isEnabled);
+ void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
void setEnableMeshVisible(bool isEnabled);
void setUseAnimPreAndPostRotations(bool isEnabled);
@@ -442,6 +443,7 @@ private:
bool _enableDebugDrawDefaultPose { false };
bool _enableDebugDrawAnimPose { false };
bool _enableDebugDrawHandControllers { false };
+ bool _enableDebugDrawSensorToWorldMatrix { false };
AudioListenerMode _audioListenerMode;
glm::vec3 _customListenPosition;
diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index c711560b53..ac47c55eca 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -164,7 +164,7 @@ void Stats::updateStats(bool force) {
MyAvatar* myAvatar = avatarManager->getMyAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
- STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.1f);
+ STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.01f);
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
if (_expanded || force) {
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp
index 0cba089256..94533137ad 100644
--- a/interface/src/ui/overlays/Base3DOverlay.cpp
+++ b/interface/src/ui/overlays/Base3DOverlay.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include "Application.h"
const float DEFAULT_LINE_WIDTH = 1.0f;
@@ -38,15 +39,17 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
_drawInFront(base3DOverlay->_drawInFront)
{
}
-
void Base3DOverlay::setProperties(const QVariantMap& properties) {
Overlay::setProperties(properties);
+ bool needRenderItemUpdate = false;
+
auto drawInFront = properties["drawInFront"];
if (drawInFront.isValid()) {
bool value = drawInFront.toBool();
setDrawInFront(value);
+ needRenderItemUpdate = true;
}
auto position = properties["position"];
@@ -60,16 +63,19 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
}
if (position.isValid()) {
setPosition(vec3FromVariant(position));
+ needRenderItemUpdate = true;
}
if (properties["lineWidth"].isValid()) {
setLineWidth(properties["lineWidth"].toFloat());
+ needRenderItemUpdate = true;
}
auto rotation = properties["rotation"];
if (rotation.isValid()) {
setRotation(quatFromVariant(rotation));
+ needRenderItemUpdate = true;
}
if (properties["isSolid"].isValid()) {
@@ -100,6 +106,17 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
if (properties["ignoreRayIntersection"].isValid()) {
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
}
+
+ // Communicate changes to the renderItem if needed
+ if (needRenderItemUpdate) {
+ auto itemID = getRenderItemID();
+ if (render::Item::isValidID(itemID)) {
+ render::ScenePointer scene = qApp->getMain3DScene();
+ render::PendingChanges pendingChanges;
+ pendingChanges.updateItem(itemID);
+ scene->enqueuePendingChanges(pendingChanges);
+ }
+ }
}
QVariant Base3DOverlay::getProperty(const QString& property) {
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index f003b0316e..1179fbaa50 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -233,19 +233,6 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
if (thisOverlay) {
thisOverlay->setProperties(properties.toMap());
- if (thisOverlay->is3D()) {
- auto itemID = thisOverlay->getRenderItemID();
- if (render::Item::isValidID(itemID)) {
- render::ScenePointer scene = qApp->getMain3DScene();
- const render::Item& item = scene->getItem(itemID);
- if (item.getKey() != render::payloadGetKey(thisOverlay)) {
- render::PendingChanges pendingChanges;
- pendingChanges.updateItem(itemID);
- scene->enqueuePendingChanges(pendingChanges);
- }
- }
- }
-
return true;
}
return false;
diff --git a/interface/src/ui/overlays/QmlOverlay.cpp b/interface/src/ui/overlays/QmlOverlay.cpp
index 27d03b5e09..eb909de993 100644
--- a/interface/src/ui/overlays/QmlOverlay.cpp
+++ b/interface/src/ui/overlays/QmlOverlay.cpp
@@ -51,27 +51,24 @@ void QmlOverlay::buildQmlElement(const QUrl& url) {
}
QmlOverlay::~QmlOverlay() {
- if (_qmlElement) {
- _qmlElement->deleteLater();
- _qmlElement = nullptr;
- }
+ _qmlElement.reset();
}
void QmlOverlay::setProperties(const QVariantMap& properties) {
Overlay2D::setProperties(properties);
auto bounds = _bounds;
- std::weak_ptr weakQmlElement;
- DependencyManager::get()->executeOnUiThread([=] {
+ std::weak_ptr weakQmlElement = _qmlElement;
+ DependencyManager::get()->executeOnUiThread([weakQmlElement, bounds, properties] {
// check to see if qmlElement still exists
auto qmlElement = weakQmlElement.lock();
if (qmlElement) {
- _qmlElement->setX(bounds.left());
- _qmlElement->setY(bounds.top());
- _qmlElement->setWidth(bounds.width());
- _qmlElement->setHeight(bounds.height());
+ qmlElement->setX(bounds.left());
+ qmlElement->setY(bounds.top());
+ qmlElement->setWidth(bounds.width());
+ qmlElement->setHeight(bounds.height());
+ QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
}
});
- QMetaObject::invokeMethod(_qmlElement.get(), "updatePropertiesFromScript", Q_ARG(QVariant, properties));
}
void QmlOverlay::render(RenderArgs* args) {
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
index f6e9d8dbbc..860ed6ba37 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
@@ -11,6 +11,7 @@
#include
#include
+#include
#include
#include
#include
@@ -414,13 +415,39 @@ void CompositorHelper::updateTooltips() {
}
static const float FADE_DURATION = 500.0f;
+static const float FADE_IN_ALPHA = 1.0f;
+static const float FADE_OUT_ALPHA = 0.0f;
+
+void CompositorHelper::startFadeFailsafe(float endValue) {
+ _fadeStarted = usecTimestampNow();
+ _fadeFailsafeEndValue = endValue;
+
+ const int SLIGHT_DELAY = 10;
+ QTimer::singleShot(FADE_DURATION + SLIGHT_DELAY, [this]{
+ checkFadeFailsafe();
+ });
+}
+
+void CompositorHelper::checkFadeFailsafe() {
+ auto elapsedInFade = usecTimestampNow() - _fadeStarted;
+ if (elapsedInFade > FADE_DURATION) {
+ setAlpha(_fadeFailsafeEndValue);
+ }
+}
+
void CompositorHelper::fadeIn() {
_fadeInAlpha = true;
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
- _alphaPropertyAnimation->setEndValue(1.0f);
+ _alphaPropertyAnimation->setEndValue(FADE_IN_ALPHA);
_alphaPropertyAnimation->start();
+
+ // Sometimes, this "QPropertyAnimation" fails to complete the animation, and we end up with a partially faded
+ // state. So we will also have this fail-safe, where we record the timestamp of the fadeRequest, and the target
+ // value of the fade, and if after that time we still haven't faded all the way, we will kick it to the final
+ // fade value
+ startFadeFailsafe(FADE_IN_ALPHA);
}
void CompositorHelper::fadeOut() {
@@ -428,8 +455,9 @@ void CompositorHelper::fadeOut() {
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
- _alphaPropertyAnimation->setEndValue(0.0f);
+ _alphaPropertyAnimation->setEndValue(FADE_OUT_ALPHA);
_alphaPropertyAnimation->start();
+ startFadeFailsafe(FADE_OUT_ALPHA);
}
void CompositorHelper::toggle() {
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
index 55364be85b..ebc0171752 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
@@ -145,6 +145,11 @@ private:
float _fadeInAlpha { true };
float _oculusUIRadius { 1.0f };
+ quint64 _fadeStarted { 0 };
+ float _fadeFailsafeEndValue { 1.0f };
+ void checkFadeFailsafe();
+ void startFadeFailsafe(float endValue);
+
int _reticleQuad;
int _previousBorderWidth { -1 };
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index 7af97678a7..1c175b778c 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -296,6 +296,9 @@ void OpenGLDisplayPlugin::customizeContext() {
if (uniform.Name() == "mvp") {
_mvpUniform = uniform.Index();
}
+ if (uniform.Name() == "alpha") {
+ _alphaUniform = uniform.Index();
+ }
uniforms.Next();
}
@@ -406,33 +409,53 @@ void OpenGLDisplayPlugin::updateFramerate() {
void OpenGLDisplayPlugin::compositeOverlay() {
using namespace oglplus;
- // Overlay draw
- if (isStereo()) {
- Uniform(*_program, _mvpUniform).Set(mat4());
- for_each_eye([&](Eye eye) {
- eyeViewport(eye);
- drawUnitQuad();
- });
- } else {
+
+ auto compositorHelper = DependencyManager::get();
+
+ // check the alpha
+ auto overlayAlpha = compositorHelper->getAlpha();
+ if (overlayAlpha > 0.0f) {
+ // set the alpha
+ Uniform(*_program, _alphaUniform).Set(overlayAlpha);
+
// Overlay draw
- Uniform(*_program, _mvpUniform).Set(mat4());
- drawUnitQuad();
+ if (isStereo()) {
+ Uniform(*_program, _mvpUniform).Set(mat4());
+ for_each_eye([&](Eye eye) {
+ eyeViewport(eye);
+ drawUnitQuad();
+ });
+ } else {
+ // Overlay draw
+ Uniform(*_program, _mvpUniform).Set(mat4());
+ drawUnitQuad();
+ }
}
+ Uniform(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get();
- Uniform(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
- if (isStereo()) {
- for_each_eye([&](Eye eye) {
- eyeViewport(eye);
+
+ // check the alpha
+ auto overlayAlpha = compositorHelper->getAlpha();
+ if (overlayAlpha > 0.0f) {
+ // set the alpha
+ Uniform(*_program, _alphaUniform).Set(overlayAlpha);
+
+ Uniform(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
+ if (isStereo()) {
+ for_each_eye([&](Eye eye) {
+ eyeViewport(eye);
+ drawUnitQuad();
+ });
+ } else {
drawUnitQuad();
- });
- } else {
- drawUnitQuad();
+ }
}
Uniform(*_program, _mvpUniform).Set(mat4());
+ Uniform(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositeLayers() {
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index 576d6d8eff..ace866a946 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -86,6 +86,7 @@ protected:
ProgramPtr _program;
int32_t _mvpUniform { -1 };
+ int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
mutable Mutex _mutex;
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index 92bfef0dc8..e3d92d5761 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -62,30 +62,50 @@ void HmdDisplayPlugin::uncustomizeContext() {
void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus;
- _sphereSection->Use();
- for_each_eye([&](Eye eye) {
- eyeViewport(eye);
- auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
- auto mvp = _eyeProjections[eye] * modelView;
- Uniform(*_program, _mvpUniform).Set(mvp);
- _sphereSection->Draw();
- });
+ auto compositorHelper = DependencyManager::get();
+
+ // check the alpha
+ auto overlayAlpha = compositorHelper->getAlpha();
+ if (overlayAlpha > 0.0f) {
+ // set the alpha
+ Uniform(*_program, _alphaUniform).Set(overlayAlpha);
+
+ _sphereSection->Use();
+ for_each_eye([&](Eye eye) {
+ eyeViewport(eye);
+ auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
+ auto mvp = _eyeProjections[eye] * modelView;
+ Uniform(*_program, _mvpUniform).Set(mvp);
+ _sphereSection->Draw();
+ });
+ }
+ Uniform(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::compositePointer() {
- //Mouse Pointer
+ using namespace oglplus;
+
auto compositorHelper = DependencyManager::get();
- _plane->Use();
- // Reconstruct the headpose from the eye poses
- auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
- for_each_eye([&](Eye eye) {
- using namespace oglplus;
- eyeViewport(eye);
- auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
- auto mvp = _eyeProjections[eye] * reticleTransform;
- Uniform(*_program, _mvpUniform).Set(mvp);
- _plane->Draw();
- });
+
+ // check the alpha
+ auto overlayAlpha = compositorHelper->getAlpha();
+ if (overlayAlpha > 0.0f) {
+ // set the alpha
+ Uniform(*_program, _alphaUniform).Set(overlayAlpha);
+
+ // Mouse pointer
+ _plane->Use();
+ // Reconstruct the headpose from the eye poses
+ auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
+ for_each_eye([&](Eye eye) {
+ eyeViewport(eye);
+ auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
+ auto mvp = _eyeProjections[eye] * reticleTransform;
+ Uniform(*_program, _mvpUniform).Set(mvp);
+ _plane->Draw();
+ });
+ }
+ Uniform(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::internalPresent() {
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 44c3146b41..39111dd3eb 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -140,8 +140,8 @@ void EntityTreeRenderer::update() {
// If we haven't already updated and previously attempted to load a texture,
// check if the texture loaded and apply it
if (!updated && (
- (_pendingSkyboxTexture && _skyboxTexture && _skyboxTexture->isLoaded()) ||
- (_pendingAmbientTexture && _ambientTexture && _ambientTexture->isLoaded()))) {
+ (_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())) ||
+ (_pendingAmbientTexture && (!_ambientTexture && _ambientTexture->isLoaded())))) {
applyZonePropertiesToScene(_bestZone);
}
@@ -158,6 +158,8 @@ void EntityTreeRenderer::update() {
}
bool EntityTreeRenderer::checkEnterLeaveEntities() {
+ bool didUpdate = false;
+
if (_tree && !_shuttingDown) {
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
@@ -172,6 +174,7 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
std::static_pointer_cast(_tree)->findEntities(avatarPosition, radius, foundEntities);
// Whenever you're in an intersection between zones, we will always choose the smallest zone.
+ auto oldBestZone = _bestZone;
_bestZone = nullptr; // NOTE: Is this what we want?
_bestZoneVolume = std::numeric_limits::max();
@@ -204,7 +207,10 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
}
}
- applyZonePropertiesToScene(_bestZone);
+ if (_bestZone != oldBestZone) {
+ applyZonePropertiesToScene(_bestZone);
+ didUpdate = true;
+ }
});
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
@@ -228,11 +234,9 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
}
_currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition;
-
- return true;
}
}
- return false;
+ return didUpdate;
}
void EntityTreeRenderer::leaveAllEntities() {
@@ -322,15 +326,19 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE);
- if (_ambientTexture && _ambientTexture->isLoaded() && _ambientTexture->getGPUTexture()) {
+ _pendingAmbientTexture = true;
+
+ if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
- if (_ambientTexture->getGPUTexture()->getIrradiance()) {
- sceneKeyLight->setAmbientSphere(_ambientTexture->getGPUTexture()->getIrradiance());
- sceneKeyLight->setAmbientMap(_ambientTexture->getGPUTexture());
+
+ auto texture = _ambientTexture->getGPUTexture();
+ if (texture) {
+ sceneKeyLight->setAmbientSphere(texture->getIrradiance());
+ sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
+ } else {
+ qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << zone->getKeyLightProperties().getAmbientURL();
}
- } else {
- _pendingAmbientTexture = true;
}
}
@@ -341,32 +349,30 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetUserData()) {
userData = zone->getUserData();
- auto procedural = std::make_shared(userData);
- if (procedural->_enabled) {
- skybox->setProcedural(procedural);
- } else {
- skybox->setProcedural(ProceduralPointer());
- }
+ skybox->parse(userData);
}
if (zone->getSkyboxProperties().getURL().isEmpty()) {
- skybox->setCubemap(gpu::TexturePointer());
+ skybox->setCubemap(nullptr);
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
} else {
// Update the Texture of the Skybox with the one pointed by this zone
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
+ _pendingSkyboxTexture = true;
+
+ if (_skyboxTexture && _skyboxTexture->isLoaded()) {
+ _pendingSkyboxTexture = false;
- if (_skyboxTexture && _skyboxTexture->isLoaded() && _skyboxTexture->getGPUTexture()) {
auto texture = _skyboxTexture->getGPUTexture();
skybox->setCubemap(texture);
- _pendingSkyboxTexture = false;
- if (!isAmbientTextureSet && texture->getIrradiance()) {
+ if (!isAmbientTextureSet) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
}
} else {
- _pendingSkyboxTexture = true;
+ skybox->setCubemap(nullptr);
+ qCDebug(entitiesrenderer) << "Failed to load skybox:" << zone->getSkyboxProperties().getURL();
}
}
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index 671043813e..e392450c08 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -19,8 +19,8 @@
#include
#include
-#include "../render-utils/simple_vert.h"
-#include "../render-utils/simple_frag.h"
+#include
+#include
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity{ new RenderableBoxEntityItem(entityID) };
@@ -31,7 +31,9 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
void RenderableBoxEntityItem::setUserData(const QString& value) {
if (value != getUserData()) {
BoxEntityItem::setUserData(value);
- _procedural.reset();
+ if (_procedural) {
+ _procedural->parse(value);
+ }
}
}
@@ -40,7 +42,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
Q_ASSERT(getType() == EntityTypes::Box);
Q_ASSERT(args->_batch);
-
if (!_procedural) {
_procedural.reset(new Procedural(this->getUserData()));
_procedural->_vertexSource = simple_vert;
@@ -62,7 +63,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
}
batch.setModelTransform(transToCenter); // we want to include the scale as well
- if (_procedural && _procedural->ready()) {
+ if (_procedural->ready()) {
_procedural->prepare(batch, getPosition(), getDimensions());
auto color = _procedural->getColor(cubeColor);
batch._glColor4f(color.r, color.g, color.b, color.a);
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index b5867fb1ee..c3437b0e4a 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -19,8 +19,8 @@
#include
#include
-#include "../render-utils/simple_vert.h"
-#include "../render-utils/simple_frag.h"
+#include
+#include
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
@@ -36,7 +36,9 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
void RenderableSphereEntityItem::setUserData(const QString& value) {
if (value != getUserData()) {
SphereEntityItem::setUserData(value);
- _procedural.reset();
+ if (_procedural) {
+ _procedural->parse(value);
+ }
}
}
diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp
index a579f061b7..1dd7068448 100644
--- a/libraries/gl/src/gl/OglplusHelpers.cpp
+++ b/libraries/gl/src/gl/OglplusHelpers.cpp
@@ -33,6 +33,7 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
#pragma line __LINE__
uniform sampler2D sampler;
+uniform float alpha = 1.0;
in vec2 vTexCoord;
out vec4 FragColor;
@@ -40,6 +41,7 @@ out vec4 FragColor;
void main() {
FragColor = texture(sampler, vTexCoord);
+ FragColor.a *= alpha;
}
)FS";
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index a2cd3284ef..58a82d5f11 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -146,7 +146,7 @@ public:
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, const QByteArray& content) {
TextureExtra extra = { type, content };
- return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast();
+ return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast();
}
/// Returns a texture version of an image file
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 72a09a8b3f..6fb0cc3177 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -101,8 +101,6 @@ private:
/// A simple object wrapper for an OpenGL texture.
class Texture {
public:
- friend class TextureCache;
-
gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); }
gpu::TextureSourcePointer _textureSource;
};
diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp
index 476ac2fa08..cb3fb43630 100755
--- a/libraries/model/src/model/Skybox.cpp
+++ b/libraries/model/src/model/Skybox.cpp
@@ -15,71 +15,68 @@
#include
#include
-#include "Skybox_vert.h"
-#include "Skybox_frag.h"
+#include "skybox_vert.h"
+#include "skybox_frag.h"
using namespace model;
Skybox::Skybox() {
- Data data;
- _dataBuffer = gpu::BufferView(std::make_shared(sizeof(Data), (const gpu::Byte*) &data));
-
-/* // PLease create a default engineer skybox
- _cubemap.reset( gpu::Texture::createCube(gpu::Element::COLOR_RGBA_32, 1));
- unsigned char texels[] = {
- 255, 0, 0, 255,
- 0, 255, 255, 255,
- 0, 0, 255, 255,
- 255, 255, 0, 255,
- 0, 255, 0, 255,
- 255, 0, 255, 255,
- };
- _cubemap->assignStoredMip(0, gpu::Element::COLOR_RGBA_32, sizeof(texels), texels);*/
+ Schema schema;
+ _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema));
}
void Skybox::setColor(const Color& color) {
- _dataBuffer.edit()._color = color;
+ _schemaBuffer.edit().color = color;
}
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
_cubemap = cubemap;
}
-
-void Skybox::updateDataBuffer() const {
+void Skybox::updateSchemaBuffer() const {
auto blend = 0.0f;
if (getCubemap() && getCubemap()->isDefined()) {
- blend = 1.0f;
+ blend = 0.5f;
+
// If pitch black neutralize the color
if (glm::all(glm::equal(getColor(), glm::vec3(0.0f)))) {
- blend = 2.0f;
+ blend = 1.0f;
}
}
- if (blend != _dataBuffer.get()._blend) {
- _dataBuffer.edit()._blend = blend;
+ if (blend != _schemaBuffer.get().blend) {
+ _schemaBuffer.edit().blend = blend;
}
}
+void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const {
+ if (bufferSlot > -1) {
+ batch.setUniformBuffer(bufferSlot, _schemaBuffer);
+ }
-
-void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
- updateDataBuffer();
- Skybox::render(batch, frustum, (*this));
+ if (textureSlot > -1) {
+ gpu::TexturePointer skymap = getCubemap();
+ // FIXME: skymap->isDefined may not be threadsafe
+ if (skymap && skymap->isDefined()) {
+ batch.setResourceTexture(textureSlot, skymap);
+ }
+ }
}
+void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
+ updateSchemaBuffer();
+ Skybox::render(batch, frustum, (*this));
+}
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
// Create the static shared elements used to render the skybox
static gpu::BufferPointer theConstants;
static gpu::PipelinePointer thePipeline;
- const int SKYBOX_SKYMAP_SLOT = 0;
- const int SKYBOX_CONSTANTS_SLOT = 0;
static std::once_flag once;
std::call_once(once, [&] {
{
- auto skyVS = gpu::Shader::createVertex(std::string(Skybox_vert));
- auto skyFS = gpu::Shader::createPixel(std::string(Skybox_frag));
+ auto skyVS = gpu::Shader::createVertex(std::string(skybox_vert));
+ auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag));
auto skyShader = gpu::Shader::createProgram(skyVS, skyFS);
gpu::Shader::BindingSet bindings;
@@ -98,10 +95,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
// Render
- gpu::TexturePointer skymap = skybox.getCubemap();
- // FIXME: skymap->isDefined may not be threadsafe
- assert(skymap && skymap->isDefined());
-
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
@@ -112,11 +105,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
batch.setModelTransform(Transform()); // only for Mac
batch.setPipeline(thePipeline);
- batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
- batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
-
+ skybox.prepare(batch);
batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
}
-
diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h
index 14ba9fa005..e9e7ee8b26 100755
--- a/libraries/model/src/model/Skybox.h
+++ b/libraries/model/src/model/Skybox.h
@@ -30,30 +30,33 @@ public:
virtual ~Skybox() {};
void setColor(const Color& color);
- const Color getColor() const { return _dataBuffer.get()._color; }
+ const Color getColor() const { return _schemaBuffer.get().color; }
void setCubemap(const gpu::TexturePointer& cubemap);
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
+ void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const;
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
-
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
protected:
+ static const int SKYBOX_SKYMAP_SLOT { 0 };
+ static const int SKYBOX_CONSTANTS_SLOT { 0 };
+
gpu::TexturePointer _cubemap;
- class Data {
+ class Schema {
public:
- glm::vec3 _color{ 1.0f, 1.0f, 1.0f };
- float _blend = 1.0f;
+ glm::vec3 color { 1.0f, 1.0f, 1.0f };
+ float blend { 0.0f };
};
- mutable gpu::BufferView _dataBuffer;
+ mutable gpu::BufferView _schemaBuffer;
- void updateDataBuffer() const;
+ void updateSchemaBuffer() const;
};
-typedef std::shared_ptr< Skybox > SkyboxPointer;
+typedef std::shared_ptr SkyboxPointer;
};
diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf
deleted file mode 100755
index f8a568bcf9..0000000000
--- a/libraries/model/src/model/Skybox.slf
+++ /dev/null
@@ -1,59 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-// Generated on <$_SCRIBE_DATE$>
-// skybox.frag
-// fragment shader
-//
-// Created by Sam Gateau on 5/5/2015.
-// 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
-//
-
-uniform samplerCube cubeMap;
-
-struct Skybox {
- vec4 _color;
-};
-
-uniform skyboxBuffer {
- Skybox _skybox;
-};
-
-in vec3 _normal;
-out vec4 _fragColor;
-
-//PROCEDURAL_COMMON_BLOCK
-
-#line 1001
-//PROCEDURAL_BLOCK
-
-#line 2033
-void main(void) {
-
-#ifdef PROCEDURAL
-
- vec3 color = getSkyboxColor();
- _fragColor = vec4(color, 0.0);
-
-#else
-
- vec3 coord = normalize(_normal);
-
- // Skybox color or blend with skymap
- vec3 color = _skybox._color.rgb;
- if (_skybox._color.a > 0.0) {
- vec3 texel = texture(cubeMap, coord).rgb;
- if (_skybox._color.a < 2.0) {
- color *= texel;
- } else {
- color = texel;
- }
- }
-
- _fragColor = vec4(color, 0.0);
-
-#endif
-
-}
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slf b/libraries/model/src/model/skybox.slf
old mode 100644
new mode 100755
similarity index 71%
rename from libraries/procedural/src/procedural/ProceduralSkybox.slf
rename to libraries/model/src/model/skybox.slf
index 7ad6f6b5a1..7b25e36af7
--- a/libraries/procedural/src/procedural/ProceduralSkybox.slf
+++ b/libraries/model/src/model/skybox.slf
@@ -14,11 +14,11 @@
uniform samplerCube cubeMap;
struct Skybox {
- vec4 _color;
+ vec4 color;
};
uniform skyboxBuffer {
- Skybox _skybox;
+ Skybox skybox;
};
in vec3 _normal;
@@ -39,11 +39,20 @@ void main(void) {
color = pow(color, vec3(2.2));
_fragColor = vec4(color, 0.0);
-#else
+ // FIXME: scribe does not yet scrub out else statements
+ return;
+#else
vec3 coord = normalize(_normal);
- vec3 texel = texture(cubeMap, coord).rgb;
- vec3 color = texel * _skybox._color.rgb;
+ vec3 color = skybox.color.rgb;
+
+ // blend is only set if there is a cubemap
+ if (skybox.color.a > 0.0) {
+ color = texture(cubeMap, coord).rgb;
+ if (skybox.color.a < 1.0) {
+ color *= skybox.color.rgb;
+ }
+ }
_fragColor = vec4(color, 0.0);
#endif
diff --git a/libraries/model/src/model/Skybox.slv b/libraries/model/src/model/skybox.slv
similarity index 100%
rename from libraries/model/src/model/Skybox.slv
rename to libraries/model/src/model/skybox.slv
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index b1e4bd53ed..922d466d42 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -62,6 +62,9 @@ QJsonValue Procedural::getProceduralData(const QString& proceduralJson) {
return doc.object()[PROCEDURAL_USER_DATA_KEY];
}
+Procedural::Procedural() {
+ _state = std::make_shared();
+}
Procedural::Procedural(const QString& userDataJson) {
parse(userDataJson);
@@ -69,74 +72,111 @@ Procedural::Procedural(const QString& userDataJson) {
}
void Procedural::parse(const QString& userDataJson) {
- _enabled = false;
auto proceduralData = getProceduralData(userDataJson);
- if (proceduralData.isObject()) {
- parse(proceduralData.toObject());
+ // Instead of parsing, prep for a parse on the rendering thread
+ // This will be called by Procedural::ready
+ std::lock_guard lock(_proceduralDataMutex);
+ _proceduralData = proceduralData.toObject();
+ _proceduralDataDirty = true;
+}
+
+bool Procedural::parseVersion(const QJsonValue& version) {
+ if (version.isDouble()) {
+ _version = (uint8_t)(floor(version.toDouble()));
+ } else {
+ // All unversioned shaders default to V1
+ _version = 1;
}
+ return (_version == 1 || _version == 2);
+}
+
+bool Procedural::parseUrl(const QUrl& shaderUrl) {
+ if (!shaderUrl.isValid()) {
+ qWarning() << "Invalid shader URL: " << shaderUrl;
+ return false;
+ }
+
+ if (_shaderUrl == shaderUrl) {
+ return true;
+ }
+
+ _shaderUrl = shaderUrl;
+ _shaderDirty = true;
+
+ if (_shaderUrl.isLocalFile()) {
+ _shaderPath = _shaderUrl.toLocalFile();
+ qDebug() << "Shader path: " << _shaderPath;
+ if (!QFile(_shaderPath).exists()) {
+ return false;;
+ }
+ } else {
+ qDebug() << "Shader url: " << _shaderUrl;
+ _networkShader = ShaderCache::instance().getShader(_shaderUrl);
+ }
+
+ return true;
+}
+
+bool Procedural::parseUniforms(const QJsonObject& uniforms) {
+ if (_parsedUniforms != uniforms) {
+ _parsedUniforms = uniforms;
+ _uniformsDirty = true;
+ }
+
+ return true;
+}
+
+bool Procedural::parseTextures(const QJsonArray& channels) {
+ if (_parsedChannels != channels) {
+ _parsedChannels = channels;
+
+ auto textureCache = DependencyManager::get();
+ size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
+ for (size_t i = 0; i < channelCount; ++i) {
+ QString url = _parsedChannels.at((int)i).toString();
+ _channels[i] = textureCache->getTexture(QUrl(url));
+ }
+
+ _channelsDirty = true;
+ }
+
+ return true;
}
void Procedural::parse(const QJsonObject& proceduralData) {
- // grab the version number
- {
- auto version = proceduralData[VERSION_KEY];
- if (version.isDouble()) {
- _version = (uint8_t)(floor(version.toDouble()));
- }
+ _enabled = false;
+
+ if (proceduralData.isEmpty()) {
+ return;
}
- // Get the path to the shader
- {
- QString shaderUrl = proceduralData[URL_KEY].toString();
- shaderUrl = ResourceManager::normalizeURL(shaderUrl);
- _shaderUrl = QUrl(shaderUrl);
- if (!_shaderUrl.isValid()) {
- qWarning() << "Invalid shader URL: " << shaderUrl;
- return;
- }
+ auto version = proceduralData[VERSION_KEY];
+ auto shaderUrl = proceduralData[URL_KEY].toString();
+ shaderUrl = ResourceManager::normalizeURL(shaderUrl);
+ auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
+ auto channels = proceduralData[CHANNELS_KEY].toArray();
- if (_shaderUrl.isLocalFile()) {
- _shaderPath = _shaderUrl.toLocalFile();
- qDebug() << "Shader path: " << _shaderPath;
- if (!QFile(_shaderPath).exists()) {
- return;
- }
- } else {
- qDebug() << "Shader url: " << _shaderUrl;
- _networkShader = ShaderCache::instance().getShader(_shaderUrl);
- }
+ if (parseVersion(version) &&
+ parseUrl(shaderUrl) &&
+ parseUniforms(uniforms) &&
+ parseTextures(channels)) {
+ _enabled = true;
}
-
- // Grab any custom uniforms
- {
- auto uniforms = proceduralData[UNIFORMS_KEY];
- if (uniforms.isObject()) {
- _parsedUniforms = uniforms.toObject();
- }
- }
-
- // Grab any textures
- {
- auto channels = proceduralData[CHANNELS_KEY];
- if (channels.isArray()) {
- auto textureCache = DependencyManager::get();
- _parsedChannels = channels.toArray();
- size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
- for (size_t i = 0; i < channelCount; ++i) {
- QString url = _parsedChannels.at((int)i).toString();
- _channels[i] = textureCache->getTexture(QUrl(url));
- }
- }
- }
- _enabled = true;
}
bool Procedural::ready() {
+ // Load any changes to the procedural
+ if (_proceduralDataDirty) {
+ std::lock_guard lock(_proceduralDataMutex);
+ parse(_proceduralData);
+ _proceduralDataDirty = false;
+ }
+
if (!_enabled) {
return false;
}
- // Do we have a network or local shader
+ // Do we have a network or local shader, and if so, is it loaded?
if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) {
return false;
}
@@ -160,15 +200,14 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
QFile file(_shaderPath);
file.open(QIODevice::ReadOnly);
_shaderSource = QTextStream(&file).readAll();
- _pipelineDirty = true;
+ _shaderDirty = true;
_shaderModified = lastModified;
}
} else if (_networkShader && _networkShader->isLoaded()) {
_shaderSource = _networkShader->_source;
}
- if (!_pipeline || _pipelineDirty) {
- _pipelineDirty = true;
+ if (!_pipeline || _shaderDirty) {
if (!_vertexShader) {
_vertexShader = gpu::Shader::createVertex(_vertexSource);
}
@@ -192,7 +231,10 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
if (replaceIndex != std::string::npos) {
fragmentShaderSource.replace(replaceIndex, PROCEDURAL_BLOCK.size(), _shaderSource.toLocal8Bit().data());
}
- //qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
+
+ // Leave this here for debugging
+ // qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
+
_fragmentShader = gpu::Shader::createPixel(fragmentShaderSource);
_shader = gpu::Shader::createProgram(_vertexShader, _fragmentShader);
@@ -214,11 +256,15 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
batch.setPipeline(_pipeline);
- if (_pipelineDirty) {
- _pipelineDirty = false;
+ if (_shaderDirty || _uniformsDirty) {
setupUniforms();
}
+ if (_shaderDirty || _uniformsDirty || _channelsDirty) {
+ setupChannels(_shaderDirty || _uniformsDirty);
+ }
+
+ _shaderDirty = _uniformsDirty = _channelsDirty = false;
for (auto lambda : _uniforms) {
lambda(batch);
@@ -359,8 +405,14 @@ void Procedural::setupUniforms() {
batch._glUniform(_standardUniformSlots[POSITION], _entityPosition);
});
}
+}
+void Procedural::setupChannels(bool shouldCreate) {
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) {
+ if (!shouldCreate) {
+ // Instead of modifying the last element, just remove and recreate it.
+ _uniforms.pop_back();
+ }
_uniforms.push_back([=](gpu::Batch& batch) {
vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS];
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index 1b02fbd435..0e55f52f5a 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -28,27 +28,25 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
// FIXME better encapsulation
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
struct Procedural {
+public:
static QJsonValue getProceduralData(const QString& proceduralJson);
+ Procedural();
Procedural(const QString& userDataJson);
void parse(const QString& userDataJson);
- void parse(const QJsonObject&);
+
bool ready();
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
- void setupUniforms();
+ const gpu::ShaderPointer& getShader() const { return _shader; }
+
glm::vec4 getColor(const glm::vec4& entityColor);
- bool _enabled{ false };
- uint8_t _version{ 1 };
+ uint8_t _version { 1 };
std::string _vertexSource;
std::string _fragmentSource;
- QString _shaderSource;
- QString _shaderPath;
- QUrl _shaderUrl;
- quint64 _shaderModified{ 0 };
- bool _pipelineDirty{ true };
+ gpu::StatePointer _state;
enum StandardUniforms {
DATE,
@@ -60,23 +58,50 @@ struct Procedural {
NUM_STANDARD_UNIFORMS
};
- int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
+protected:
+ // Procedural metadata
+ bool _enabled { false };
+ uint64_t _start { 0 };
+ int32_t _frameCount { 0 };
- uint64_t _start{ 0 };
- int32_t _frameCount{ 0 };
+ // Rendering object descriptions, from userData
+ QJsonObject _proceduralData;
+ std::mutex _proceduralDataMutex;
+ QString _shaderSource;
+ QString _shaderPath;
+ QUrl _shaderUrl;
+ quint64 _shaderModified { 0 };
NetworkShaderPointer _networkShader;
QJsonObject _parsedUniforms;
QJsonArray _parsedChannels;
+ bool _proceduralDataDirty { true };
+ bool _shaderDirty { true };
+ bool _uniformsDirty { true };
+ bool _channelsDirty { true };
+ // Rendering objects
UniformLambdas _uniforms;
+ int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
gpu::PipelinePointer _pipeline;
gpu::ShaderPointer _vertexShader;
gpu::ShaderPointer _fragmentShader;
gpu::ShaderPointer _shader;
- gpu::StatePointer _state;
+
+ // Entity metadata
glm::vec3 _entityDimensions;
glm::vec3 _entityPosition;
+
+private:
+ // This should only be called from the render thread, as it shares data with Procedural::prepare
+ void parse(const QJsonObject&);
+ bool parseVersion(const QJsonValue& version);
+ bool parseUrl(const QUrl& url);
+ bool parseUniforms(const QJsonObject& uniforms);
+ bool parseTextures(const QJsonArray& channels);
+
+ void setupUniforms();
+ void setupChannels(bool shouldCreate);
};
#endif
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
index 167d49cbaf..4ff38eac74 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
@@ -15,55 +15,39 @@
#include
#include
-#include "ProceduralSkybox_vert.h"
-#include "ProceduralSkybox_frag.h"
+#include
+#include
ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
-}
-
-ProceduralSkybox::ProceduralSkybox(const ProceduralSkybox& skybox) :
- model::Skybox(skybox),
- _procedural(skybox._procedural) {
-
-}
-
-void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) {
- _procedural = procedural;
- if (_procedural) {
- _procedural->_vertexSource = ProceduralSkybox_vert;
- _procedural->_fragmentSource = ProceduralSkybox_frag;
- // Adjust the pipeline state for background using the stencil test
- _procedural->_state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
- }
+ _procedural._vertexSource = skybox_vert;
+ _procedural._fragmentSource = skybox_frag;
+ // Adjust the pipeline state for background using the stencil test
+ _procedural._state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
}
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
- ProceduralSkybox::render(batch, frustum, (*this));
+ if (_procedural.ready()) {
+ ProceduralSkybox::render(batch, frustum, (*this));
+ } else {
+ Skybox::render(batch, frustum);
+ }
}
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) {
- if (!(skybox._procedural)) {
- skybox.updateDataBuffer();
- Skybox::render(batch, viewFrustum, skybox);
- }
+ glm::mat4 projMat;
+ viewFrustum.evalProjectionMatrix(projMat);
- if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) {
- gpu::TexturePointer skymap = skybox.getCubemap();
- // FIXME: skymap->isDefined may not be threadsafe
- assert(skymap && skymap->isDefined());
+ Transform viewTransform;
+ viewFrustum.evalViewTransform(viewTransform);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewTransform);
+ batch.setModelTransform(Transform()); // only for Mac
- glm::mat4 projMat;
- viewFrustum.evalProjectionMatrix(projMat);
-
- Transform viewTransform;
- viewFrustum.evalViewTransform(viewTransform);
- batch.setProjectionTransform(projMat);
- batch.setViewTransform(viewTransform);
- batch.setModelTransform(Transform()); // only for Mac
- batch.setResourceTexture(0, skybox.getCubemap());
-
- skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1));
- batch.draw(gpu::TRIANGLE_STRIP, 4);
- }
+ auto& procedural = skybox._procedural;
+ procedural.prepare(batch, glm::vec3(0), glm::vec3(1));
+ auto textureSlot = procedural.getShader()->getTextures().findLocation("cubeMap");
+ auto bufferSlot = procedural.getShader()->getBuffers().findLocation("skyboxBuffer");
+ skybox.prepare(batch, textureSlot, bufferSlot);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
}
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h
index 057a1ccc74..e817ce271d 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.h
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.h
@@ -17,22 +17,18 @@
#include "Procedural.h"
-typedef std::shared_ptr ProceduralPointer;
-
class ProceduralSkybox: public model::Skybox {
public:
ProceduralSkybox();
- ProceduralSkybox(const ProceduralSkybox& skybox);
- ProceduralSkybox& operator= (const ProceduralSkybox& skybox);
virtual ~ProceduralSkybox() {};
- void setProcedural(const ProceduralPointer& procedural);
+ void parse(const QString& userData) { _procedural.parse(userData); }
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox);
protected:
- ProceduralPointer _procedural;
+ mutable Procedural _procedural;
};
typedef std::shared_ptr< ProceduralSkybox > ProceduralSkyboxPointer;
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slv b/libraries/procedural/src/procedural/ProceduralSkybox.slv
deleted file mode 100644
index 810afb1033..0000000000
--- a/libraries/procedural/src/procedural/ProceduralSkybox.slv
+++ /dev/null
@@ -1,39 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-// Generated on <$_SCRIBE_DATE$>
-// skybox.vert
-// vertex shader
-//
-// Created by Sam Gateau on 5/5/2015.
-// 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 gpu/Transform.slh@>
-
-<$declareStandardTransform()$>
-
-out vec3 _normal;
-
-void main(void) {
- const float depth = 0.0;
- const vec4 UNIT_QUAD[4] = vec4[4](
- vec4(-1.0, -1.0, depth, 1.0),
- vec4(1.0, -1.0, depth, 1.0),
- vec4(-1.0, 1.0, depth, 1.0),
- vec4(1.0, 1.0, depth, 1.0)
- );
- vec4 inPosition = UNIT_QUAD[gl_VertexID];
-
- // standard transform
- TransformCamera cam = getTransformCamera();
- vec3 clipDir = vec3(inPosition.xy, 0.0);
- vec3 eyeDir;
- <$transformClipToEyeDir(cam, clipDir, eyeDir)$>
- <$transformEyeToWorldDir(cam, eyeDir, _normal)$>
-
- // Position is supposed to come in clip space
- gl_Position = vec4(inPosition.xy, 0.0, 1.0);
-}
\ No newline at end of file
diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp
index 0cdd736fee..79f4b2a269 100644
--- a/libraries/render/src/render/Scene.cpp
+++ b/libraries/render/src/render/Scene.cpp
@@ -48,6 +48,10 @@ ItemID Scene::allocateID() {
return _IDAllocator.fetch_add(1);
}
+bool Scene::isAllocatedID(const ItemID& id) {
+ return Item::isValidID(id) && (id < _numAllocatedItems.load());
+}
+
/// Enqueue change batch to the scene
void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) {
_changeQueueMutex.lock();
@@ -79,10 +83,22 @@ void Scene::processPendingChangesQueue() {
}
// Now we know for sure that we have enough items in the array to
// capture anything coming from the pendingChanges
+
+ // resets and potential NEW items
resetItems(consolidatedPendingChanges._resetItems, consolidatedPendingChanges._resetPayloads);
+
+ // Update the numItemsAtomic counter AFTER the reset changes went through
+ _numAllocatedItems.exchange(maxID);
+
+ // updates
updateItems(consolidatedPendingChanges._updatedItems, consolidatedPendingChanges._updateFunctors);
+
+ // removes
removeItems(consolidatedPendingChanges._removedItems);
+ // Update the numItemsAtomic counter AFTER the pending changes went through
+ _numAllocatedItems.exchange(maxID);
+
// ready to go back to rendering activities
_itemsMutex.unlock();
}
diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 40c0baca60..b7cfc367b8 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -60,18 +60,24 @@ public:
// This call is thread safe, can be called from anywhere to allocate a new ID
ItemID allocateID();
+ // Check that the ID is valid and allocated for this scene, this a threadsafe call
+ bool isAllocatedID(const ItemID& id);
+
+ // THis is the total number of allocated items, this a threadsafe call
+ size_t getNumItems() const { return _numAllocatedItems.load(); }
+
// Enqueue change batch to the scene
void enqueuePendingChanges(const PendingChanges& pendingChanges);
// Process the penging changes equeued
void processPendingChangesQueue();
+ // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues
+
// Access a particular item form its ID
// WARNING, There is No check on the validity of the ID, so this could return a bad Item
const Item& getItem(const ItemID& id) const { return _items[id]; }
- size_t getNumItems() const { return _items.size(); }
-
// Access the spatialized items
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
@@ -81,6 +87,7 @@ public:
protected:
// Thread safe elements that can be accessed from anywhere
std::atomic _IDAllocator{ 1 }; // first valid itemID will be One
+ std::atomic _numAllocatedItems{ 1 }; // num of allocated items, matching the _items.size()
std::mutex _changeQueueMutex;
PendingChangesQueue _changeQueue;
diff --git a/libraries/render/src/render/SpatialTree.cpp b/libraries/render/src/render/SpatialTree.cpp
index b09b6f4778..1bb3538521 100644
--- a/libraries/render/src/render/SpatialTree.cpp
+++ b/libraries/render/src/render/SpatialTree.cpp
@@ -264,12 +264,33 @@ Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& acc
return brickID;
}
+Octree::Location ItemSpatialTree::evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const {
+ minCoordf = evalCoordf(bound.getMinimumPoint());
+ maxCoordf = evalCoordf(bound.getMaximumPoint());
+
+ // If the bound crosses any of the octree volume limit, then return root cell
+ if ( (minCoordf.x < 0.0f)
+ || (minCoordf.y < 0.0f)
+ || (minCoordf.z < 0.0f)
+ || (maxCoordf.x >= _size)
+ || (maxCoordf.y >= _size)
+ || (maxCoordf.z >= _size)) {
+ return Location();
+ }
+
+ Coord3 minCoord(minCoordf);
+ Coord3 maxCoord(maxCoordf);
+ return Location::evalFromRange(minCoord, maxCoord);
+}
+
Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const {
Locations locations;
+ Coord3f minCoordf, maxCoordf;
+
locations.reserve(bounds.size());
for (auto& bound : bounds) {
if (!bound.bound.isNull()) {
- locations.emplace_back(evalLocation(bound.bound));
+ locations.emplace_back(evalLocation(bound.bound, minCoordf, maxCoordf));
} else {
locations.emplace_back(Location());
}
@@ -344,11 +365,8 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID
ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) {
auto newCell = INVALID_CELL;
if (!newKey.isViewSpace()) {
- auto minCoordf = evalCoordf(bound.getMinimumPoint());
- auto maxCoordf = evalCoordf(bound.getMaximumPoint());
- Coord3 minCoord(minCoordf);
- Coord3 maxCoord(maxCoordf);
- auto location = Location::evalFromRange(minCoord, maxCoord);
+ Coord3f minCoordf, maxCoordf;
+ auto location = evalLocation(bound, minCoordf, maxCoordf);
// Compare range size vs cell location size and tag itemKey accordingly
// If Item bound fits in sub cell then tag as small
@@ -403,7 +421,21 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey&
int Octree::select(CellSelection& selection, const FrustumSelector& selector) const {
Index cellID = ROOT_CELL;
- return selectTraverse(cellID, selection, selector);
+ auto cell = getConcreteCell(cellID);
+ int numSelectedsIn = (int)selection.size();
+
+ // Always include the root cell partially containing potentially outer objects
+ selectCellBrick(cellID, selection, false);
+
+ // then traverse deeper
+ for (int i = 0; i < NUM_OCTANTS; i++) {
+ Index subCellID = cell.child((Link)i);
+ if (subCellID != INVALID_CELL) {
+ selectTraverse(subCellID, selection, selector);
+ }
+ }
+
+ return (int)selection.size() - numSelectedsIn;
}
diff --git a/libraries/render/src/render/SpatialTree.h b/libraries/render/src/render/SpatialTree.h
index a5dbb29544..a89b9847e6 100644
--- a/libraries/render/src/render/SpatialTree.h
+++ b/libraries/render/src/render/SpatialTree.h
@@ -117,7 +117,6 @@ namespace render {
return depth;
}
-
class Location {
void assertValid() {
assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0));
@@ -157,6 +156,7 @@ namespace render {
// Eval the location best fitting the specified range
static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH);
+
// Eval the intersection test against a frustum
enum Intersection {
Outside = 0,
@@ -367,7 +367,7 @@ namespace render {
// An octree of Items organizing them efficiently for culling
// The octree only cares about the bound & the key of an item to store it a the right cell location
class ItemSpatialTree : public Octree {
- float _size { 32768.0f };
+ float _size{ 32768.0f };
float _invSize { 1.0f / _size };
glm::vec3 _origin { -16384.0f };
@@ -398,10 +398,26 @@ namespace render {
return getOrigin() + glm::vec3(coord) * cellWidth;
}
+
+ // Clamp a 3D relative position to make sure it is in the valid range space of the octree
+ glm::vec3 clampRelPosToTreeRange(const glm::vec3& pos) const {
+ const float EPSILON = 0.0001f;
+ return glm::vec3(
+ std::min(std::max(pos.x, 0.0f), _size - EPSILON),
+ std::min(std::max(pos.y, 0.0f), _size - EPSILON),
+ std::min(std::max(pos.z, 0.0f), _size - EPSILON));
+ }
+
+ // Eval an integer cell coordinate (at the specified deepth) from a given 3d position
+ // If the 3D position is out of the octree volume, then the position is clamped
+ // so the integer coordinate is meaningfull
Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
- auto npos = (pos - getOrigin());
+ auto npos = clampRelPosToTreeRange((pos - getOrigin()));
return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part
}
+
+ // Eval a real cell coordinate (at the specified deepth) from a given 3d position
+ // Position is NOT clamped to the boundaries of the octree so beware of conversion to a Coord3!
Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
auto npos = (pos - getOrigin());
return Coord3f(npos * getInvCellWidth(depth));
@@ -412,9 +428,10 @@ namespace render {
float cellWidth = getCellWidth(loc.depth);
return AABox(evalPos(loc.pos, cellWidth), cellWidth);
}
- Location evalLocation(const AABox& bound) const {
- return Location::evalFromRange(evalCoord(bound.getMinimumPoint()), evalCoord(bound.getMaximumPoint()));
- }
+
+ // Eval the cell location for a given arbitrary Bound,
+ // if the Bound crosses any of the Octree planes then the root cell is returned
+ Location evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const;
Locations evalLocations(const ItemBounds& bounds) const;
// Managing itemsInserting items in cells
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index e5708572f6..b86a7f9ebc 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -68,6 +68,21 @@ void OpenVrDisplayPlugin::activate() {
_compositor = vr::VRCompositor();
Q_ASSERT(_compositor);
HmdDisplayPlugin::activate();
+
+ // set up default sensor space such that the UI overlay will align with the front of the room.
+ auto chaperone = vr::VRChaperone();
+ if (chaperone) {
+ float const UI_RADIUS = 1.0f;
+ float const UI_HEIGHT = 1.6f;
+ float const UI_Z_OFFSET = 0.5;
+
+ float xSize, zSize;
+ chaperone->GetPlayAreaSize(&xSize, &zSize);
+ glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET);
+ _sensorResetMat = glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos));
+ } else {
+ qDebug() << "OpenVR: error could not get chaperone pointer";
+ }
}
void OpenVrDisplayPlugin::deactivate() {
@@ -115,7 +130,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
#endif
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
- _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
+ _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
// copy and process predictedTrackedDevicePoses
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp
index ddc7ad6540..2bc957b2d1 100644
--- a/tests/shaders/src/main.cpp
+++ b/tests/shaders/src/main.cpp
@@ -23,83 +23,77 @@
#include
#include
-#include "../model/Skybox_vert.h"
-#include "../model/Skybox_frag.h"
+#include
+#include
+#include
+#include
-#include "simple_vert.h"
-#include "simple_frag.h"
-#include "simple_textured_frag.h"
-#include "simple_textured_emisive_frag.h"
+#include
+#include
-#include "deferred_light_vert.h"
-#include "deferred_light_limited_vert.h"
+#include
+#include
+#include
-#include "directional_light_frag.h"
+#include
+#include
-#include "directional_ambient_light_frag.h"
+#include
+#include
-#include "directional_skybox_light_frag.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
-#include "point_light_frag.h"
-#include "spot_light_frag.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
-#include "standardTransformPNTC_vert.h"
-#include "standardDrawTexture_frag.h"
+#include
+#include
+#include
+#include
-#include "model_vert.h"
-#include "model_shadow_vert.h"
-#include "model_normal_map_vert.h"
-#include "model_lightmap_vert.h"
-#include "model_lightmap_normal_map_vert.h"
-#include "skin_model_vert.h"
-#include "skin_model_shadow_vert.h"
-#include "skin_model_normal_map_vert.h"
+#include
+#include
-#include "model_frag.h"
-#include "model_shadow_frag.h"
-#include "model_normal_map_frag.h"
-#include "model_normal_specular_map_frag.h"
-#include "model_specular_map_frag.h"
-#include "model_lightmap_frag.h"
-#include "model_lightmap_normal_map_frag.h"
-#include "model_lightmap_normal_specular_map_frag.h"
-#include "model_lightmap_specular_map_frag.h"
-#include "model_translucent_frag.h"
+#include
+#include
-#include "untextured_particle_frag.h"
-#include "untextured_particle_vert.h"
-#include "textured_particle_frag.h"
-#include "textured_particle_vert.h"
+#include
+#include
+#include
+#include
+#include
-#include "hit_effect_vert.h"
-#include "hit_effect_frag.h"
+#include
+#include
+#include
+#include
+#include
+#include
-#include "overlay3D_vert.h"
-#include "overlay3D_frag.h"
+#include
+#include
-#include "Skybox_vert.h"
-#include "Skybox_frag.h"
+#include
+#include
-#include "stars_vert.h"
-#include "stars_frag.h"
-#include "starsGrid_frag.h"
-
-#include "DrawTransformUnitQuad_vert.h"
-#include "DrawTexcoordRectTransformUnitQuad_vert.h"
-#include "DrawViewportQuadTransformTexcoord_vert.h"
-#include "DrawTexture_frag.h"
-#include "DrawTextureOpaque_frag.h"
-#include "DrawColoredTexture_frag.h"
-
-#include "sdf_text3D_vert.h"
-#include "sdf_text3D_frag.h"
-
-#include "paintStroke_vert.h"
-#include "paintStroke_frag.h"
-
-#include "polyvox_vert.h"
-#include "polyvox_frag.h"
+#include
+#include
// Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow {
@@ -163,7 +157,7 @@ void QTestWindow::draw() {
testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag);
testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag);
- testShaderBuild(Skybox_vert, Skybox_frag);
+ testShaderBuild(skybox_vert, skybox_frag);
testShaderBuild(simple_vert, simple_frag);
testShaderBuild(simple_vert, simple_textured_frag);
testShaderBuild(simple_vert, simple_textured_emisive_frag);
@@ -209,8 +203,6 @@ void QTestWindow::draw() {
testShaderBuild(overlay3D_vert, overlay3D_frag);
- testShaderBuild(Skybox_vert, Skybox_frag);
-
testShaderBuild(paintStroke_vert,paintStroke_frag);
testShaderBuild(polyvox_vert, polyvox_frag);