diff --git a/doomsday/apps/client/include/gl/gl_main.h b/doomsday/apps/client/include/gl/gl_main.h index f1b3767142..1c92be0173 100644 --- a/doomsday/apps/client/include/gl/gl_main.h +++ b/doomsday/apps/client/include/gl/gl_main.h @@ -133,12 +133,6 @@ void GL_ProjectionMatrix(); de::Rangef GL_DepthClipRange(); -/** - * Returns the projection matrix that is used for rendering the current frame's - * 3D portions. - */ -de::Matrix4f GL_GetProjectionMatrix(); - /** * The first selected unit is active after this call. */ diff --git a/doomsday/apps/client/include/render/rend_main.h b/doomsday/apps/client/include/render/rend_main.h index 061ffd9ba0..704a6436dd 100644 --- a/doomsday/apps/client/include/render/rend_main.h +++ b/doomsday/apps/client/include/render/rend_main.h @@ -179,8 +179,17 @@ de::Matrix4f Rend_GetModelViewMatrix(int consoleNum, bool inWorldSpace = true); de::Vector3d Rend_EyeOrigin(); +/** + * Returns the projection matrix that is used for rendering the current frame's + * 3D portions. + */ +de::Matrix4f Rend_GetProjectionMatrix(); + #define Rend_PointDist2D(c) (fabs((vOrigin.z-(c)[VY])*viewsidex - (vOrigin.x-(c)[VX])*viewsidey)) +void Rend_SetFixedView(int consoleNum, float yaw, float pitch, float fov, de::Vector2f viewportSize); +void Rend_UnsetFixedView(); + /** * The DOOM lighting model applies a light level delta to everything when * e.g. the player shoots. diff --git a/doomsday/apps/client/include/ui/widgets/gamewidget.h b/doomsday/apps/client/include/ui/widgets/gamewidget.h index 1086cf5333..a5043d4efd 100644 --- a/doomsday/apps/client/include/ui/widgets/gamewidget.h +++ b/doomsday/apps/client/include/ui/widgets/gamewidget.h @@ -32,15 +32,6 @@ class GameWidget : public de::GuiWidget public: GameWidget(de::String const &name = "game"); - /* - * Convenience method for changing and immediately applying a new GL - * viewport. The viewport is automatically normalized in relation to the - * root view size. - * - * This is only intended to support old graphics code that doesn't use libgui. - */ - //void glApplyViewport(de::Rectanglei const &rect); - /** * Pauses the game, if one is currently running and pausing is allowed. */ @@ -48,6 +39,8 @@ class GameWidget : public de::GuiWidget void drawComposited(); + void renderCubeMap(uint size, de::String const &outputImagePath); + // Events. void viewResized() override; void update() override; diff --git a/doomsday/apps/client/src/gl/gl_main.cpp b/doomsday/apps/client/src/gl/gl_main.cpp index 88de4a1ab0..b83acecd77 100644 --- a/doomsday/apps/client/src/gl/gl_main.cpp +++ b/doomsday/apps/client/src/gl/gl_main.cpp @@ -390,16 +390,6 @@ Rangef GL_DepthClipRange() return Rangef(glNearClip, glFarClip); } -Matrix4f GL_GetProjectionMatrix() -{ - dfloat const fov = Rend_FieldOfView(); - //Vector2f const size(viewpw, viewph); - Vector2f const size = R_Console3DViewRect(displayPlayer).size(); - yfov = vrCfg().verticalFieldOfView(fov, size); - return vrCfg().projectionMatrix(Rend_FieldOfView(), size, glNearClip, glFarClip) * - Matrix4f::scale(Vector3f(1, 1, -1)); -} - void GL_ProjectionMatrix() { DENG_ASSERT_IN_MAIN_THREAD(); @@ -408,7 +398,7 @@ void GL_ProjectionMatrix() // Actually shift the player viewpoint // We'd like to have a left-handed coordinate system. LIBGUI_GL.glMatrixMode(GL_PROJECTION); - LIBGUI_GL.glLoadMatrixf(GL_GetProjectionMatrix().values()); + LIBGUI_GL.glLoadMatrixf(Rend_GetProjectionMatrix().values()); } void GL_SetupFogFromMapInfo(Record const *mapInfo) diff --git a/doomsday/apps/client/src/render/fx/lensflares.cpp b/doomsday/apps/client/src/render/fx/lensflares.cpp index 2caae3715f..eb651a8a00 100644 --- a/doomsday/apps/client/src/render/fx/lensflares.cpp +++ b/doomsday/apps/client/src/render/fx/lensflares.cpp @@ -517,7 +517,7 @@ void LensFlares::draw() d->uViewUnit = Vector2f(aspect, 1.f); d->uPixelAsUv = Vector2f(1.f / window.pixelWidth(), 1.f / window.pixelHeight()); - d->uMvpMatrix = Viewer_Matrix(); //GL_GetProjectionMatrix() * Rend_GetModelViewMatrix(console()); + d->uMvpMatrix = Viewer_Matrix(); //Rend_GetProjectionMatrix() * Rend_GetModelViewMatrix(console()); DENG2_ASSERT(console() == displayPlayer); //DENG2_ASSERT(viewPlayer - ddPlayers == displayPlayer); diff --git a/doomsday/apps/client/src/render/rend_main.cpp b/doomsday/apps/client/src/render/rend_main.cpp index 72177ca152..a535f33804 100644 --- a/doomsday/apps/client/src/render/rend_main.cpp +++ b/doomsday/apps/client/src/render/rend_main.cpp @@ -157,6 +157,7 @@ D_CMD(OpenRendererAppearanceEditor); D_CMD(LowRes); D_CMD(MipMap); D_CMD(TexReset); +D_CMD(CubeShot); #if 0 dint useBias; ///< Shadow Bias enabled? cvar @@ -229,6 +230,17 @@ Vector3d vOrigin; dfloat vang, vpitch; dfloat viewsidex, viewsidey; +// Helper for overriding the normal view matrices. +struct FixedView +{ + Matrix4f projectionMatrix; + Matrix4f modelViewMatrix; + float yaw; + float pitch; + float horizontalFov; +}; +static std::unique_ptr fixedView; + dbyte freezeRLs; dint devNoCulling; ///< @c 1= disabled (cvar). dint devRendSkyMode; @@ -413,6 +425,11 @@ bool Rend_IsMTexDetails() dfloat Rend_FieldOfView() { + if (fixedView) + { + return fixedView->horizontalFov; + } + if (vrCfg().mode() == VRConfig::OculusRift) { // OVR tells us which FOV to use. @@ -445,20 +462,53 @@ Vector3d Rend_EyeOrigin() return vEyeOrigin; } -Matrix4f Rend_GetModelViewMatrix(dint consoleNum, bool inWorldSpace) +void Rend_SetFixedView(int consoleNum, float yaw, float pitch, float fov, Vector2f viewportSize) { viewdata_t const *viewData = &DD_Player(consoleNum)->viewport(); - dfloat bodyAngle = viewData->current.angleWithoutHeadTracking() / (dfloat) ANGLE_MAX * 360 - 90; + fixedView.reset(new FixedView); + + fixedView->yaw = yaw; + fixedView->pitch = pitch; + fixedView->horizontalFov = fov; + fixedView->modelViewMatrix = + Matrix4f::rotate(pitch, Vector3f(1, 0, 0)) * + Matrix4f::rotate(yaw, Vector3f(0, 1, 0)) * + Matrix4f::scale(Vector3f(1.0f, 1.2f, 1.0f)) * // This is the aspect correction. + Matrix4f::translate(-viewData->current.origin.xzy()); + + Rangef const clip = GL_DepthClipRange(); + fixedView->projectionMatrix = BaseGuiApp::app().vr() + .projectionMatrix(fov, viewportSize, clip.start, clip.end) * + Matrix4f::scale(Vector3f(1, 1, -1)); +} + +void Rend_UnsetFixedView() +{ + fixedView.reset(); +} + +Matrix4f Rend_GetModelViewMatrix(dint consoleNum, bool inWorldSpace) +{ + viewdata_t const *viewData = &DD_Player(consoleNum)->viewport(); /// @todo vOrigin et al. shouldn't be changed in a getter function. -jk vOrigin = viewData->current.origin.xzy(); - vang = viewData->current.angle() / (dfloat) ANGLE_MAX * 360 - 90; // head tracking included - vpitch = viewData->current.pitch * 85.0 / 110.0; - vEyeOrigin = vOrigin; + if (fixedView) + { + vang = fixedView->yaw; + vpitch = fixedView->pitch; + return fixedView->modelViewMatrix; + } + + vang = viewData->current.angle() / (dfloat) ANGLE_MAX * 360 - 90; // head tracking included + vpitch = viewData->current.pitch * 85.0 / 110.0; + + dfloat bodyAngle = viewData->current.angleWithoutHeadTracking() / (dfloat) ANGLE_MAX * 360 - 90; + OculusRift &ovr = vrCfg().oculusRift(); bool const applyHead = (vrCfg().mode() == VRConfig::OculusRift && ovr.isReady()); @@ -523,6 +573,22 @@ void Rend_ModelViewMatrix(bool inWorldSpace) LIBGUI_GL.glLoadMatrixf(Rend_GetModelViewMatrix(DoomsdayApp::players().indexOf(viewPlayer), inWorldSpace).values()); } +Matrix4f Rend_GetProjectionMatrix() +{ + if (fixedView) + { + return fixedView->projectionMatrix; + } + + dfloat const fov = Rend_FieldOfView(); + //Vector2f const size(viewpw, viewph); + Vector2f const size = R_Console3DViewRect(displayPlayer).size(); + yfov = vrCfg().verticalFieldOfView(fov, size); + Rangef const clip = GL_DepthClipRange(); + return vrCfg().projectionMatrix(Rend_FieldOfView(), size, clip.start, clip.end) * + Matrix4f::scale(Vector3f(1, 1, -1)); +} + static inline ddouble viewFacingDot(Vector2d const &v1, Vector2d const &v2) { // The dot product. @@ -6439,6 +6505,7 @@ void Rend_Register() C_CMD("rendedit", "", OpenRendererAppearanceEditor); C_CMD("modeledit", "", OpenModelAssetEditor); + C_CMD("cubeshot", "i", CubeShot); C_CMD_FLAGS("lowres", "", LowRes, CMDF_NO_DEDICATED); C_CMD_FLAGS("mipmap", "i", MipMap, CMDF_NO_DEDICATED); diff --git a/doomsday/apps/client/src/render/viewports.cpp b/doomsday/apps/client/src/render/viewports.cpp index 5acef6a004..e6d12b78dd 100644 --- a/doomsday/apps/client/src/render/viewports.cpp +++ b/doomsday/apps/client/src/render/viewports.cpp @@ -675,7 +675,6 @@ void R_SetupFrame(player_t *player) player->extraLight = player->targetExtraLight; } - // Why? validCount++; extraLight = player->extraLight; @@ -887,7 +886,7 @@ static Matrix4f frameViewMatrix; static void setupViewMatrix() { // This will be the view matrix for the current frame. - frameViewMatrix = GL_GetProjectionMatrix() * + frameViewMatrix = Rend_GetProjectionMatrix() * Rend_GetModelViewMatrix(DoomsdayApp::players().indexOf(viewPlayer)); } @@ -1223,56 +1222,27 @@ void R_RenderViewPort(int playerNum) RectRaw vdWindow(vd->window.topLeft.x, vd->window.topLeft.y, vd->window.width(), vd->window.height()); - //switch(layer) - //{ - //case Player3DViewLayer: - R_UpdateViewer(vp->console); - //LensFx_BeginFrame(vp->console); - gx.DrawViewPort(localNum, &vpGeometry, &vdWindow, displayPlayer, /* layer: */ 0); - //LensFx_EndFrame(); - //break; - - LensFx_Draw(vp->console); - // Apply camera lens effects on the rendered view. - - -#if 0 - case ViewBorderLayer: - R_RenderPlayerViewBorder(); - break; - - case HUDLayer: - gx.DrawViewPort(p, &vpGeometry, &vdWindow, displayPlayer, /* layer: */ 1); - break; - } -#endif + LensFx_Draw(vp->console); restoreDefaultGLState(); LIBGUI_GL.glMatrixMode(GL_PROJECTION); LIBGUI_GL.glPopMatrix(); -//} -//if(layer == Player3DViewLayer) -//{ // Increment the internal frame count. This does not // affect the window's FPS counter. frameCount++; // Keep reseting until a new sharp world has arrived. if(resetNextViewer > 1) resetNextViewer = 0; -//} // Restore things back to normal. displayPlayer = oldDisplay; - - //R_UseViewPort(nullptr); - //currentViewport = nullptr; } #if 0 @@ -1539,9 +1509,9 @@ void R_ViewerClipLumobj(Lumobj *lum) /// @todo Determine the exact centerpoint of the light in addLuminous! Vector3d const origin(lum->x(), lum->y(), lum->z() + lum->zOffset()); - if(!(devNoCulling || P_IsInVoid(DD_Player(displayPlayer)))) + if (!P_IsInVoid(DD_Player(displayPlayer)) && !devNoCulling) { - if(!ClientApp::renderSystem().angleClipper().isPointVisible(origin)) + if (!ClientApp::renderSystem().angleClipper().isPointVisible(origin)) { markLumobjClipped(*lum); // Won't have a halo. } @@ -1551,7 +1521,7 @@ void R_ViewerClipLumobj(Lumobj *lum) markLumobjClipped(*lum); Vector3d const eye = Rend_EyeOrigin().xzy(); - if(LineSightTest(eye, origin, -1, 1, LS_PASSLEFT | LS_PASSOVER | LS_PASSUNDER) + if (LineSightTest(eye, origin, -1, 1, LS_PASSLEFT | LS_PASSOVER | LS_PASSUNDER) .trace(lum->map().bspTree())) { markLumobjClipped(*lum, false); // Will have a halo. diff --git a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp index 7e2e195985..f1fb51a131 100644 --- a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp @@ -48,11 +48,16 @@ #include "gl/gl_defer.h" #include +#include #include #include #include #include +#include +#include +#include + /** * Maximum number of milliseconds spent uploading textures at the beginning * of a frame. Note that non-uploaded textures will appear as pure white @@ -87,6 +92,22 @@ DENG2_PIMPL(GameWidget) }); } + void renderPlayerViewToFramebuffer(int playerNum, GLFramebuffer &dest) + { + GLState::push() + .setTarget(dest) + .setViewport(Rectangleui::fromSize(dest.size())) + .apply(); + + dest.clear(GLFramebuffer::ColorDepthStencil); + + // Rendering is done by the caller-provided callback. + R_RenderViewPort(playerNum); + + GLState::pop() + .apply(); + } + /** * Draw the game widget's contents by compositing the various layers: game view, * border, HUD, finale, intermission, and engine/debug overlays. This is generally @@ -183,6 +204,67 @@ void GameWidget::drawComposited() d->drawComposited(); } +void GameWidget::renderCubeMap(uint size, String const &outputImagePath) +{ + int const player = consolePlayer; + Vector2ui fbSize(size, size); + + GLTextureFramebuffer destFb(Image::RGB_888, fbSize, 1); + destFb.glInit(); + + LOG_GL_MSG("Rendering %ix%i cube map to \"%s\"") << 6*fbSize.x << fbSize.y << outputImagePath; + + // Prevent the angleclipper from clipping anything. + int old_devNoCulling = devNoCulling; + devNoCulling = 1; + + // Make the player temporarily a plain camera to hide weapons etc. + ClientPlayer &plr = ClientApp::player(player); + auto const oldPlrFlags = plr.publicData().flags; + plr.publicData().flags |= DDPF_CAMERA; + + // Notify the world that a new render frame has begun. + App_World().beginFrame(CPP_BOOL(R_NextViewer())); + + QImage composited(QSize(6 * size, size), QImage::Format_RGB32); + QPainter painter(&composited); + + int const baseYaw = 180; + + for (int i = 0; i < 6; ++i) + { + if (i < 4) + { + Rend_SetFixedView(player, baseYaw + 90 + i * -90, 0, 90, fbSize); + } + else + { + Rend_SetFixedView(player, baseYaw, i == 4? -90 : 90, 90, fbSize); + } + d->renderPlayerViewToFramebuffer(player, destFb); + painter.drawImage(i * size, 0, destFb.toImage()); + } + + App_World().endFrame(); + + // Write the composited image to a file. + { + QBuffer buf; + buf.open(QBuffer::WriteOnly); + composited.save(&buf, outputImagePath.fileNameExtension().mid(1).toLatin1()); + + File &outFile = FS::get().root().replaceFile(outputImagePath); + outFile << Block(buf.data()); + outFile.flush(); + } + + // Cleanup. + destFb.glDeinit(); + Rend_UnsetFixedView(); + devNoCulling = old_devNoCulling; + plr.publicData().flags = oldPlrFlags; +} + void GameWidget::viewResized() { GuiWidget::viewResized(); @@ -305,3 +387,13 @@ void GameWidget::glDeinit() //d->glDeinit(); } + +D_CMD(CubeShot) +{ + DENG2_UNUSED2(src, argc); + + int size = String(argv[1]).toInt(); + if (size < 8) return false; + ClientWindow::main().game().renderCubeMap(size, "/home/cubeshot.png"); + return true; +}