mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 14:47:41 +02:00
New stereo rendering implementation
This commit is contained in:
parent
f6f189c4b8
commit
76f236adf6
27 changed files with 452 additions and 210 deletions
|
@ -1058,29 +1058,18 @@ void Application::paintGL() {
|
||||||
// Using the latter will cause the camera to wobble with idle animations,
|
// Using the latter will cause the camera to wobble with idle animations,
|
||||||
// or with changes from the face tracker
|
// or with changes from the face tracker
|
||||||
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||||
|
_myCamera.setPosition(_myAvatar->getDefaultEyePosition());
|
||||||
if (!getActiveDisplayPlugin()->isHmd()) {
|
_myCamera.setRotation(_myAvatar->getOrientation());
|
||||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition());
|
|
||||||
_myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation());
|
|
||||||
} else {
|
|
||||||
mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix();
|
|
||||||
_myCamera.setPosition(extractTranslation(camMat));
|
|
||||||
_myCamera.setRotation(glm::quat_cast(camMat));
|
|
||||||
}
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
if (isHMDMode()) {
|
_myCamera.setRotation(_myAvatar->getOrientation());
|
||||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation());
|
|
||||||
} else {
|
|
||||||
_myCamera.setRotation(_myAvatar->getHead()->getOrientation());
|
|
||||||
}
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
|
||||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
|
||||||
_myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale());
|
|
||||||
} else {
|
|
||||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
|
||||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// https://www.youtube.com/watch?v=pFriRcIwqNU
|
||||||
|
vec3 boomStick = glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale();
|
||||||
|
quat boomRotation = _myAvatar->getOrientation();
|
||||||
|
if (!isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||||
|
boomRotation = _myCamera.getRotation();
|
||||||
|
}
|
||||||
|
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() + boomRotation * boomStick);
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
||||||
|
@ -1089,7 +1078,6 @@ void Application::paintGL() {
|
||||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update camera position
|
// Update camera position
|
||||||
if (!isHMDMode()) {
|
if (!isHMDMode()) {
|
||||||
_myCamera.update(1.0f / _fps);
|
_myCamera.update(1.0f / _fps);
|
||||||
|
@ -1105,57 +1093,37 @@ void Application::paintGL() {
|
||||||
QSize size = DependencyManager::get<FramebufferCache>()->getFrameBufferSize();
|
QSize size = DependencyManager::get<FramebufferCache>()->getFrameBufferSize();
|
||||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
||||||
|
|
||||||
{
|
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
||||||
PROFILE_RANGE(__FUNCTION__ "/clear");
|
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
batch.setFramebuffer(primaryFbo);
|
||||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
// clear the normal and specular buffers
|
||||||
batch.setFramebuffer(primaryFbo);
|
batch.clearFramebuffer(
|
||||||
// clear the normal and specular buffers
|
gpu::Framebuffer::BUFFER_COLOR0 |
|
||||||
batch.clearFramebuffer(
|
gpu::Framebuffer::BUFFER_COLOR1 |
|
||||||
gpu::Framebuffer::BUFFER_COLOR0 |
|
gpu::Framebuffer::BUFFER_COLOR2 |
|
||||||
gpu::Framebuffer::BUFFER_COLOR1 |
|
gpu::Framebuffer::BUFFER_DEPTH,
|
||||||
gpu::Framebuffer::BUFFER_COLOR2 |
|
vec4(vec3(0), 1), 1.0, 0.0);
|
||||||
gpu::Framebuffer::BUFFER_DEPTH,
|
});
|
||||||
vec4(vec3(0), 1), 1.0, 0.0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height());
|
||||||
if (displayPlugin->isStereo()) {
|
if (displayPlugin->isStereo()) {
|
||||||
PROFILE_RANGE(__FUNCTION__ "/stereoRender");
|
//_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection()));
|
||||||
QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height()));
|
renderArgs._context->enableStereo(true);
|
||||||
glEnable(GL_SCISSOR_TEST);
|
mat4 eyeViews[2];
|
||||||
for_each_eye([&](Eye eye){
|
mat4 eyeProjections[2];
|
||||||
// Load the view frustum, used by meshes
|
auto baseProjection = renderArgs._viewFrustum->getProjection();
|
||||||
Camera eyeCamera;
|
// FIXME we don't need to set these every frame,
|
||||||
if (qApp->isHMDMode()) {
|
// only when the display plugin changes
|
||||||
// Allow the displayPlugin to compose the final eye transform, based on the most up-to-date head motion.
|
for_each_eye([&](Eye eye) {
|
||||||
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myAvatar->getSensorToWorldMatrix()));
|
eyeViews[eye] = displayPlugin->getModelview(eye, mat4());
|
||||||
} else {
|
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
|
||||||
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myCamera.getTransform()));
|
|
||||||
}
|
|
||||||
eyeCamera.setProjection(displayPlugin->getProjection(eye, _myCamera.getProjection()));
|
|
||||||
renderArgs._viewport = toGlm(currentViewport);
|
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
|
||||||
batch.setViewportTransform(renderArgs._viewport);
|
|
||||||
batch.setStateScissorRect(renderArgs._viewport);
|
|
||||||
});
|
|
||||||
displaySide(&renderArgs, eyeCamera);
|
|
||||||
}, [&] {
|
|
||||||
currentViewport.moveLeft(currentViewport.width());
|
|
||||||
});
|
});
|
||||||
glDisable(GL_SCISSOR_TEST);
|
renderArgs._context->setStereoProjections(eyeProjections);
|
||||||
} else {
|
renderArgs._context->setStereoViews(eyeViews);
|
||||||
PROFILE_RANGE(__FUNCTION__ "/monoRender");
|
|
||||||
renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height());
|
|
||||||
// Viewport is assigned to the size of the framebuffer
|
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
|
||||||
batch.setViewportTransform(renderArgs._viewport);
|
|
||||||
batch.setStateScissorRect(renderArgs._viewport);
|
|
||||||
});
|
|
||||||
displaySide(&renderArgs, _myCamera);
|
|
||||||
}
|
}
|
||||||
|
displaySide(&renderArgs, _myCamera);
|
||||||
doInBatch(&renderArgs, [](gpu::Batch& batch){
|
renderArgs._context->enableStereo(false);
|
||||||
|
doInBatch(&renderArgs, [](gpu::Batch& batch) {
|
||||||
batch.setFramebuffer(nullptr);
|
batch.setFramebuffer(nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4997,6 +4965,11 @@ mat4 Application::getHMDSensorPose() const {
|
||||||
return mat4();
|
return mat4();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME there is a bug in the fullscreen setting, where leaving
|
||||||
|
// fullscreen does not restore the window frame, making it difficult
|
||||||
|
// or impossible to move or size the window.
|
||||||
|
// Additionally, setting fullscreen isn't hiding the menu on windows
|
||||||
|
// make it useless for stereoscopic modes.
|
||||||
void Application::setFullscreen(const QScreen* target) {
|
void Application::setFullscreen(const QScreen* target) {
|
||||||
if (!_window->isFullScreen()) {
|
if (!_window->isFullScreen()) {
|
||||||
_savedGeometry = _window->geometry();
|
_savedGeometry = _window->geometry();
|
||||||
|
|
|
@ -191,7 +191,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch& batch = *renderArgs->_batch;
|
||||||
batch.setViewTransform(Transform());
|
batch.setViewTransform(Transform());
|
||||||
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
|
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
|
||||||
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
|
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
|
||||||
|
@ -219,6 +219,4 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
batch.setInputBuffer(VERTICES_SLOT, posView);
|
batch.setInputBuffer(VERTICES_SLOT, posView);
|
||||||
batch.setInputBuffer(COLOR_SLOT, colView);
|
batch.setInputBuffer(COLOR_SLOT, colView);
|
||||||
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
||||||
|
|
||||||
renderArgs->_context->render(batch);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||||
//Handle fading and deactivation/activation of UI
|
//Handle fading and deactivation/activation of UI
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
geometryCache->useSimpleDrawPipeline(batch);
|
geometryCache->useSimpleDrawPipeline(batch);
|
||||||
|
@ -279,7 +278,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
vec2 canvasSize = qApp->getCanvasSize();
|
vec2 canvasSize = qApp->getCanvasSize();
|
||||||
_textureAspectRatio = aspect(canvasSize);
|
_textureAspectRatio = aspect(canvasSize);
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
|
@ -92,7 +92,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
||||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||||
renderStatsAndLogs(renderArgs); // currently renders nothing
|
renderStatsAndLogs(renderArgs); // currently renders nothing
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
renderArgs->_context->render(batch);
|
renderArgs->_context->render(batch);
|
||||||
|
|
||||||
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
|
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
|
||||||
|
|
|
@ -28,7 +28,7 @@ DisplayPluginList getDisplayPlugins() {
|
||||||
|
|
||||||
// Stereo modes
|
// Stereo modes
|
||||||
// FIXME fix stereo display plugins
|
// FIXME fix stereo display plugins
|
||||||
//new SideBySideStereoDisplayPlugin(),
|
new SideBySideStereoDisplayPlugin(),
|
||||||
//new InterleavedStereoDisplayPlugin(),
|
//new InterleavedStereoDisplayPlugin(),
|
||||||
|
|
||||||
// HMDs
|
// HMDs
|
||||||
|
|
|
@ -107,6 +107,10 @@ public:
|
||||||
static const glm::mat4 pose; return pose;
|
static const glm::mat4 pose; return pose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual glm::vec3 getEyeOffset(Eye eye) const {
|
||||||
|
static const glm::vec3 offset; return offset;
|
||||||
|
}
|
||||||
|
|
||||||
virtual glm::mat4 getHeadPose() const {
|
virtual glm::mat4 getHeadPose() const {
|
||||||
static const glm::mat4 pose; return pose;
|
static const glm::mat4 pose; return pose;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
using namespace Oculus;
|
using namespace Oculus;
|
||||||
|
|
||||||
|
OculusBaseDisplayPlugin::OculusBaseDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) {
|
||||||
|
}
|
||||||
|
|
||||||
void OculusBaseDisplayPlugin::activate() {
|
void OculusBaseDisplayPlugin::activate() {
|
||||||
glm::uvec2 eyeSizes[2];
|
glm::uvec2 eyeSizes[2];
|
||||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||||
|
@ -27,9 +30,12 @@ void OculusBaseDisplayPlugin::activate() {
|
||||||
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
||||||
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
// We handle the eye offsets slightly differently, using an _ipd in the base class
|
||||||
|
// _eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||||
|
_eyeOffsets[eye] = { 0, 0, 0 };
|
||||||
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
||||||
});
|
});
|
||||||
|
_ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
|
||||||
_desiredFramebufferSize = uvec2(
|
_desiredFramebufferSize = uvec2(
|
||||||
eyeSizes[0].x + eyeSizes[1].x,
|
eyeSizes[0].x + eyeSizes[1].x,
|
||||||
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
||||||
public:
|
public:
|
||||||
|
OculusBaseDisplayPlugin();
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual bool isHmd() const override { return true; }
|
virtual bool isHmd() const override { return true; }
|
||||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
|
@ -22,5 +23,6 @@ public:
|
||||||
virtual void resetSensors() override;
|
virtual void resetSensors() override;
|
||||||
virtual glm::mat4 getEyePose(Eye eye) const override;
|
virtual glm::mat4 getEyePose(Eye eye) const override;
|
||||||
virtual glm::mat4 getHeadPose() const override;
|
virtual glm::mat4 getHeadPose() const override;
|
||||||
|
protected:
|
||||||
|
float _ipd;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#include <gpu/GLBackend.h>
|
#include <gpu/GLBackend.h>
|
||||||
|
|
||||||
const QString SideBySideStereoDisplayPlugin::NAME("SBS Stereo Display");
|
const QString SideBySideStereoDisplayPlugin::NAME("Debug Stereo Display");
|
||||||
|
|
||||||
const QString & SideBySideStereoDisplayPlugin::getName() const {
|
const QString & SideBySideStereoDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
|
|
|
@ -52,6 +52,16 @@ glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelv
|
||||||
|
|
||||||
void StereoDisplayPlugin::activate() {
|
void StereoDisplayPlugin::activate() {
|
||||||
WindowOpenGLDisplayPlugin::activate();
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
CONTAINER->setFullscreen(qApp->primaryScreen());
|
// FIXME there is a bug in the fullscreen setting, see
|
||||||
|
// Application::setFullscreen
|
||||||
|
//CONTAINER->setFullscreen(qApp->primaryScreen());
|
||||||
// FIXME Add menu items
|
// FIXME Add menu items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 StereoDisplayPlugin::getEyeOffset(Eye eye) const {
|
||||||
|
glm::vec3 result(_ipd / 2.0f, 0, 0);
|
||||||
|
if (eye == Eye::Right) {
|
||||||
|
result *= -1.0f;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -20,5 +20,8 @@ public:
|
||||||
|
|
||||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
|
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
|
||||||
|
virtual glm::vec3 getEyeOffset(Eye eye) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float _ipd{ 0.064f };
|
||||||
};
|
};
|
||||||
|
|
|
@ -288,3 +288,18 @@ void Batch::resetStages() {
|
||||||
ADD_COMMAND(resetStages);
|
ADD_COMMAND(resetStages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Batch::enableStereo(bool enable) {
|
||||||
|
_enableStereo = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Batch::isStereoEnabled() const {
|
||||||
|
return _enableStereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Batch::enableSkybox(bool enable) {
|
||||||
|
_enableSkybox = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Batch::isSkyboxEnabled() const {
|
||||||
|
return _enableSkybox;
|
||||||
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
ProfileRange(const char *name);
|
ProfileRange(const char *name);
|
||||||
~ProfileRange();
|
~ProfileRange();
|
||||||
};
|
};
|
||||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||||
#else
|
#else
|
||||||
#define PROFILE_RANGE(name)
|
#define PROFILE_RANGE(name)
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +47,19 @@ public:
|
||||||
~Batch();
|
~Batch();
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
// Batches may need to override the context level stereo settings
|
||||||
|
// if they're performing framebuffer copy operations, like the
|
||||||
|
// deferred lighting resolution mechanism
|
||||||
|
void enableStereo(bool enable = true);
|
||||||
|
bool isStereoEnabled() const;
|
||||||
|
|
||||||
|
// Stereo batches will pre-translate the view matrix, but this isn't
|
||||||
|
// appropriate for skyboxes or other things intended to be drawn at
|
||||||
|
// infinite distance, so provide a mechanism to render in stereo
|
||||||
|
// without the pre-translation of the view.
|
||||||
|
void enableSkybox(bool enable = true);
|
||||||
|
bool isSkyboxEnabled() const;
|
||||||
|
|
||||||
// Drawcalls
|
// Drawcalls
|
||||||
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
|
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
|
||||||
|
@ -276,6 +289,9 @@ public:
|
||||||
FramebufferCaches _framebuffers;
|
FramebufferCaches _framebuffers;
|
||||||
QueryCaches _queries;
|
QueryCaches _queries;
|
||||||
|
|
||||||
|
bool _enableStereo{ true };
|
||||||
|
bool _enableSkybox{ false };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,18 @@ void Context::render(Batch& batch) {
|
||||||
_backend->render(batch);
|
_backend->render(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::enableStereo(bool enable) {
|
||||||
|
_backend->enableStereo(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::setStereoProjections(const mat4 eyeProjections[2]) {
|
||||||
|
_backend->setStereoProjections(eyeProjections);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::setStereoViews(const mat4 eyeViews[2]) {
|
||||||
|
_backend->setStereoViews(eyeViews);
|
||||||
|
}
|
||||||
|
|
||||||
void Context::syncCache() {
|
void Context::syncCache() {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
_backend->syncCache();
|
_backend->syncCache();
|
||||||
|
@ -49,3 +61,26 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons
|
||||||
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
|
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived() const {
|
||||||
|
_projectionInverse = glm::inverse(_projection);
|
||||||
|
_viewInverse = glm::inverse(_view);
|
||||||
|
|
||||||
|
Mat4 viewUntranslated = _view;
|
||||||
|
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
_projectionViewUntranslated = _projection * viewUntranslated;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo) const {
|
||||||
|
TransformCamera result = *this;
|
||||||
|
if (!_stereo._skybox) {
|
||||||
|
result._view = _stereo._eyeViews[eye] * result._view;
|
||||||
|
} else {
|
||||||
|
glm::mat4 skyboxView = _stereo._eyeViews[eye];
|
||||||
|
skyboxView[3] = vec4(0, 0, 0, 1);
|
||||||
|
result._view = skyboxView * result._view;
|
||||||
|
}
|
||||||
|
result._projection = _stereo._eyeProjections[eye];
|
||||||
|
result.recomputeDerived();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
#include "Batch.h"
|
#include "Batch.h"
|
||||||
|
|
||||||
#include "Resource.h"
|
#include "Resource.h"
|
||||||
|
@ -25,28 +27,58 @@ class QImage;
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
struct StereoState {
|
||||||
|
bool _enable{ false };
|
||||||
|
bool _skybox{ false };
|
||||||
|
// 0 for left eye, 1 for right eye
|
||||||
|
uint8_t _pass{ 0 };
|
||||||
|
mat4 _eyeViews[2];
|
||||||
|
mat4 _eyeProjections[2];
|
||||||
|
};
|
||||||
|
|
||||||
class Backend {
|
class Backend {
|
||||||
public:
|
public:
|
||||||
virtual~ Backend() {};
|
virtual~ Backend() {};
|
||||||
|
|
||||||
virtual void render(Batch& batch) = 0;
|
virtual void render(Batch& batch) = 0;
|
||||||
|
virtual void enableStereo(bool enable) {
|
||||||
|
_stereo._enable = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStereoProjections(const mat4 eyeProjections[2]) {
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_stereo._eyeProjections[i] = eyeProjections[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStereoViews(const mat4 views[2]) {
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_stereo._eyeViews[i] = views[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void syncCache() = 0;
|
virtual void syncCache() = 0;
|
||||||
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
||||||
|
|
||||||
|
// UBO class... layout MUST match the layout in TransformCamera.slh
|
||||||
class TransformObject {
|
class TransformObject {
|
||||||
public:
|
public:
|
||||||
Mat4 _model;
|
Mat4 _model;
|
||||||
Mat4 _modelInverse;
|
Mat4 _modelInverse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// UBO class... layout MUST match the layout in TransformCamera.slh
|
||||||
class TransformCamera {
|
class TransformCamera {
|
||||||
public:
|
public:
|
||||||
Mat4 _view;
|
Mat4 _view;
|
||||||
Mat4 _viewInverse;
|
mutable Mat4 _viewInverse;
|
||||||
Mat4 _projectionViewUntranslated;
|
mutable Mat4 _projectionViewUntranslated;
|
||||||
Mat4 _projection;
|
Mat4 _projection;
|
||||||
Mat4 _projectionInverse;
|
mutable Mat4 _projectionInverse;
|
||||||
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
||||||
|
|
||||||
|
const Backend::TransformCamera& recomputeDerived() const;
|
||||||
|
TransformCamera getEyeCamera(int eye, const StereoState& stereo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
|
@ -113,7 +145,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
StereoState _stereo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
|
@ -136,7 +168,9 @@ public:
|
||||||
~Context();
|
~Context();
|
||||||
|
|
||||||
void render(Batch& batch);
|
void render(Batch& batch);
|
||||||
|
void enableStereo(bool enable = true);
|
||||||
|
void setStereoProjections(const mat4 eyeProjections[2]);
|
||||||
|
void setStereoViews(const mat4 eyeViews[2]);
|
||||||
void syncCache();
|
void syncCache();
|
||||||
|
|
||||||
// Downloading the Framebuffer is a synchronous action that is not efficient.
|
// Downloading the Framebuffer is a synchronous action that is not efficient.
|
||||||
|
|
77
libraries/gpu/src/gpu/Forward.h
Normal file
77
libraries/gpu/src/gpu/Forward.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015/08/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_gpu_Forward_h
|
||||||
|
#define hifi_gpu_Forward_h
|
||||||
|
|
||||||
|
namespace gpu {
|
||||||
|
class Batch;
|
||||||
|
class Backend;
|
||||||
|
class Context;
|
||||||
|
typedef std::shared_ptr<Context> ContextPointer;
|
||||||
|
class GPUObject;
|
||||||
|
|
||||||
|
typedef int Stamp;
|
||||||
|
typedef uint32_t uint32;
|
||||||
|
typedef int32_t int32;
|
||||||
|
typedef uint16_t uint16;
|
||||||
|
typedef int16_t int16;
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef int8_t int8;
|
||||||
|
|
||||||
|
typedef uint8 Byte;
|
||||||
|
typedef uint32 Offset;
|
||||||
|
typedef std::vector<Offset> Offsets;
|
||||||
|
|
||||||
|
typedef glm::mat4 Mat4;
|
||||||
|
typedef glm::mat3 Mat3;
|
||||||
|
typedef glm::vec4 Vec4;
|
||||||
|
typedef glm::ivec4 Vec4i;
|
||||||
|
typedef glm::vec3 Vec3;
|
||||||
|
typedef glm::vec2 Vec2;
|
||||||
|
typedef glm::ivec2 Vec2i;
|
||||||
|
typedef glm::uvec2 Vec2u;
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
typedef Element Format;
|
||||||
|
class Swapchain;
|
||||||
|
typedef std::shared_ptr<Swapchain> SwapchainPointer;
|
||||||
|
class Framebuffer;
|
||||||
|
typedef std::shared_ptr<Framebuffer> FramebufferPointer;
|
||||||
|
class Pipeline;
|
||||||
|
typedef std::shared_ptr<Pipeline> PipelinePointer;
|
||||||
|
typedef std::vector<PipelinePointer> Pipelines;
|
||||||
|
class Query;
|
||||||
|
typedef std::shared_ptr<Query> QueryPointer;
|
||||||
|
typedef std::vector<QueryPointer> Queries;
|
||||||
|
class Resource;
|
||||||
|
class Buffer;
|
||||||
|
typedef std::shared_ptr<Buffer> BufferPointer;
|
||||||
|
typedef std::vector<BufferPointer> Buffers;
|
||||||
|
class BufferView;
|
||||||
|
class Shader;
|
||||||
|
typedef Shader::Pointer ShaderPointer;
|
||||||
|
typedef std::vector<ShaderPointer> Shaders;
|
||||||
|
class State;
|
||||||
|
typedef std::shared_ptr<State> StatePointer;
|
||||||
|
typedef std::vector<StatePointer> States;
|
||||||
|
class Stream;
|
||||||
|
class BufferStream;
|
||||||
|
typedef std::shared_ptr<BufferStream> BufferStreamPointer;
|
||||||
|
class Texture;
|
||||||
|
class SphericalHarmonics;
|
||||||
|
typedef std::shared_ptr<SphericalHarmonics> SHPointer;
|
||||||
|
class Sampler;
|
||||||
|
class Texture;
|
||||||
|
typedef std::shared_ptr<Texture> TexturePointer;
|
||||||
|
typedef std::vector<TexturePointer> Textures;
|
||||||
|
class TextureView;
|
||||||
|
typedef std::vector<TextureView> TextureViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -127,39 +127,29 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
||||||
const Batch::Commands::value_type* command = batch.getCommands().data();
|
const Batch::Commands::value_type* command = batch.getCommands().data();
|
||||||
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
||||||
|
|
||||||
_transform._cameraTransforms.resize(0);
|
// Reset the transform buffers
|
||||||
_transform._cameraTransforms.push_back(TransformCamera());
|
_transform._cameras.resize(0);
|
||||||
_transform._cameraOffsets.clear();
|
_transform._cameraOffsets.clear();
|
||||||
_transform._cameraOffsets.push_back(TransformStageState::Pair(0, 0));
|
_transform._objects.resize(0);
|
||||||
|
|
||||||
_transform._objectTransforms.push_back(TransformObject());
|
|
||||||
_transform._objectOffsets.push_back(TransformStageState::Pair(0, 0));
|
|
||||||
_transform._objectOffsets.clear();
|
_transform._objectOffsets.clear();
|
||||||
_transform._objectTransforms.resize(0);
|
|
||||||
|
|
||||||
_commandIndex = 0;
|
|
||||||
preUpdateTransform();
|
|
||||||
|
|
||||||
int drawCount = 0;
|
|
||||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||||
switch (*command) {
|
switch (*command) {
|
||||||
case Batch::COMMAND_draw:
|
case Batch::COMMAND_draw:
|
||||||
case Batch::COMMAND_drawIndexed:
|
case Batch::COMMAND_drawIndexed:
|
||||||
case Batch::COMMAND_drawInstanced:
|
case Batch::COMMAND_drawInstanced:
|
||||||
case Batch::COMMAND_drawIndexedInstanced:
|
case Batch::COMMAND_drawIndexedInstanced:
|
||||||
preUpdateTransform();
|
_transform.preUpdate(_commandIndex, _stereo);
|
||||||
++drawCount;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Batch::COMMAND_setModelTransform:
|
case Batch::COMMAND_setModelTransform:
|
||||||
case Batch::COMMAND_setViewportTransform:
|
case Batch::COMMAND_setViewportTransform:
|
||||||
case Batch::COMMAND_setViewTransform:
|
case Batch::COMMAND_setViewTransform:
|
||||||
case Batch::COMMAND_setProjectionTransform:
|
case Batch::COMMAND_setProjectionTransform: {
|
||||||
{
|
|
||||||
CommandCall call = _commandCalls[(*command)];
|
CommandCall call = _commandCalls[(*command)];
|
||||||
(this->*(call))(batch, *offset);
|
(this->*(call))(batch, *offset);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -167,44 +157,31 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
||||||
command++;
|
command++;
|
||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
|
_transform.transfer();
|
||||||
|
|
||||||
static QByteArray bufferData;
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer);
|
|
||||||
bufferData.resize(_transform._cameraUboSize * _transform._cameraTransforms.size());
|
|
||||||
for (size_t i = 0; i < _transform._cameraTransforms.size(); ++i) {
|
|
||||||
memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameraTransforms[i], sizeof(TransformCamera));
|
|
||||||
}
|
|
||||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer);
|
|
||||||
bufferData.resize(_transform._objectUboSize * _transform._objectTransforms.size());
|
|
||||||
for (size_t i = 0; i < _transform._objectTransforms.size(); ++i) {
|
|
||||||
memcpy(bufferData.data() + (_transform._objectUboSize * i), &_transform._objectTransforms[i], sizeof(TransformObject));
|
|
||||||
}
|
|
||||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
||||||
CHECK_GL_ERROR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::renderPassDraw(Batch& batch) {
|
void GLBackend::renderPassDraw(Batch& batch) {
|
||||||
|
_transform._objectsItr = _transform._objectOffsets.begin();
|
||||||
|
_transform._camerasItr = _transform._cameraOffsets.begin();
|
||||||
const size_t numCommands = batch.getCommands().size();
|
const size_t numCommands = batch.getCommands().size();
|
||||||
const Batch::Commands::value_type* command = batch.getCommands().data();
|
const Batch::Commands::value_type* command = batch.getCommands().data();
|
||||||
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
||||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||||
switch (*command) {
|
switch (*command) {
|
||||||
// Ignore these commands on this pass, taken care of in the transfer pass
|
// Ignore these commands on this pass, taken care of in the transfer pass
|
||||||
|
// Note we allow COMMAND_setViewportTransform to occur in both passes
|
||||||
|
// as it both updates the transform object (and thus the uniforms in the
|
||||||
|
// UBO) as well as executes the actual viewport call
|
||||||
case Batch::COMMAND_setModelTransform:
|
case Batch::COMMAND_setModelTransform:
|
||||||
case Batch::COMMAND_setViewportTransform:
|
|
||||||
case Batch::COMMAND_setViewTransform:
|
case Batch::COMMAND_setViewTransform:
|
||||||
case Batch::COMMAND_setProjectionTransform:
|
case Batch::COMMAND_setProjectionTransform:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
{
|
|
||||||
CommandCall call = _commandCalls[(*command)];
|
CommandCall call = _commandCalls[(*command)];
|
||||||
(this->*(call))(batch, *offset);
|
(this->*(call))(batch, *offset);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command++;
|
command++;
|
||||||
|
@ -213,8 +190,33 @@ void GLBackend::renderPassDraw(Batch& batch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::render(Batch& batch) {
|
void GLBackend::render(Batch& batch) {
|
||||||
renderPassTransfer(batch);
|
_stereo._skybox = batch.isSkyboxEnabled();
|
||||||
renderPassDraw(batch);
|
// Allow the batch to override the rendering stereo settings
|
||||||
|
// for things like full framebuffer copy operations (deferred lighting passes)
|
||||||
|
bool savedStereo = _stereo._enable;
|
||||||
|
if (!batch.isStereoEnabled()) {
|
||||||
|
_stereo._enable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE("Transfer");
|
||||||
|
renderPassTransfer(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(_stereo._enable ? "LeftRender" : "Render");
|
||||||
|
renderPassDraw(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stereo._enable) {
|
||||||
|
PROFILE_RANGE("RightRender");
|
||||||
|
_stereo._pass = 1;
|
||||||
|
renderPassDraw(batch);
|
||||||
|
_stereo._pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the saved stereo state for the next batch
|
||||||
|
_stereo._enable = savedStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLBackend::checkGLError(const char* name) {
|
bool GLBackend::checkGLError(const char* name) {
|
||||||
|
|
|
@ -310,19 +310,22 @@ protected:
|
||||||
void killTransform();
|
void killTransform();
|
||||||
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
||||||
void syncTransformStateCache();
|
void syncTransformStateCache();
|
||||||
void updateTransform();
|
void updateTransform() const;
|
||||||
void preUpdateTransform();
|
|
||||||
void resetTransformStage();
|
void resetTransformStage();
|
||||||
struct TransformStageState {
|
|
||||||
TransformObject _transformObject;
|
|
||||||
TransformCamera _transformCamera;
|
|
||||||
|
|
||||||
std::vector<TransformObject> _objectTransforms;
|
struct TransformStageState {
|
||||||
std::vector<TransformCamera> _cameraTransforms;
|
using TransformObjects = std::vector<TransformObject>;
|
||||||
|
using TransformCameras = std::vector<TransformCamera>;
|
||||||
|
|
||||||
|
TransformObject _object;
|
||||||
|
TransformCamera _camera;
|
||||||
|
TransformObjects _objects;
|
||||||
|
TransformCameras _cameras;
|
||||||
|
|
||||||
size_t _cameraUboSize{ 0 };
|
size_t _cameraUboSize{ 0 };
|
||||||
size_t _objectUboSize{ 0 };
|
size_t _objectUboSize{ 0 };
|
||||||
GLuint _transformObjectBuffer{ 0 };
|
GLuint _objectBuffer{ 0 };
|
||||||
GLuint _transformCameraBuffer{ 0 };
|
GLuint _cameraBuffer{ 0 };
|
||||||
Transform _model;
|
Transform _model;
|
||||||
Transform _view;
|
Transform _view;
|
||||||
Mat4 _projection;
|
Mat4 _projection;
|
||||||
|
@ -336,6 +339,12 @@ protected:
|
||||||
using List = std::list<Pair>;
|
using List = std::list<Pair>;
|
||||||
List _cameraOffsets;
|
List _cameraOffsets;
|
||||||
List _objectOffsets;
|
List _objectOffsets;
|
||||||
|
mutable List::const_iterator _objectsItr;
|
||||||
|
mutable List::const_iterator _camerasItr;
|
||||||
|
|
||||||
|
void preUpdate(size_t commandIndex, const StereoState& stereo);
|
||||||
|
void update(size_t commandIndex, const StereoState& stereo) const;
|
||||||
|
void transfer() const;
|
||||||
} _transform;
|
} _transform;
|
||||||
|
|
||||||
int32_t _uboAlignment{ 0 };
|
int32_t _uboAlignment{ 0 };
|
||||||
|
|
|
@ -198,6 +198,9 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
|
void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
|
||||||
|
if (_stereo._enable && !_pipeline._stateCache.scissorEnable) {
|
||||||
|
qWarning("Clear without scissor in stereo mode");
|
||||||
|
}
|
||||||
|
|
||||||
uint32 masks = batch._params[paramOffset + 7]._uint;
|
uint32 masks = batch._params[paramOffset + 7]._uint;
|
||||||
Vec4 color;
|
Vec4 color;
|
||||||
|
|
|
@ -768,6 +768,12 @@ void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) {
|
||||||
Vec4i rect;
|
Vec4i rect;
|
||||||
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
||||||
|
|
||||||
|
if (_stereo._enable) {
|
||||||
|
rect.z /= 2;
|
||||||
|
if (_stereo._pass) {
|
||||||
|
rect.x += rect.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
glScissor(rect.x, rect.y, rect.z, rect.w);
|
glScissor(rect.x, rect.y, rect.z, rect.w);
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,16 +33,26 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
|
||||||
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
|
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
|
||||||
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
||||||
|
|
||||||
|
ivec4& vp = _transform._viewport;
|
||||||
|
|
||||||
// Where we assign the GL viewport
|
// Where we assign the GL viewport
|
||||||
glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w);
|
if (_stereo._enable) {
|
||||||
|
vp.z /= 2;
|
||||||
|
if (_stereo._pass) {
|
||||||
|
vp.x += vp.z;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glViewport(vp.x, vp.y, vp.z, vp.w);
|
||||||
|
|
||||||
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall
|
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall
|
||||||
_transform._invalidViewport = true;
|
_transform._invalidViewport = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::initTransform() {
|
void GLBackend::initTransform() {
|
||||||
glGenBuffers(1, &_transform._transformObjectBuffer);
|
glGenBuffers(1, &_transform._objectBuffer);
|
||||||
glGenBuffers(1, &_transform._transformCameraBuffer);
|
glGenBuffers(1, &_transform._cameraBuffer);
|
||||||
size_t cameraSize = sizeof(TransformCamera);
|
size_t cameraSize = sizeof(TransformCamera);
|
||||||
while (_transform._cameraUboSize < cameraSize) {
|
while (_transform._cameraUboSize < cameraSize) {
|
||||||
_transform._cameraUboSize += _uboAlignment;
|
_transform._cameraUboSize += _uboAlignment;
|
||||||
|
@ -54,8 +64,8 @@ void GLBackend::initTransform() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::killTransform() {
|
void GLBackend::killTransform() {
|
||||||
glDeleteBuffers(1, &_transform._transformObjectBuffer);
|
glDeleteBuffers(1, &_transform._objectBuffer);
|
||||||
glDeleteBuffers(1, &_transform._transformCameraBuffer);
|
glDeleteBuffers(1, &_transform._cameraBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::syncTransformStateCache() {
|
void GLBackend::syncTransformStateCache() {
|
||||||
|
@ -72,73 +82,98 @@ void GLBackend::syncTransformStateCache() {
|
||||||
_transform._model.setIdentity();
|
_transform._model.setIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::preUpdateTransform() {
|
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) {
|
||||||
// Check all the dirty flags and update the state accordingly
|
// Check all the dirty flags and update the state accordingly
|
||||||
if (_transform._invalidViewport) {
|
if (_invalidViewport) {
|
||||||
_transform._transformCamera._viewport = glm::vec4(_transform._viewport);
|
_camera._viewport = glm::vec4(_viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidProj) {
|
if (_invalidProj) {
|
||||||
_transform._transformCamera._projection = _transform._projection;
|
_camera._projection = _projection;
|
||||||
_transform._transformCamera._projectionInverse = glm::inverse(_transform._projection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView) {
|
if (_invalidView) {
|
||||||
_transform._view.getInverseMatrix(_transform._transformCamera._view);
|
_view.getInverseMatrix(_camera._view);
|
||||||
_transform._view.getMatrix(_transform._transformCamera._viewInverse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidModel) {
|
if (_invalidModel) {
|
||||||
_transform._model.getMatrix(_transform._transformObject._model);
|
_model.getMatrix(_object._model);
|
||||||
_transform._model.getInverseMatrix(_transform._transformObject._modelInverse);
|
_model.getInverseMatrix(_object._modelInverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView || _transform._invalidProj) {
|
if (_invalidView || _invalidProj || _invalidViewport) {
|
||||||
Mat4 viewUntranslated = _transform._transformCamera._view;
|
size_t offset = _cameraUboSize * _cameras.size();
|
||||||
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
if (stereo._enable) {
|
||||||
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated;
|
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_cameras.push_back(_camera.getEyeCamera(i, stereo));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
|
_cameras.push_back(_camera.recomputeDerived());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) {
|
if (_invalidModel) {
|
||||||
_transform._cameraOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._cameraUboSize * _transform._cameraTransforms.size()));
|
size_t offset = _objectUboSize * _objects.size();
|
||||||
_transform._cameraTransforms.push_back(_transform._transformCamera);
|
_objectOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
}
|
_objects.push_back(_object);
|
||||||
|
|
||||||
if (_transform._invalidModel) {
|
|
||||||
_transform._objectOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._objectUboSize * _transform._objectTransforms.size()));
|
|
||||||
_transform._objectTransforms.push_back(_transform._transformObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags are clean
|
// Flags are clean
|
||||||
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false;
|
_invalidView = _invalidProj = _invalidModel = _invalidViewport = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::updateTransform() {
|
void GLBackend::TransformStageState::transfer() const {
|
||||||
|
static QByteArray bufferData;
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
|
||||||
|
bufferData.resize(_cameraUboSize * _cameras.size());
|
||||||
|
for (size_t i = 0; i < _cameras.size(); ++i) {
|
||||||
|
memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
|
||||||
|
}
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
|
||||||
|
bufferData.resize(_objectUboSize * _objects.size());
|
||||||
|
for (size_t i = 0; i < _objects.size(); ++i) {
|
||||||
|
memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
|
||||||
|
}
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
|
CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const {
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
while (!_transform._objectOffsets.empty() && _commandIndex >= _transform._objectOffsets.front().first) {
|
while ((_objectsItr != _objectOffsets.end()) && (commandIndex >= (*_objectsItr).first)) {
|
||||||
offset = _transform._objectOffsets.front().second;
|
offset = (*_objectsItr).second;
|
||||||
_transform._objectOffsets.pop_front();
|
++_objectsItr;
|
||||||
}
|
}
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT,
|
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT,
|
||||||
_transform._transformObjectBuffer,
|
_objectBuffer, offset, sizeof(Backend::TransformObject));
|
||||||
offset, sizeof(Backend::TransformObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = -1;
|
offset = -1;
|
||||||
while (!_transform._cameraOffsets.empty() && _commandIndex >= _transform._cameraOffsets.front().first) {
|
while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) {
|
||||||
offset = _transform._cameraOffsets.front().second;
|
offset = (*_camerasItr).second;
|
||||||
_transform._cameraOffsets.pop_front();
|
++_camerasItr;
|
||||||
}
|
}
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT,
|
// We include both camera offsets for stereo
|
||||||
_transform._transformCameraBuffer,
|
if (stereo._enable && stereo._pass) {
|
||||||
offset, sizeof(Backend::TransformCamera));
|
offset += _cameraUboSize;
|
||||||
|
}
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT,
|
||||||
|
_cameraBuffer, offset, sizeof(Backend::TransformCamera));
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLBackend::updateTransform() const {
|
||||||
|
_transform.update(_commandIndex, _stereo);
|
||||||
|
}
|
||||||
|
|
||||||
void GLBackend::resetTransformStage() {
|
void GLBackend::resetTransformStage() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,6 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
|
||||||
renderHand(rightHand, batch, RIGHT_HAND);
|
renderHand(rightHand, batch, RIGHT_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
args->_context->syncCache();
|
|
||||||
args->_context->render(batch);
|
args->_context->render(batch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,6 +246,5 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
||||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
||||||
|
|
||||||
// Ready to render
|
// Ready to render
|
||||||
args->_context->syncCache();
|
|
||||||
args->_context->render((batch));
|
args->_context->render((batch));
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
|
||||||
|
|
||||||
void DeferredLightingEffect::prepare(RenderArgs* args) {
|
void DeferredLightingEffect::prepare(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
|
|
||||||
batch.setStateScissorRect(args->_viewport);
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
|
@ -244,6 +245,9 @@ gpu::FramebufferPointer _copyFBO;
|
||||||
void DeferredLightingEffect::render(RenderArgs* args) {
|
void DeferredLightingEffect::render(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
||||||
|
// Framebuffer copy operations cannot function as multipass stereo operations.
|
||||||
|
batch.enableStereo(false);
|
||||||
|
|
||||||
// perform deferred lighting, rendering to free fbo
|
// perform deferred lighting, rendering to free fbo
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
|
||||||
|
@ -555,6 +559,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
|
||||||
|
|
||||||
void DeferredLightingEffect::copyBack(RenderArgs* args) {
|
void DeferredLightingEffect::copyBack(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderCon
|
||||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
batch.setFramebuffer(nullptr);
|
batch.setFramebuffer(nullptr);
|
||||||
batch.setFramebuffer(primaryFbo);
|
batch.setFramebuffer(primaryFbo);
|
||||||
|
|
||||||
|
@ -159,6 +160,8 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
renderContext->_numDrawnOpaqueItems = inItems.size();
|
renderContext->_numDrawnOpaqueItems = inItems.size();
|
||||||
|
@ -188,6 +191,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
renderContext->_numDrawnTransparentItems = inItems.size();
|
renderContext->_numDrawnTransparentItems = inItems.size();
|
||||||
|
@ -247,30 +252,42 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
||||||
renderContext->_numFeedOverlay3DItems = inItems.size();
|
renderContext->_numFeedOverlay3DItems = inItems.size();
|
||||||
renderContext->_numDrawnOverlay3DItems = inItems.size();
|
renderContext->_numDrawnOverlay3DItems = inItems.size();
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
|
||||||
gpu::Batch batch;
|
|
||||||
args->_batch = &batch;
|
|
||||||
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
|
|
||||||
|
|
||||||
glm::mat4 projMat;
|
|
||||||
Transform viewMat;
|
|
||||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
|
||||||
args->_viewFrustum->evalViewTransform(viewMat);
|
|
||||||
|
|
||||||
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()) {
|
if (!inItems.empty()) {
|
||||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
|
RenderArgs* args = renderContext->args;
|
||||||
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
args->_context->render((*args->_batch));
|
// Clear the framebuffer without stereo
|
||||||
args->_batch = nullptr;
|
// Needs to be distinct from the other batch because using the clear call
|
||||||
args->_whiteTexture.reset();
|
// while stereo is enabled triggers a warning
|
||||||
|
{
|
||||||
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
|
||||||
|
args->_context->render(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the items
|
||||||
|
{
|
||||||
|
gpu::Batch batch;
|
||||||
|
args->_batch = &batch;
|
||||||
|
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||||
|
args->_viewFrustum->evalViewTransform(viewMat);
|
||||||
|
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
|
batch.setPipeline(getOpaquePipeline());
|
||||||
|
batch.setResourceTexture(0, args->_whiteTexture);
|
||||||
|
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
|
||||||
|
|
||||||
|
args->_context->render((*args->_batch));
|
||||||
|
args->_batch = nullptr;
|
||||||
|
args->_whiteTexture.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,8 +161,5 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
||||||
batch.draw(gpu::TRIANGLES, 24, 0);
|
batch.draw(gpu::TRIANGLES, 24, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before rendering the batch make sure we re in sync with gl state
|
args->_context->render(batch);
|
||||||
args->_context->syncCache();
|
|
||||||
renderContext->args->_context->syncCache();
|
|
||||||
args->_context->render((batch));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,9 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
|
||||||
}
|
}
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableSkybox(true);
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
glm::mat4 projMat;
|
glm::mat4 projMat;
|
||||||
|
@ -271,9 +274,6 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
|
||||||
renderItems(sceneContext, renderContext, inItems);
|
renderItems(sceneContext, renderContext, inItems);
|
||||||
args->_context->render((*args->_batch));
|
args->_context->render((*args->_batch));
|
||||||
args->_batch = nullptr;
|
args->_batch = nullptr;
|
||||||
|
|
||||||
// Force the context sync
|
|
||||||
args->_context->syncCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
|
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
|
||||||
|
|
Loading…
Reference in a new issue