diff --git a/examples/html/entityList.html b/examples/html/entityList.html
index 9506c78994..62bbbd08a6 100644
--- a/examples/html/entityList.html
+++ b/examples/html/entityList.html
@@ -35,8 +35,32 @@
function onRowClicked(e) {
var id = this.dataset.entityId;
var selection = [this.dataset.entityId];
- if (e.shiftKey) {
+ if (e.ctrlKey) {
selection = selection.concat(selectedEntities);
+ } else if (e.shiftKey && selectedEntities.length > 0) {
+ var previousItemFound = -1;
+ var clickedItemFound = -1;
+ for (var i in entityList.visibleItems) {
+ if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[i].values().id) {
+ clickedItemFound = i;
+ } else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[i].values().id) {
+ previousItemFound = i;
+ }
+ }
+ if (previousItemFound !== -1 && clickedItemFound !== -1) {
+ var betweenItems = [];
+ var toItem = Math.max(previousItemFound, clickedItemFound);
+ // skip first and last item in this loop, we add them to selection after the loop
+ for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) {
+ entityList.visibleItems[i].elm.className = 'selected';
+ betweenItems.push(entityList.visibleItems[i].values().id);
+ }
+ if (previousItemFound > clickedItemFound) {
+ // always make sure that we add the items in the right order
+ betweenItems.reverse();
+ }
+ selection = selection.concat(betweenItems, selectedEntities);
+ }
}
selectedEntities = selection;
@@ -151,6 +175,17 @@
refreshEntities();
}
+ document.addEventListener("keydown", function (e) {
+ if (e.target.nodeName === "INPUT") {
+ return;
+ }
+ var keyCode = e.keyCode;
+ if (keyCode === 46) {
+ EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
+ refreshEntities();
+ }
+ }, false);
+
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data);
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index b4a285a680..72109bf81a 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -921,6 +921,30 @@ void Application::paintGL() {
PerformanceWarning warn(showWarnings, "Application::paintGL()");
resizeGL();
+ // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
+ renderArgs._context->syncCache();
+
+ if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
+ auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor();
+
+ renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
+ renderRearViewMirror(&renderArgs, _mirrorViewRect);
+ renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
+
+ {
+ float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale());
+ auto mirrorViewport = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio);
+ auto mirrorViewportDest = mirrorViewport;
+
+ auto selfieFbo = DependencyManager::get()->getSelfieFramebuffer();
+ gpu::Batch batch;
+ batch.setFramebuffer(selfieFbo);
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
+ batch.blit(primaryFbo, mirrorViewport, selfieFbo, mirrorViewportDest);
+ batch.setFramebuffer(nullptr);
+ renderArgs._context->render(batch);
+ }
+ }
{
PerformanceTimer perfTimer("renderOverlay");
@@ -937,10 +961,14 @@ void Application::paintGL() {
Application::getInstance()->cameraMenuChanged();
}
+ // The render mode is default or mirror if the camera is in mirror mode, assigned further below
+ renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
+
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
// Always use the default eye position, not the actual head eye position.
// Using the latter will cause the camera to wobble with idle animations,
// or with changes from the face tracker
+ renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
_myCamera.setPosition(_myAvatar->getDefaultEyePosition());
if (!getActiveDisplayPlugin()->isHmd()) {
@@ -970,6 +998,7 @@ void Application::paintGL() {
glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) +
(_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
+ renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
}
// Update camera position
@@ -977,17 +1006,15 @@ void Application::paintGL() {
_myCamera.update(1.0f / _fps);
}
- // Sync up the View Furstum with the camera
- loadViewFrustum(_myCamera, _viewFrustum);
-
-
- renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
// Primary rendering pass
auto framebufferCache = DependencyManager::get();
QSize size = framebufferCache->getFrameBufferSize();
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
+ // Viewport is assigned to the size of the framebuffer
+ QSize size = DependencyManager::get()->getFrameBufferSize();
+ renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
{
PROFILE_RANGE(__FUNCTION__ "/clear");
@@ -1019,12 +1046,6 @@ void Application::paintGL() {
batch.setStateScissorRect(renderArgs._viewport);
});
displaySide(&renderArgs, eyeCamera);
- if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror) &&
- !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
-// renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
-// renderRearViewMirror(&renderArgs, _mirrorViewRect);
-// renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
- }
}, [&] {
r.moveLeft(r.width());
});
@@ -1038,13 +1059,11 @@ void Application::paintGL() {
batch.setStateScissorRect(renderArgs._viewport);
});
displaySide(&renderArgs, _myCamera);
- if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror) &&
- !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
- renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
- renderRearViewMirror(&renderArgs, _mirrorViewRect);
- renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
- }
}
+
+ doInBatch(&renderArgs, [](gpu::Batch& batch){
+ batch.setFramebuffer(nullptr);
+ });
}
// Overlay Composition, needs to occur after screen space effects have completed
@@ -1089,6 +1108,7 @@ void Application::paintGL() {
// Ensure all operations from the previous context are complete before we try to read the fbo
#ifdef Q_OS_MAC
+ // FIXME once we move to core profile, use fencesync on both platforms
#else
// FIXME? make the sync a parameter to preDisplay and let the plugin manage this
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
@@ -1104,11 +1124,11 @@ void Application::paintGL() {
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
displayPlugin->finishFrame();
}
-
- _offscreenContext->makeCurrent();
- _frameCount++;
- Stats::getInstance()->setRenderDetails(renderArgs._details);
}
+
+ _offscreenContext->makeCurrent();
+ _frameCount++;
+ Stats::getInstance()->setRenderDetails(renderArgs._details);
}
void Application::runTests() {
@@ -1151,6 +1171,7 @@ void Application::resizeGL() {
uvec2 framebufferSize = getActiveDisplayPlugin()->getRecommendedRenderSize();
uvec2 renderSize = uvec2(vec2(framebufferSize) * getRenderResolutionScale());
if (_renderResolution != renderSize) {
+ _numFramesSinceLastResize = 0;
_renderResolution = renderSize;
DependencyManager::get()->setFrameBufferSize(fromGlm(renderSize));
@@ -1933,13 +1954,6 @@ void Application::idle() {
}
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
if (timeSinceLastUpdate > targetFramePeriod) {
-
- {
- static const int IDLE_EVENT_PROCESS_MAX_TIME_MS = 2;
- PerformanceTimer perfTimer("processEvents");
- processEvents(QEventLoop::AllEvents, IDLE_EVENT_PROCESS_MAX_TIME_MS);
- }
-
_lastTimeUpdated.start();
{
PerformanceTimer perfTimer("update");
@@ -1978,7 +1992,7 @@ void Application::idle() {
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
// perpetuity and not expect events to get backed up.
- static const int IDLE_TIMER_DELAY_MS = 0;
+ static const int IDLE_TIMER_DELAY_MS = 2;
int desiredInterval = getActiveDisplayPlugin()->isThrottled() ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS;
if (idleTimer->interval() != desiredInterval) {
@@ -2353,23 +2367,21 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtSpot = _myAvatar->getHead()->getEyePosition() +
(_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
}
- }
- // Deflect the eyes a bit to match the detected gaze from Faceshift if active.
- // DDE doesn't track eyes.
- if (tracker && typeid(*tracker) == typeid(Faceshift) && !tracker->isMuted()) {
- float eyePitch = tracker->getEstimatedEyePitch();
- float eyeYaw = tracker->getEstimatedEyeYaw();
- const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
- glm::vec3 origin = _myAvatar->getHead()->getEyePosition();
- float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
- float deflection = DependencyManager::get()->getEyeDeflection();
- if (isLookingAtSomeone) {
- deflection *= GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT;
- }
- lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
- eyePitch * pitchSign * deflection, eyeYaw * deflection, 0.0f))) *
+ // Deflect the eyes a bit to match the detected gaze from the face tracker if active.
+ if (tracker && !tracker->isMuted()) {
+ float eyePitch = tracker->getEstimatedEyePitch();
+ float eyeYaw = tracker->getEstimatedEyeYaw();
+ const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
+ glm::vec3 origin = _myAvatar->getHead()->getEyePosition();
+ float deflection = tracker->getEyeDeflection();
+ if (isLookingAtSomeone) {
+ deflection *= GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT;
+ }
+ lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
+ eyePitch * deflection, eyeYaw * deflection, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
+ }
}
_myAvatar->getHead()->setLookAtPosition(lookAtSpot);
@@ -2546,10 +2558,18 @@ void Application::update(float deltaTime) {
_myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN));
_myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT));
_myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT));
- _myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP));
- _myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN));
- _myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT));
- _myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT));
+ if (deltaTime > FLT_EPSILON) {
+ // For rotations what we really want are meausures of "angles per second" (in order to prevent
+ // fps-dependent spin rates) so we need to scale the units of the controller contribution.
+ // (TODO?: maybe we should similarly scale ALL action state info, or change the expected behavior
+ // controllers to provide a delta_per_second value rather than a raw delta.)
+ const float EXPECTED_FRAME_RATE = 60.0f;
+ float timeFactor = EXPECTED_FRAME_RATE * deltaTime;
+ _myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor);
+ _myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor);
+ _myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor);
+ _myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor);
+ }
}
_myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN));
_myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT));
@@ -3190,37 +3210,25 @@ PickRay Application::computePickRay(float x, float y) const {
if (isHMDMode()) {
getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction);
} else {
- if (QThread::currentThread() == activeRenderingThread) {
- getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction);
- } else {
- getViewFrustum()->computePickRay(x, y, result.origin, result.direction);
- }
+ getViewFrustum()->computePickRay(x, y, result.origin, result.direction);
}
return result;
}
QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) {
- auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer();
- glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer));
-
- // clear the alpha channel so the background is transparent
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
- glClearColor(0.0, 0.0, 0.0, 0.0);
- glClear(GL_COLOR_BUFFER_BIT);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
const int BILLBOARD_SIZE = 64;
-#if 0
- renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE,
- BILLBOARD_SIZE, BILLBOARD_SIZE),
- true);
-#endif
+ // Need to make sure the gl context is current here
+ _offscreenContext->makeCurrent();
+
+ renderArgs->_renderMode = RenderArgs::DEFAULT_RENDER_MODE;
+ renderRearViewMirror(renderArgs, QRect(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), true);
+
+ auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor();
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
- glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ renderArgs->_context->downloadFramebuffer(primaryFbo, glm::ivec4(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), image);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
return image;
}
@@ -3595,16 +3603,20 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
gpu::Vec4i viewport;
if (billboard) {
QSize size = DependencyManager::get()->getFrameBufferSize();
- viewport = gpu::Vec4i(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
+ viewport = gpu::Vec4i(0, 0, region.width(), region.height());
} else {
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
QSize size = DependencyManager::get()->getFrameBufferSize();
float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
- int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
- viewport = gpu::Vec4i(x, size.height() - y - height, width, height);
+ int x = region.x() * ratio;
+ int y = region.y() * ratio;
+ int width = region.width() * ratio;
+ int height = region.height() * ratio;
+ viewport = gpu::Vec4i(0, 0, width, height);
}
renderArgs->_viewport = viewport;
+#if 0
auto origRenderMode = renderArgs->_renderMode;
renderArgs->_renderMode = RenderArgs::MIRROR_RENDER_MODE;
@@ -3623,14 +3635,12 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
bool updateViewFrustum = false;
loadViewFrustum(_mirrorCamera, _viewFrustum);
-
+#endif
+
// render rear mirror view
displaySide(renderArgs, _mirrorCamera, true, billboard);
renderArgs->_viewport = originalViewport;
-
- // restore renderMode
- renderArgs->_renderMode = origRenderMode;
}
void Application::resetSensors() {
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 10605d3e57..47cc8840c6 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -341,6 +341,8 @@ public:
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
+ const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
+
signals:
/// Fired when we're simulating; allows external parties to hook in.
@@ -657,6 +659,7 @@ private:
int _oldHandMouseY[2];
bool _oldHandLeftClick[2];
bool _oldHandRightClick[2];
+ int _numFramesSinceLastResize = 0;
};
#endif // hifi_Application_h
diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp
index 16b370d459..26ef621730 100644
--- a/interface/src/avatar/FaceModel.cpp
+++ b/interface/src/avatar/FaceModel.cpp
@@ -72,8 +72,8 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
- glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() +
- _owningHead->getSaccade() - model->getTranslation(), 1.0f));
+ glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
+ glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index d8b4bf703b..a9dc6c85b2 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -133,13 +133,14 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
const float AVERAGE_SACCADE_INTERVAL = 6.0f;
const float MICROSACCADE_MAGNITUDE = 0.002f;
const float SACCADE_MAGNITUDE = 0.04f;
+ const float NOMINAL_FRAME_RATE = 60.0f;
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
}
- _saccade += (_saccadeTarget - _saccade) * 0.5f;
+ _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime);
// Detect transition from talking to not; force blink after that and a delay
bool forceBlink = false;
@@ -352,7 +353,8 @@ glm::quat Head::getCameraOrientation() const {
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
glm::quat orientation = getOrientation();
- return rotationBetween(orientation * IDENTITY_FRONT, _lookAtPosition + _saccade - eyePosition) * orientation;
+ glm::vec3 lookAtDelta = _lookAtPosition - eyePosition;
+ return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
}
glm::vec3 Head::getScalePivot() const {
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 8d8e9a7996..07fb4cb6e3 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -53,7 +53,7 @@
using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
-const float YAW_SPEED = 500.0f; // degrees/sec
+const float YAW_SPEED = 150.0f; // degrees/sec
const float PITCH_SPEED = 100.0f; // degrees/sec
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f;
@@ -1345,21 +1345,34 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
void MyAvatar::updateOrientation(float deltaTime) {
// Smoothly rotate body with arrow keys
- _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
- _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime;
+ float driveLeft = _driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT];
+ float targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED;
+ if (targetSpeed != 0.0f) {
+ const float ROTATION_RAMP_TIMESCALE = 0.1f;
+ float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
+ if (blend > 1.0f) {
+ blend = 1.0f;
+ }
+ _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
+ } else if (_bodyYawDelta != 0.0f) {
+ // attenuate body rotation speed
+ const float ROTATION_DECAY_TIMESCALE = 0.05f;
+ float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
+ if (attenuation < 0.0f) {
+ attenuation = 0.0f;
+ }
+ _bodyYawDelta *= attenuation;
+
+ float MINIMUM_ROTATION_RATE = 2.0f;
+ if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
+ _bodyYawDelta = 0.0f;
+ }
+ }
+
getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);
-
- glm::quat twist = glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta, 0.0f) * deltaTime));
- setOrientation(twist * getOrientation());
-
- // decay body rotation momentum
- const float BODY_SPIN_FRICTION = 7.5f;
- float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime;
- if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
- _bodyYawDelta *= bodySpinMomentum;
-
- float MINIMUM_ROTATION_RATE = 2.0f;
- if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; }
+ // update body orientation by movement inputs
+ setOrientation(getOrientation() *
+ glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
if (qApp->isHMDMode()) {
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
@@ -1597,6 +1610,9 @@ void MyAvatar::maybeUpdateBillboard() {
QBuffer buffer(&_billboard);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
+#ifdef DEBUG
+ image.save("billboard.png", "PNG");
+#endif
_billboardValid = true;
sendBillboardPacket();
diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp
index 71039475a4..c9170bd413 100644
--- a/interface/src/devices/DdeFaceTracker.cpp
+++ b/interface/src/devices/DdeFaceTracker.cpp
@@ -157,6 +157,10 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_reset(false),
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
_rightBlinkIndex(1),
+ _leftEyeDownIndex(4),
+ _rightEyeDownIndex(5),
+ _leftEyeInIndex(6),
+ _rightEyeInIndex(7),
_leftEyeOpenIndex(8),
_rightEyeOpenIndex(9),
_browDownLeftIndex(14),
@@ -173,6 +177,14 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_filteredHeadTranslation(glm::vec3(0.0f)),
_lastBrowUp(0.0f),
_filteredBrowUp(0.0f),
+ _eyePitch(0.0f),
+ _eyeYaw(0.0f),
+ _lastEyePitch(0.0f),
+ _lastEyeYaw(0.0f),
+ _filteredEyePitch(0.0f),
+ _filteredEyeYaw(0.0f),
+ _longTermAverageEyePitch(0.0f),
+ _longTermAverageEyeYaw(0.0f),
_lastEyeBlinks(),
_filteredEyeBlinks(),
_lastEyeCoefficients(),
@@ -282,6 +294,17 @@ void DdeFaceTracker::reset() {
}
}
+void DdeFaceTracker::update(float deltaTime) {
+ if (!isActive()) {
+ return;
+ }
+ FaceTracker::update(deltaTime);
+
+ glm::vec3 headEulers = glm::degrees(glm::eulerAngles(_headRotation));
+ _estimatedEyePitch = _eyePitch - headEulers.x;
+ _estimatedEyeYaw = _eyeYaw - headEulers.y;
+}
+
bool DdeFaceTracker::isActive() const {
return (_ddeProcess != NULL);
}
@@ -436,6 +459,28 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
_coefficients[_mouthSmileLeftIndex] = _coefficients[_mouthSmileLeftIndex] - SMILE_THRESHOLD;
_coefficients[_mouthSmileRightIndex] = _coefficients[_mouthSmileRightIndex] - SMILE_THRESHOLD;
+ // Eye pitch and yaw
+ // EyeDown coefficients work better over both +ve and -ve values than EyeUp values.
+ // EyeIn coefficients work better over both +ve and -ve values than EyeOut values.
+ // Pitch and yaw values are relative to the screen.
+ const float EYE_PITCH_SCALE = -1500.0f; // Sign, scale, and average to be similar to Faceshift values.
+ _eyePitch = EYE_PITCH_SCALE * (_coefficients[_leftEyeDownIndex] + _coefficients[_rightEyeDownIndex]);
+ const float EYE_YAW_SCALE = 2000.0f; // Scale and average to be similar to Faceshift values.
+ _eyeYaw = EYE_YAW_SCALE * (_coefficients[_leftEyeInIndex] + _coefficients[_rightEyeInIndex]);
+ if (isFiltering) {
+ const float EYE_VELOCITY_FILTER_STRENGTH = 0.005f;
+ float pitchVelocity = fabsf(_eyePitch - _lastEyePitch) / _averageMessageTime;
+ float pitchVelocityFilter = glm::clamp(pitchVelocity * EYE_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
+ _filteredEyePitch = pitchVelocityFilter * _eyePitch + (1.0f - pitchVelocityFilter) * _filteredEyePitch;
+ _lastEyePitch = _eyePitch;
+ _eyePitch = _filteredEyePitch;
+ float yawVelocity = fabsf(_eyeYaw - _lastEyeYaw) / _averageMessageTime;
+ float yawVelocityFilter = glm::clamp(yawVelocity * EYE_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
+ _filteredEyeYaw = yawVelocityFilter * _eyeYaw + (1.0f - yawVelocityFilter) * _filteredEyeYaw;
+ _lastEyeYaw = _eyeYaw;
+ _eyeYaw = _filteredEyeYaw;
+ }
+
// Velocity filter EyeBlink values
const float DDE_EYEBLINK_SCALE = 3.0f;
float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex],
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index b3318d0cb0..5536fa14bd 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -31,6 +31,7 @@ class DdeFaceTracker : public FaceTracker, public Dependency {
public:
virtual void init();
virtual void reset();
+ virtual void update(float deltaTime);
virtual bool isActive() const;
virtual bool isTracking() const;
@@ -93,6 +94,11 @@ private:
int _leftEyeOpenIndex;
int _rightEyeOpenIndex;
+ int _leftEyeDownIndex;
+ int _rightEyeDownIndex;
+ int _leftEyeInIndex;
+ int _rightEyeInIndex;
+
int _browDownLeftIndex;
int _browDownRightIndex;
int _browUpCenterIndex;
@@ -115,6 +121,16 @@ private:
float _lastBrowUp;
float _filteredBrowUp;
+ float _eyePitch; // Degrees, relative to screen
+ float _eyeYaw;
+ float _lastEyePitch;
+ float _lastEyeYaw;
+ float _filteredEyePitch;
+ float _filteredEyeYaw;
+ float _longTermAverageEyePitch = 0.0f;
+ float _longTermAverageEyeYaw = 0.0f;
+ bool _longTermAverageInitialized = false;
+
enum EyeState {
EYE_UNCONTROLLED,
EYE_OPEN,
diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp
index 25a76ff2b1..76a4534952 100644
--- a/interface/src/devices/FaceTracker.cpp
+++ b/interface/src/devices/FaceTracker.cpp
@@ -20,6 +20,9 @@
const int FPS_TIMER_DELAY = 2000; // ms
const int FPS_TIMER_DURATION = 2000; // ms
+const float DEFAULT_EYE_DEFLECTION = 0.25f;
+Setting::Handle FaceTracker::_eyeDeflection("faceshiftEyeDeflection", DEFAULT_EYE_DEFLECTION);
+
void FaceTracker::init() {
_isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking);
_isInitialized = true; // FaceTracker can be used now
@@ -106,3 +109,7 @@ void FaceTracker::toggleMute() {
_isMuted = !_isMuted;
emit muteToggled();
}
+
+void FaceTracker::setEyeDeflection(float eyeDeflection) {
+ _eyeDeflection.set(eyeDeflection);
+}
diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h
index 2a0c4438a4..193262d121 100644
--- a/interface/src/devices/FaceTracker.h
+++ b/interface/src/devices/FaceTracker.h
@@ -18,6 +18,8 @@
#include
#include
+#include
+
/// Base class for face trackers (Faceshift, DDE).
class FaceTracker : public QObject {
Q_OBJECT
@@ -47,6 +49,9 @@ public:
void setIsMuted(bool isMuted) { _isMuted = isMuted; }
void toggleMute();
+ static float getEyeDeflection() { return _eyeDeflection.get(); }
+ static void setEyeDeflection(float eyeDeflection);
+
signals:
void muteToggled();
@@ -77,6 +82,8 @@ private slots:
private:
bool _isCalculatingFPS = false;
int _frameCount = 0;
+
+ static Setting::Handle _eyeDeflection;
};
#endif // hifi_FaceTracker_h
diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp
index 33124858c5..a25135cd76 100644
--- a/interface/src/devices/Faceshift.cpp
+++ b/interface/src/devices/Faceshift.cpp
@@ -28,10 +28,8 @@ using namespace std;
const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost";
const quint16 FACESHIFT_PORT = 33433;
-const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
Faceshift::Faceshift() :
- _eyeDeflection("faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION),
_hostname("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME)
{
#ifdef HAVE_FACESHIFT
@@ -306,10 +304,6 @@ void Faceshift::receive(const QByteArray& buffer) {
FaceTracker::countFrame();
}
-void Faceshift::setEyeDeflection(float faceshiftEyeDeflection) {
- _eyeDeflection.set(faceshiftEyeDeflection);
-}
-
void Faceshift::setHostname(const QString& hostname) {
_hostname.set(hostname);
}
diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h
index 9be1766170..4cb7557410 100644
--- a/interface/src/devices/Faceshift.h
+++ b/interface/src/devices/Faceshift.h
@@ -68,9 +68,6 @@ public:
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
- float getEyeDeflection() { return _eyeDeflection.get(); }
- void setEyeDeflection(float faceshiftEyeDeflection);
-
QString getHostname() { return _hostname.get(); }
void setHostname(const QString& hostname);
@@ -134,7 +131,6 @@ private:
float _longTermAverageEyeYaw = 0.0f;
bool _longTermAverageInitialized = false;
- Setting::Handle _eyeDeflection;
Setting::Handle _hostname;
// see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp
index 67e5e86280..3bd7e390ec 100644
--- a/interface/src/scripting/WebWindowClass.cpp
+++ b/interface/src/scripting/WebWindowClass.cpp
@@ -142,3 +142,7 @@ QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine*
return engine->newQObject(retVal);
}
+
+void WebWindowClass::setTitle(const QString& title) {
+ _windowWidget->setWindowTitle(title);
+}
diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h
index f5a292874c..c44898fa97 100644
--- a/interface/src/scripting/WebWindowClass.h
+++ b/interface/src/scripting/WebWindowClass.h
@@ -48,6 +48,7 @@ public slots:
void raise();
ScriptEventBridge* getEventBridge() const { return _eventBridge; }
void addEventBridgeToWindowObject();
+ void setTitle(const QString& title);
signals:
void closed();
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index a7f03db24f..bc10b555e4 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -93,6 +94,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Now render the overlay components together into a single texture
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
renderAudioScope(renderArgs); // audio scope in the very back
+ renderRearView(renderArgs); // renders the mirror view selfie
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
renderStatsAndLogs(renderArgs); // currently renders nothing
@@ -167,6 +169,39 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
}
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
+ if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
+ gpu::Batch& batch = *renderArgs->_batch;
+
+ auto geometryCache = DependencyManager::get();
+
+ auto framebuffer = DependencyManager::get();
+ auto selfieTexture = framebuffer->getSelfieFramebuffer()->getRenderBuffer(0);
+
+ int width = renderArgs->_viewport.z;
+ int height = renderArgs->_viewport.w;
+ mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
+ batch.setProjectionTransform(legacyProjection);
+ batch.setModelTransform(Transform());
+ batch.setViewTransform(Transform());
+
+ float screenRatio = ((float)qApp->getDevicePixelRatio());
+ float renderRatio = ((float)screenRatio * qApp->getRenderResolutionScale());
+
+ auto viewport = qApp->getMirrorViewRect();
+ glm::vec2 bottomLeft(viewport.left(), viewport.top() + viewport.height());
+ glm::vec2 topRight(viewport.left() + viewport.width(), viewport.top());
+ bottomLeft *= screenRatio;
+ topRight *= screenRatio;
+ glm::vec2 texCoordMinCorner(0.0f, 0.0f);
+ glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight()));
+
+ geometryCache->useSimpleDrawPipeline(batch, true);
+ batch.setResourceTexture(0, selfieTexture);
+ geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
+
+ batch.setResourceTexture(0, renderArgs->_whiteTexture);
+ geometryCache->useSimpleDrawPipeline(batch, false);
+ }
}
void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) {
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index a1c0a0a512..8e9c164563 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -142,10 +142,10 @@ void PreferencesDialog::loadPreferences() {
ui.ddeEyeClosingThresholdSlider->setValue(dde->getEyeClosingThreshold() *
ui.ddeEyeClosingThresholdSlider->maximum());
- auto faceshift = DependencyManager::get();
- ui.faceshiftEyeDeflectionSider->setValue(faceshift->getEyeDeflection() *
- ui.faceshiftEyeDeflectionSider->maximum());
+ ui.faceTrackerEyeDeflectionSider->setValue(FaceTracker::getEyeDeflection() *
+ ui.faceTrackerEyeDeflectionSider->maximum());
+ auto faceshift = DependencyManager::get();
ui.faceshiftHostnameEdit->setText(faceshift->getHostname());
auto audio = DependencyManager::get();
@@ -236,10 +236,10 @@ void PreferencesDialog::savePreferences() {
dde->setEyeClosingThreshold(ui.ddeEyeClosingThresholdSlider->value() /
(float)ui.ddeEyeClosingThresholdSlider->maximum());
- auto faceshift = DependencyManager::get();
- faceshift->setEyeDeflection(ui.faceshiftEyeDeflectionSider->value() /
- (float)ui.faceshiftEyeDeflectionSider->maximum());
+ FaceTracker::setEyeDeflection(ui.faceTrackerEyeDeflectionSider->value() /
+ (float)ui.faceTrackerEyeDeflectionSider->maximum());
+ auto faceshift = DependencyManager::get();
faceshift->setHostname(ui.faceshiftHostnameEdit->text());
qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui
index 78f9f5bf09..df6d28c07b 100644
--- a/interface/ui/preferencesDialog.ui
+++ b/interface/ui/preferencesDialog.ui
@@ -1468,13 +1468,13 @@
- Faceshift eye deflection
+ Face tracker eye deflection
0
- faceshiftEyeDeflectionSider
+ faceTrackerEyeDeflectionSider
@@ -1497,7 +1497,7 @@
-
-
+
0
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 3a3b895c66..5b970a95a3 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -1114,11 +1114,15 @@ void AvatarData::sendIdentityPacket() {
void AvatarData::sendBillboardPacket() {
if (!_billboard.isEmpty()) {
auto nodeList = DependencyManager::get();
-
- auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, _billboard.size());
- billboardPacket->write(_billboard);
-
- nodeList->broadcastToNodes(std::move(billboardPacket), NodeSet() << NodeType::AvatarMixer);
+
+ // This makes sure the billboard won't be too large to send.
+ // Once more protocol changes are done and we can send blocks of data we can support sending > MTU sized billboards.
+ if (_billboard.size() <= NLPacket::maxPayloadSize(PacketType::AvatarBillboard)) {
+ auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, _billboard.size());
+ billboardPacket->write(_billboard);
+
+ nodeList->broadcastToNodes(std::move(billboardPacket), NodeSet() << NodeType::AvatarMixer);
+ }
}
}
diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp
index 01c3c4ade7..567ce66cd8 100644
--- a/libraries/gpu/src/gpu/Batch.cpp
+++ b/libraries/gpu/src/gpu/Batch.cpp
@@ -220,10 +220,7 @@ void Batch::setStateBlendFactor(const Vec4& factor) {
void Batch::setStateScissorRect(const Vec4i& rect) {
ADD_COMMAND(setStateScissorRect);
- _params.push_back(rect.x);
- _params.push_back(rect.y);
- _params.push_back(rect.z);
- _params.push_back(rect.w);
+ _params.push_back(cacheData(sizeof(Vec4i), &rect));
}
void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) {
diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h
index 2f1d2e8ece..acc1f6fdac 100644
--- a/libraries/gpu/src/gpu/Batch.h
+++ b/libraries/gpu/src/gpu/Batch.h
@@ -108,7 +108,6 @@ public:
void blit(const FramebufferPointer& src, const Vec4i& srcViewport,
const FramebufferPointer& dst, const Vec4i& dstViewport);
-
// Query Section
void beginQuery(const QueryPointer& query);
void endQuery(const QueryPointer& query);
diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp
index 51335f78df..604f39f46f 100644
--- a/libraries/gpu/src/gpu/Context.cpp
+++ b/libraries/gpu/src/gpu/Context.cpp
@@ -40,4 +40,8 @@ void Context::render(Batch& batch) {
void Context::syncCache() {
PROFILE_RANGE(__FUNCTION__);
_backend->syncCache();
-}
\ No newline at end of file
+}
+
+void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) {
+ _backend->downloadFramebuffer(srcFramebuffer, region, destImage);
+}
diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h
index 484c772c71..ab7a1d1c11 100644
--- a/libraries/gpu/src/gpu/Context.h
+++ b/libraries/gpu/src/gpu/Context.h
@@ -20,6 +20,8 @@
#include "Pipeline.h"
#include "Framebuffer.h"
+class QImage;
+
namespace gpu {
class Backend {
@@ -28,6 +30,8 @@ public:
virtual~ Backend() {};
virtual void render(Batch& batch) = 0;
virtual void syncCache() = 0;
+ virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
+
class TransformObject {
public:
@@ -121,6 +125,10 @@ public:
void syncCache();
+ // Downloading the Framebuffer is a synchronous action that is not efficient.
+ // It s here for convenience to easily capture a snapshot
+ void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage);
+
protected:
Context(const Context& context);
diff --git a/libraries/gpu/src/gpu/DrawTextureOpaque.slf b/libraries/gpu/src/gpu/DrawTextureOpaque.slf
new file mode 100755
index 0000000000..1ce3a34c02
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawTextureOpaque.slf
@@ -0,0 +1,22 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw texture 0 fetched at texcoord.xy
+// Alpha is 1
+//
+// Created by Sam Gateau on 6/22/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 sampler2D colorMap;
+
+varying vec2 varTexcoord;
+
+void main(void) {
+ gl_FragColor = vec4(texture2D(colorMap, varTexcoord).xyz, 1.0);
+}
diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h
index 5a1504d25c..6f2b762bb0 100755
--- a/libraries/gpu/src/gpu/Framebuffer.h
+++ b/libraries/gpu/src/gpu/Framebuffer.h
@@ -14,8 +14,6 @@
#include "Texture.h"
#include
-class QImage;
-
namespace gpu {
typedef Element Format;
@@ -134,8 +132,6 @@ public:
static const uint32 MAX_NUM_RENDER_BUFFERS = 8;
static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; }
- void getImage(QImage* result) const;
-
protected:
SwapchainPointer _swapchain;
diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp
index 7fd0f9be76..ae50b96bc5 100644
--- a/libraries/gpu/src/gpu/GLBackend.cpp
+++ b/libraries/gpu/src/gpu/GLBackend.cpp
@@ -192,6 +192,7 @@ void GLBackend::syncCache() {
syncTransformStateCache();
syncPipelineStateCache();
syncInputStateCache();
+ syncOutputStateCache();
glEnable(GL_LINE_SMOOTH);
}
@@ -281,6 +282,9 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
glClearColor(color.x, color.y, color.z, color.w);
glmask |= GL_COLOR_BUFFER_BIT;
}
+
+ // Force the color mask cache to WRITE_ALL if not the case
+ do_setStateColorWriteMask(State::ColorMask::WRITE_ALL);
}
// Apply scissor if needed and if not already on
diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h
index 0e6f181028..894e2c4548 100644
--- a/libraries/gpu/src/gpu/GLBackend.h
+++ b/libraries/gpu/src/gpu/GLBackend.h
@@ -38,6 +38,10 @@ public:
// Let's try to avoid to do that as much as possible!
virtual void syncCache();
+ // This is the ugly "download the pixels to sysmem for taking a snapshot"
+ // Just avoid using it, it's ugly and will break performances
+ virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage);
+
static bool checkGLError(const char* name = nullptr);
// Only checks in debug builds
@@ -383,11 +387,14 @@ protected:
void do_setFramebuffer(Batch& batch, uint32 paramOffset);
void do_blit(Batch& batch, uint32 paramOffset);
-
+ // Synchronize the state cache of this Backend with the actual real state of the GL Context
+ void syncOutputStateCache();
+
struct OutputStageState {
FramebufferPointer _framebuffer = nullptr;
-
+ GLuint _drawFBO = 0;
+
OutputStageState() {}
} _output;
diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp
index 8876db95ac..1b22649ad6 100755
--- a/libraries/gpu/src/gpu/GLBackendOutput.cpp
+++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp
@@ -8,9 +8,12 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
+
#include "GPULogging.h"
#include "GLBackendShared.h"
+
using namespace gpu;
GLBackend::GLFramebuffer::GLFramebuffer() {}
@@ -34,6 +37,9 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
// need to have a gpu object?
if (!object) {
+ GLint currentFBO;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO);
+
GLuint fbo;
glGenFramebuffers(1, &fbo);
(void) CHECK_GL_ERROR();
@@ -84,6 +90,8 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
(void) CHECK_GL_ERROR();
}
+
+ // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
#endif
@@ -139,6 +147,9 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
object->_fbo = fbo;
object->_colorBuffers = colorBuffers;
Backend::setGPUObject(framebuffer, object);
+
+ // restore the current framebuffer
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO);
}
return object;
@@ -158,11 +169,24 @@ GLuint GLBackend::getFramebufferID(const FramebufferPointer& framebuffer) {
}
}
+void GLBackend::syncOutputStateCache() {
+ GLint currentFBO;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO);
+
+ _output._drawFBO = currentFBO;
+ _output._framebuffer.reset();
+}
+
+
void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) {
auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint);
if (_output._framebuffer != framebuffer) {
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(framebuffer));
+ auto newFBO = getFramebufferID(framebuffer);
+ if (_output._drawFBO != newFBO) {
+ _output._drawFBO = newFBO;
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFBO);
+ }
_output._framebuffer = framebuffer;
}
}
@@ -184,4 +208,38 @@ void GLBackend::do_blit(Batch& batch, uint32 paramOffset) {
glBlitFramebuffer(srcvp.x, srcvp.y, srcvp.z, srcvp.w,
dstvp.x, dstvp.y, dstvp.z, dstvp.w,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+ (void) CHECK_GL_ERROR();
+
+ if (_output._framebuffer) {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(_output._framebuffer));
+ }
}
+
+
+void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) {
+ auto readFBO = gpu::GLBackend::getFramebufferID(srcFramebuffer);
+ if (srcFramebuffer && readFBO) {
+ if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) {
+ qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried";
+ return;
+ }
+ }
+
+ if ((destImage.width() < region.z) || (destImage.height() < region.w)) {
+ qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer";
+ return;
+ }
+
+ GLenum format = GL_BGRA;
+ if (destImage.format() != QImage::Format_ARGB32) {
+ qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer";
+ return;
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(srcFramebuffer));
+ glReadPixels(region.x, region.y, region.z, region.w, format, GL_UNSIGNED_BYTE, destImage.bits());
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+
+ (void) CHECK_GL_ERROR();
+}
\ No newline at end of file
diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp
index f4f27a5ee9..18fc9ddd3c 100644
--- a/libraries/gpu/src/gpu/GLBackendState.cpp
+++ b/libraries/gpu/src/gpu/GLBackendState.cpp
@@ -757,11 +757,8 @@ void GLBackend::do_setStateBlendFactor(Batch& batch, uint32 paramOffset) {
}
void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) {
-
- Vec4 rect(batch._params[paramOffset + 0]._float,
- batch._params[paramOffset + 1]._float,
- batch._params[paramOffset + 2]._float,
- batch._params[paramOffset + 3]._float);
+ Vec4i rect;
+ memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
glScissor(rect.x, rect.y, rect.z, rect.w);
(void) CHECK_GL_ERROR();
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp
index 1c1cca458a..3f27a7fc35 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.cpp
+++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp
@@ -16,6 +16,7 @@
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
#include "DrawViewportQuadTransformTexcoord_vert.h"
#include "DrawTexture_frag.h"
+#include "DrawTextureOpaque_frag.h"
#include "DrawColoredTexture_frag.h"
using namespace gpu;
@@ -24,6 +25,7 @@ ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;
ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
ShaderPointer StandardShaderLib::_drawTexturePS;
+ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
ShaderPointer StandardShaderLib::_drawColoredTexturePS;
StandardShaderLib::ProgramMap StandardShaderLib::_programs;
@@ -82,6 +84,15 @@ ShaderPointer StandardShaderLib::getDrawTexturePS() {
return _drawTexturePS;
}
+ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() {
+ if (!_drawTextureOpaquePS) {
+ _drawTextureOpaquePS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(DrawTextureOpaque_frag)));
+ }
+ return _drawTextureOpaquePS;
+}
+
+
+
ShaderPointer StandardShaderLib::getDrawColoredTexturePS() {
if (!_drawColoredTexturePS) {
_drawColoredTexturePS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(DrawColoredTexture_frag)));
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h
index d5a3685feb..2d9c168473 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.h
+++ b/libraries/gpu/src/gpu/StandardShaderLib.h
@@ -35,6 +35,7 @@ public:
static ShaderPointer getDrawViewportQuadTransformTexcoordVS();
static ShaderPointer getDrawTexturePS();
+ static ShaderPointer getDrawTextureOpaquePS();
static ShaderPointer getDrawColoredTexturePS();
// The shader program combining the shaders available above, so they are unique
@@ -47,6 +48,7 @@ protected:
static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;
static ShaderPointer _drawViewportQuadTransformTexcoordVS;
static ShaderPointer _drawTexturePS;
+ static ShaderPointer _drawTextureOpaquePS;
static ShaderPointer _drawColoredTexturePS;
typedef std::map, ShaderPointer> ProgramMap;
diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp
index 1464afa45d..314492881f 100755
--- a/libraries/model/src/model/Skybox.cpp
+++ b/libraries/model/src/model/Skybox.cpp
@@ -110,7 +110,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
} else {
// skybox has no cubemap, just clear the color buffer
auto color = skybox.getColor();
- batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0);
+ batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0, true);
}
}
diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh
index a7f4055bba..a3edfbdbbb 100755
--- a/libraries/render-utils/src/DeferredBufferWrite.slh
+++ b/libraries/render-utils/src/DeferredBufferWrite.slh
@@ -21,6 +21,9 @@ float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold));
}
+const vec3 DEFAULT_SPECULAR = vec3(0.1);
+const float DEFAULT_SHININESS = 10;
+
void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
if (alpha != glowIntensity) {
discard;
diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp
index 7da314cdf2..c14bbfcb1d 100644
--- a/libraries/render-utils/src/DeferredLightingEffect.cpp
+++ b/libraries/render-utils/src/DeferredLightingEffect.cpp
@@ -142,6 +142,10 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured,
bool emmisive, bool depthBias) {
SimpleProgramKey config{textured, culled, emmisive, depthBias};
batch.setPipeline(getPipeline(config));
+
+ gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
+ int glowIntensity = program->getUniforms().findLocation("glowIntensity");
+ batch._glUniform1f(glowIntensity, 1.0f);
if (!config.isTextured()) {
// If it is not textured, bind white texture and keep using textured pipeline
@@ -216,13 +220,17 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
void DeferredLightingEffect::prepare(RenderArgs* args) {
gpu::Batch batch;
-
- // clear the normal and specular buffers
- batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
- const float MAX_SPECULAR_EXPONENT = 128.0f;
- batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT));
- args->_context->syncCache();
+ batch.setStateScissorRect(args->_viewport);
+
+ auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer();
+
+ batch.setFramebuffer(primaryFbo);
+ // clear the normal and specular buffers
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true);
+ const float MAX_SPECULAR_EXPONENT = 128.0f;
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true);
+
args->_context->render(batch);
}
@@ -241,8 +249,9 @@ void DeferredLightingEffect::render(RenderArgs* args) {
batch.setFramebuffer(_copyFBO);
batch.setViewportTransform(args->_viewport);
+ batch.setStateScissorRect(args->_viewport);
- batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
+ batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true);
batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture());
@@ -529,7 +538,6 @@ void DeferredLightingEffect::render(RenderArgs* args) {
batch.setResourceTexture(2, nullptr);
batch.setResourceTexture(3, nullptr);
- args->_context->syncCache();
args->_context->render(batch);
// End of the Lighting pass
@@ -542,7 +550,8 @@ void DeferredLightingEffect::copyBack(RenderArgs* args) {
QSize framebufferSize = framebufferCache->getFrameBufferSize();
// TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror.
- auto destFbo = framebufferCache->getPrimaryFramebuffer();
+ // auto destFbo = framebufferCache->getPrimaryFramebuffer();
+ auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor();
// gpu::Vec4i vp = args->_viewport;
// batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp);
batch.setFramebuffer(destFbo);
@@ -561,11 +570,6 @@ void DeferredLightingEffect::copyBack(RenderArgs* args) {
batch.setModelTransform(model);
}
- GLenum buffers[3];
- int bufferCount = 0;
- buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
- batch._glDrawBuffers(bufferCount, buffers);
-
batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp
index c7cb6451f7..601d99108d 100644
--- a/libraries/render-utils/src/FramebufferCache.cpp
+++ b/libraries/render-utils/src/FramebufferCache.cpp
@@ -34,17 +34,20 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
//If the size changed, we need to delete our FBOs
if (_frameBufferSize != frameBufferSize) {
_frameBufferSize = frameBufferSize;
- _primaryFramebuffer.reset();
+ _primaryFramebufferFull.reset();
+ _primaryFramebufferDepthColor.reset();
_primaryDepthTexture.reset();
_primaryColorTexture.reset();
_primaryNormalTexture.reset();
_primarySpecularTexture.reset();
+ _selfieFramebuffer.reset();
_cachedFramebuffers.clear();
}
}
void FramebufferCache::createPrimaryFramebuffer() {
- _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
+ _primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create());
+ _primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _frameBufferSize.width();
@@ -55,24 +58,37 @@ void FramebufferCache::createPrimaryFramebuffer() {
_primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
_primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
- _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture);
- _primaryFramebuffer->setRenderBuffer(1, _primaryNormalTexture);
- _primaryFramebuffer->setRenderBuffer(2, _primarySpecularTexture);
+ _primaryFramebufferFull->setRenderBuffer(0, _primaryColorTexture);
+ _primaryFramebufferFull->setRenderBuffer(1, _primaryNormalTexture);
+ _primaryFramebufferFull->setRenderBuffer(2, _primarySpecularTexture);
+ _primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture);
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));
- _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
+ _primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
+
+ _primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
+
+ _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
+ auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler));
+ _selfieFramebuffer->setRenderBuffer(0, tex);
}
gpu::FramebufferPointer FramebufferCache::getPrimaryFramebuffer() {
- if (!_primaryFramebuffer) {
+ if (!_primaryFramebufferFull) {
createPrimaryFramebuffer();
}
- return _primaryFramebuffer;
+ return _primaryFramebufferFull;
}
+gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() {
+ if (!_primaryFramebufferDepthColor) {
+ createPrimaryFramebuffer();
+ }
+ return _primaryFramebufferDepthColor;
+}
gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() {
@@ -112,7 +128,6 @@ gpu::FramebufferPointer FramebufferCache::getFramebuffer() {
return result;
}
-
void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebuffer) {
if (QSize(framebuffer->getSize().x, framebuffer->getSize().y) == _frameBufferSize) {
_cachedFramebuffers.push_back(framebuffer);
@@ -126,3 +141,10 @@ gpu::FramebufferPointer FramebufferCache::getShadowFramebuffer() {
}
return _shadowFramebuffer;
}
+
+gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() {
+ if (!_selfieFramebuffer) {
+ createPrimaryFramebuffer();
+ }
+ return _selfieFramebuffer;
+}
diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h
index ca01a470d9..c2274a77e8 100644
--- a/libraries/render-utils/src/FramebufferCache.h
+++ b/libraries/render-utils/src/FramebufferCache.h
@@ -30,6 +30,7 @@ public:
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
/// used for scene rendering.
gpu::FramebufferPointer getPrimaryFramebuffer();
+ gpu::FramebufferPointer getPrimaryFramebufferDepthColor();
gpu::TexturePointer getPrimaryDepthTexture();
gpu::TexturePointer getPrimaryColorTexture();
@@ -39,8 +40,12 @@ public:
/// Returns the framebuffer object used to render shadow maps;
gpu::FramebufferPointer getShadowFramebuffer();
+ /// Returns the framebuffer object used to render selfie maps;
+ gpu::FramebufferPointer getSelfieFramebuffer();
+
/// Returns a free framebuffer with a single color attachment for temp or intra-frame operations
gpu::FramebufferPointer getFramebuffer();
+
// TODO add sync functionality to the release, so we don't reuse a framebuffer being read from
/// Releases a free framebuffer back for reuse
void releaseFramebuffer(const gpu::FramebufferPointer& framebuffer);
@@ -51,13 +56,17 @@ private:
void createPrimaryFramebuffer();
- gpu::FramebufferPointer _primaryFramebuffer;
+ gpu::FramebufferPointer _primaryFramebufferFull;
+ gpu::FramebufferPointer _primaryFramebufferDepthColor;
gpu::TexturePointer _primaryDepthTexture;
gpu::TexturePointer _primaryColorTexture;
gpu::TexturePointer _primaryNormalTexture;
gpu::TexturePointer _primarySpecularTexture;
gpu::FramebufferPointer _shadowFramebuffer;
+
+ gpu::FramebufferPointer _selfieFramebuffer;
+
QSize _frameBufferSize{ 100, 100 };
};
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index c43cddff6e..8550f8d8b6 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -29,6 +29,8 @@
#include "standardTransformPNTC_vert.h"
#include "standardDrawTexture_frag.h"
+#include "gpu/StandardShaderLib.h"
+
//#define WANT_DEBUG
const int GeometryCache::UNKNOWN_ID = -1;
@@ -1651,7 +1653,7 @@ QSharedPointer GeometryCache::createResource(const QUrl& url, const QS
return geometry.staticCast();
}
-void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) {
+void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
if (!_standardDrawPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag)));
@@ -1660,12 +1662,24 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) {
auto state = std::make_shared();
+
// enable decal blend
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_standardDrawPipeline.reset(gpu::Pipeline::create(program, state));
+
+
+ auto stateNoBlend = std::make_shared();
+ auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
+ auto programNoBlend = gpu::ShaderPointer(gpu::Shader::createProgram(vs, noBlendPS));
+ gpu::Shader::makeProgram((*programNoBlend));
+ _standardDrawPipelineNoBlend.reset(gpu::Pipeline::create(programNoBlend, stateNoBlend));
+ }
+ if (noBlend) {
+ batch.setPipeline(_standardDrawPipelineNoBlend);
+ } else {
+ batch.setPipeline(_standardDrawPipeline);
}
- batch.setPipeline(_standardDrawPipeline);
}
const float NetworkGeometry::NO_HYSTERESIS = -1.0f;
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index 2f90c33adf..812d12b846 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -206,7 +206,7 @@ public:
QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
/// Set a batch to the simple pipeline, returning the previous pipeline
- void useSimpleDrawPipeline(gpu::Batch& batch);
+ void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false);
protected:
@@ -221,6 +221,7 @@ private:
typedef QPair VerticesIndices;
gpu::PipelinePointer _standardDrawPipeline;
+ gpu::PipelinePointer _standardDrawPipelineNoBlend;
QHash _cubeVerticies;
QHash _cubeColors;
gpu::BufferPointer _wireCubeIndexBuffer;
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 6a627bb6ab..0c8d19250b 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -17,6 +17,7 @@
#include
#include
+#include "FramebufferCache.h"
#include "DeferredLightingEffect.h"
#include "TextureCache.h"
@@ -27,6 +28,26 @@
using namespace render;
+void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
+ RenderArgs* args = renderContext->args;
+
+ auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor();
+
+ gpu::Batch batch;
+ batch.setFramebuffer(nullptr);
+ batch.setFramebuffer(primaryFbo);
+
+ batch.setViewportTransform(args->_viewport);
+ batch.setStateScissorRect(args->_viewport);
+
+ batch.clearFramebuffer(
+ gpu::Framebuffer::BUFFER_COLOR0 |
+ gpu::Framebuffer::BUFFER_DEPTH,
+ vec4(vec3(0), 1), 1.0, 0.0, true);
+
+ args->_context->render(batch);
+}
+
void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
DependencyManager::get()->prepare(renderContext->args);
}
@@ -41,6 +62,7 @@ void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderC
}
RenderDeferredTask::RenderDeferredTask() : Task() {
+ _jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer")));
_jobs.push_back(Job(new DrawBackground::JobModel("DrawBackground")));
_jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred")));
@@ -56,7 +78,6 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
auto& renderedOpaques = _jobs.back().getOutput();
_jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput())));
_jobs.push_back(Job(new DrawLight::JobModel("DrawLight")));
- _jobs.push_back(Job(new ResetGLState::JobModel()));
_jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred")));
_jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred")));
_jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent",
@@ -133,21 +154,12 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
batch.setViewTransform(viewMat);
{
- GLenum buffers[3];
- int bufferCount = 0;
- buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
- buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
- buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
- batch._glDrawBuffers(bufferCount, buffers);
const float OPAQUE_ALPHA_THRESHOLD = 0.5f;
args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD;
}
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOpaqueItems);
- // Before rendering the batch make sure we re in sync with gl state
- args->_context->syncCache();
- renderContext->args->_context->syncCache();
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
@@ -171,21 +183,15 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
- const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
{
- GLenum buffers[3];
- int bufferCount = 0;
- buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
- batch._glDrawBuffers(bufferCount, buffers);
+ const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD;
}
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnTransparentItems);
-
- // Before rendering the batch make sure we re in sync with gl state
- args->_context->syncCache();
+
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
@@ -239,17 +245,17 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
+ batch.setViewportTransform(args->_viewport);
+ batch.setStateScissorRect(args->_viewport);
batch.setPipeline(getOpaquePipeline());
batch.setResourceTexture(0, args->_whiteTexture);
if (!inItems.empty()) {
- batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
+ batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
}
- // Before rendering the batch make sure we re in sync with gl state
- args->_context->syncCache();
args->_context->render((*args->_batch));
args->_batch = nullptr;
args->_whiteTexture.reset();
diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h
index 4040606c62..1fec1c936f 100755
--- a/libraries/render-utils/src/RenderDeferredTask.h
+++ b/libraries/render-utils/src/RenderDeferredTask.h
@@ -16,6 +16,13 @@
#include "gpu/Pipeline.h"
+class SetupDeferred {
+public:
+ void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
+
+ typedef render::Job::Model JobModel;
+};
+
class PrepareDeferred {
public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index bbbb44cc51..84f1752c37 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -22,6 +22,5 @@ void main(void) {
normalize(interpolatedNormal.xyz),
glowIntensity,
gl_Color.rgb,
- gl_FrontMaterial.specular.rgb,
- gl_FrontMaterial.shininess);
+ DEFAULT_SPECULAR, DEFAULT_SHININESS);
}
diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf
index 7444d512e4..68d235050d 100644
--- a/libraries/render-utils/src/simple_textured.slf
+++ b/libraries/render-utils/src/simple_textured.slf
@@ -27,6 +27,5 @@ void main(void) {
normalize(interpolatedNormal.xyz),
glowIntensity * texel.a,
gl_Color.rgb * texel.rgb,
- gl_FrontMaterial.specular.rgb,
- gl_FrontMaterial.shininess);
+ DEFAULT_SPECULAR, DEFAULT_SHININESS);
}
\ No newline at end of file
diff --git a/libraries/render-utils/src/simple_textured_emisive.slf b/libraries/render-utils/src/simple_textured_emisive.slf
index 643dcde190..d2c7d403c3 100644
--- a/libraries/render-utils/src/simple_textured_emisive.slf
+++ b/libraries/render-utils/src/simple_textured_emisive.slf
@@ -27,7 +27,6 @@ void main(void) {
normalize(interpolatedNormal.xyz),
glowIntensity * texel.a,
gl_Color.rgb,
- gl_FrontMaterial.specular.rgb,
- gl_FrontMaterial.shininess,
+ DEFAULT_SPECULAR, DEFAULT_SHININESS,
texel.rgb);
}
\ No newline at end of file
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 7bb62a01ab..d5e727657c 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -46,13 +46,21 @@
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
- qCDebug(scriptengine) << "script:print()<<" << context->argument(0).toString();
- QString message = context->argument(0).toString()
- .replace("\\", "\\\\")
- .replace("\n", "\\n")
- .replace("\r", "\\r")
- .replace("'", "\\'");
+ QString message = "";
+ for (int i = 0; i < context->argumentCount(); i++) {
+ if (i > 0) {
+ message += " ";
+ }
+ message += context->argument(i).toString();
+ }
+ qCDebug(scriptengine) << "script:print()<<" << message;
+
+ message = message.replace("\\", "\\\\")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("'", "\\'");
engine->evaluate("Script.print('" + message + "')");
+
return QScriptValue();
}
diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp
index f1ff0e9e4f..4860785091 100644
--- a/tools/vhacd-util/src/VHACDUtil.cpp
+++ b/tools/vhacd-util/src/VHACDUtil.cpp
@@ -118,6 +118,13 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result,
glm::vec3 p2 = result.vertices[index2];
glm::vec3 av = (p0 + p1 + p2) / 3.0f; // center of the triangular face
+ glm::vec3 normal = glm::normalize(glm::cross(p1 - p0, p2 - p0));
+ float threshold = 1.0f / sqrtf(3.0f);
+ if (normal.y > -threshold && normal.y < threshold) {
+ // this triangle is more a wall than a floor, skip it.
+ continue;
+ }
+
float dropAmount = 0;
dropAmount = glm::max(glm::length(p1 - p0), dropAmount);
dropAmount = glm::max(glm::length(p2 - p1), dropAmount);