Merge branch 'master' into consoleversion

This commit is contained in:
James B. Pollack 2016-03-11 13:49:08 -08:00
commit e82fa70bb4
43 changed files with 660 additions and 279 deletions

View file

@ -1237,7 +1237,7 @@
<div class="section-header">
<label>Spacial Properties</label>
<label>Spatial Properties</label>
</div>
<div class="property">

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3757,19 +3757,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<BackgroundRenderData>() ... stars...");
"Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
// should be the first rendering pass - w/o depth buffer / lighting
static const float alpha = 1.0f;
@ -3777,6 +3777,7 @@ namespace render {
}
}
break;
case model::SunSkyStage::NO_BACKGROUND:
default:
// this line intentionally left blank

View file

@ -92,3 +92,8 @@ glm::quat HMDScriptingInterface::getOrientation() const {
}
return glm::quat();
}
bool HMDScriptingInterface::isMounted() const{
auto displayPlugin = qApp->getActiveDisplayPlugin();
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
}

View file

@ -25,6 +25,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation)
Q_PROPERTY(bool mounted READ isMounted)
public:
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
@ -39,6 +40,7 @@ public:
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
bool isMounted() const;
private:
// Get the position of the HMD

View file

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

View file

@ -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<QQuickItem> weakQmlElement;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
DependencyManager::get<OffscreenUi>()->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) {

View file

@ -11,6 +11,7 @@
#include <memory>
#include <math.h>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
@ -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() {

View file

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

View file

@ -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<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
auto compositorHelper = DependencyManager::get<CompositorHelper>();
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
Uniform<glm::mat4>(*_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<float>(*_program, _alphaUniform).Set(overlayAlpha);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
drawUnitQuad();
});
} else {
drawUnitQuad();
}
}
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositeLayers() {

View file

@ -29,24 +29,26 @@ protected:
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
public:
OpenGLDisplayPlugin();
virtual void activate() override;
virtual void deactivate() override;
virtual void stop() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
void activate() override;
void deactivate() override;
void stop() override;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
virtual float presentRate() override;
virtual glm::uvec2 getRecommendedRenderSize() const override {
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
float presentRate() override;
glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
}
virtual glm::uvec2 getRecommendedUiSize() const override {
glm::uvec2 getRecommendedUiSize() const override {
return getSurfaceSize();
}
virtual QImage getScreenshot() const override;
QImage getScreenshot() const override;
protected:
#if THREADED_PRESENT
@ -86,6 +88,7 @@ protected:
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
mutable Mutex _mutex;

View file

@ -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<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
auto compositorHelper = DependencyManager::get<CompositorHelper>();
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_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<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::compositePointer() {
//Mouse Pointer
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
_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<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_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<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::internalPresent() {

View file

@ -22,12 +22,16 @@ public:
glm::uvec2 getRecommendedUiSize() const override final;
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
bool isDisplayVisible() const override { return isHmdMounted(); }
void activate() override;
void deactivate() override;
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
void compositeOverlay() override;
void compositePointer() override;
void internalPresent() override;

View file

@ -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<EntityTree>(_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<float>::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_ptr<ZoneEntityIt
_ambientTexture.clear();
} else {
_ambientTexture = textureCache->getTexture(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;
}
}
@ -344,24 +352,27 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
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();
}
}

View file

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

View file

@ -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<NetworkTexture>();
return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast<NetworkTexture>();
}
/// Returns a texture version of an image file

View file

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

View file

@ -15,71 +15,68 @@
#include <gpu/Context.h>
#include <ViewFrustum.h>
#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<gpu::Buffer>(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<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
}
void Skybox::setColor(const Color& color) {
_dataBuffer.edit<Data>()._color = color;
_schemaBuffer.edit<Schema>().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<Data>()._blend) {
_dataBuffer.edit<Data>()._blend = blend;
if (blend != _schemaBuffer.get<Schema>().blend) {
_schemaBuffer.edit<Schema>().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);
}

View file

@ -30,30 +30,33 @@ public:
virtual ~Skybox() {};
void setColor(const Color& color);
const Color getColor() const { return _dataBuffer.get<Data>()._color; }
const Color getColor() const { return _schemaBuffer.get<Schema>().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<Skybox> SkyboxPointer;
};

View file

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

View file

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

View file

@ -65,6 +65,12 @@ public:
virtual bool isThrottled() const { return false; }
virtual float getTargetFrameRate() { return 0.0f; }
/// Returns a boolean value indicating whether the display is currently visible
/// to the user. For monitor displays, false might indicate that a screensaver,
/// or power-save mode is active. For HMDs it may reflect a sensor indicating
/// whether the HMD is being worn
virtual bool isDisplayVisible() const { return false; }
// Rendering support
// Stop requesting renders, but don't do full deactivation

View file

@ -101,6 +101,7 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) {
}
_shaderUrl = shaderUrl;
_shaderDirty = true;
if (_shaderUrl.isLocalFile()) {
_shaderPath = _shaderUrl.toLocalFile();
@ -230,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);

View file

@ -37,6 +37,7 @@ public:
bool ready();
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
const gpu::ShaderPointer& getShader() const { return _shader; }
glm::vec4 getColor(const glm::vec4& entityColor);

View file

@ -15,40 +15,39 @@
#include <gpu/Context.h>
#include <ViewFrustum.h>
#include "ProceduralSkybox_vert.h"
#include "ProceduralSkybox_frag.h"
#include <model/skybox_vert.h>
#include <model/skybox_frag.h>
ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
_procedural._vertexSource = ProceduralSkybox_vert;
_procedural._fragmentSource = ProceduralSkybox_frag;
_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.ready())) {
skybox.updateDataBuffer();
Skybox::render(batch, viewFrustum, skybox);
} else {
gpu::TexturePointer skymap = skybox.getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
assert(skymap && skymap->isDefined());
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac
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);
}

View file

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

View file

@ -96,6 +96,9 @@ void Scene::processPendingChangesQueue() {
// 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();
}

View file

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

View file

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

View file

@ -17,6 +17,7 @@ public:
protected:
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
private:
static const QString NAME;

View file

@ -24,6 +24,8 @@ public:
protected:
void hmdPresent() override;
// FIXME update with Oculus API call once it's available in the SDK
bool isHmdMounted() const override { return true; }
void customizeContext() override;
void uncustomizeContext() override;
void updateFrameData() override;

View file

@ -35,6 +35,7 @@ public:
protected:
virtual void customizeContext() override;
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
#if 0
virtual void uncustomizeContext() override;
virtual void internalPresent() override;

View file

@ -154,4 +154,10 @@ void OpenVrDisplayPlugin::hmdPresent() {
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
_hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
}
bool OpenVrDisplayPlugin::isHmdMounted() const {
return _hmdActivityLevel == vr::k_EDeviceActivityLevel_UserInteraction;
}

View file

@ -33,9 +33,11 @@ public:
protected:
void hmdPresent() override;
bool isHmdMounted() const override;
private:
vr::IVRSystem* _system { nullptr };
std::atomic<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
static const QString NAME;
mutable Mutex _poseMutex;
};

View file

@ -72,8 +72,8 @@
#include <render-utils/overlay3D_vert.h>
#include <render-utils/overlay3D_frag.h>
#include <model/Skybox_vert.h>
#include <model/Skybox_frag.h>
#include <model/skybox_vert.h>
#include <model/skybox_frag.h>
#include <render-utils/stars_vert.h>
#include <render-utils/stars_frag.h>
@ -157,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);
@ -203,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);