diff --git a/CREDITS b/CREDITS index c436b2e42..be97421d3 100644 --- a/CREDITS +++ b/CREDITS @@ -95,8 +95,6 @@ template.level OD Team GPLv3+ Name Author License Source URL ------------------------------------------------------------------------------------------------------------------------ -RTShaderLib/* The Ogre 3D Team MIT http://www.ogre3d.org/licensing - scripts/ -> *.material OD Team GPLv3+ @@ -185,6 +183,8 @@ textures/ -> MysteryBox*.png Nobiax, Danimal CC-BY-SA 3.0 http://opengameart.org/users/danimal -> OrcBed*.png Bart, p0ss CC-BY-SA 3.0 http://forum.freegamedev.net/viewtopic.php?p=60191#p60191 -> Orc{,Normal}.png +-> Panels_Diffuse.png The Ogre 3D Team MIT https://github.com/OGRECave/ogre - Samples/Media/materials/textures +-> Panels_Normal_Tangent.png The Ogre 3D Team MIT https://github.com/OGRECave/ogre - Samples/Media/materials/textures -> PitDemon*.png -> Podium*.png -> Portal{,Normal}.png Yughues CC0 http://opengameart.org/users/yughues diff --git a/gui/LoadConfirm.layout b/gui/LoadConfirm.layout index e84f22e81..6f7004eed 100644 --- a/gui/LoadConfirm.layout +++ b/gui/LoadConfirm.layout @@ -7,20 +7,25 @@ + - + + + - + - + diff --git a/gui/OD.looknfeel b/gui/OD.looknfeel index 26f70ceef..72eee5267 100644 --- a/gui/OD.looknfeel +++ b/gui/OD.looknfeel @@ -973,6 +973,10 @@ + + @@ -1000,6 +1004,7 @@ + diff --git a/gui/WindowApplyChanges.layout b/gui/WindowApplyChanges.layout index 001b0adb4..37a8780df 100644 --- a/gui/WindowApplyChanges.layout +++ b/gui/WindowApplyChanges.layout @@ -2,27 +2,30 @@ - + + - + - + - + - + diff --git a/gui/WindowQuit.layout b/gui/WindowQuit.layout index d7a85262a..fcb0553c2 100644 --- a/gui/WindowQuit.layout +++ b/gui/WindowQuit.layout @@ -2,24 +2,27 @@ - + + - + - + - + diff --git a/materials/RTShaderLib/GLSL/RTSLib_Colour.glsl b/materials/RTShaderLib/GLSL/RTSLib_Colour.glsl deleted file mode 100644 index f0ffb3495..000000000 --- a/materials/RTShaderLib/GLSL/RTSLib_Colour.glsl +++ /dev/null @@ -1,16 +0,0 @@ -// This file is part of the OGRE project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at https://www.ogre3d.org/licensing. -// SPDX-License-Identifier: MIT - -#ifdef USE_LINEAR_COLOURS -#define ENABLE_LINEAR_COLOUR(colour) colour.rgb = pow(colour.rgb, vec3_splat(2.2)) -#else -#define ENABLE_LINEAR_COLOUR(colour) -#endif - -#if defined(USE_LINEAR_COLOURS) && !defined(TARGET_CONSUMES_LINEAR) -#define COLOUR_TRANSFER(colour) colour.rgb = pow(colour.rgb, vec3_splat(1.0/2.2)) -#else -#define COLOUR_TRANSFER(colour) -#endif diff --git a/materials/RTShaderLib/GLSL/SGXLib_NormalMap.glsl b/materials/RTShaderLib/GLSL/SGXLib_NormalMap.glsl deleted file mode 100644 index 31f03b5a8..000000000 --- a/materials/RTShaderLib/GLSL/SGXLib_NormalMap.glsl +++ /dev/null @@ -1,125 +0,0 @@ -/* ------------------------------------------------------------------------------ -This source file is part of OGRE -(Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org - -Copyright (c) 2000-2014 Torus Knot Software Ltd -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ -*/ - -//----------------------------------------------------------------------------- -// Program Name: SGXLib_NormalMapLighting -// Program Desc: Normal map lighting functions. -// Program Type: Vertex/Pixel shader -// Language: GLSL -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -void SGX_FetchNormal(in sampler2D s, - in vec2 uv, - out vec3 vOut) -{ - vOut = 2.0 * texture2D(s, uv).xyz - 1.0; -} - -//----------------------------------------------------------------------------- -void SGX_CalculateTBN(in vec3 vNormal, - in vec4 vTangent, - out mat3 TBN) -{ - vec3 vBinormal = cross(vNormal, vTangent.xyz) * vTangent.w; - - // direction: from tangent space to world - TBN = mtxFromCols(vTangent.xyz, vBinormal, vNormal); -} - -//----------------------------------------------------------------------------- -void SGX_Generate_Parallax_Texcoord(in sampler2D normalHeightMap, - in vec2 texCoord, - in vec3 viewPos, - in float heightScale, - in mat3 TBN, - out f32vec2 newTexCoord) -{ - //Calculate eye direction - f32vec3 eyeVec = mul(-viewPos, TBN); - eyeVec = normalize(eyeVec); -#ifndef TERRAIN_PARALLAX_MAPPING - eyeVec.y = -eyeVec.y; //Inverse y -#endif - - newTexCoord = texCoord; - -#ifndef POM_LAYER_COUNT - //Simple parallax mapping - float height = 1.0 - texture2D(normalHeightMap, newTexCoord).a; - - #ifndef TERRAIN_PARALLAX_MAPPING - vec2 p = eyeVec.xy / eyeVec.z * (height * heightScale); - #else - vec2 p = eyeVec.xy * (height * heightScale); - #endif - - newTexCoord = newTexCoord - p; -#else - // parallax occlusion mapping -#ifdef POM_MAX_DISTANCE - if (abs(viewPos.z) > POM_MAX_DISTANCE) - return; -#endif - - //Configure steep mapping layering. - float layerDepth = 1.0 / float(POM_LAYER_COUNT); - float currentLayerDepth = 0.0; - vec2 parallaxShift = (eyeVec.xy) * heightScale; - vec2 deltaTexCoords = parallaxShift / float(POM_LAYER_COUNT); - - float currentDepthMapValue = 1.0 - texture2D(normalHeightMap, newTexCoord).a; - - //Loop through layers and break early if match found. - for (int currentLayerId = 0; currentLayerId < POM_LAYER_COUNT; currentLayerId++) - { - // shift texture coordinates along direction of P - newTexCoord -= deltaTexCoords; - - // get depthmap value at current texture coordinates - currentDepthMapValue = 1.0 - texture2D(normalHeightMap, newTexCoord).a; - - //Break if layer height matched - if (currentLayerDepth > currentDepthMapValue) - break; - - // get depth of next layer - currentLayerDepth += layerDepth; - } - - // get texture coordinates before collision (reverse operations) - vec2 prevTexCoords = newTexCoord + deltaTexCoords; - - // get depth after and before collision for linear interpolation - float afterDepth = currentDepthMapValue - currentLayerDepth; - float beforeDepth = (1.0 - texture2D(normalHeightMap, prevTexCoords).a) - currentLayerDepth + layerDepth; - - // interpolation of texture coordinates - float weight = afterDepth / (afterDepth - beforeDepth); - newTexCoord = mix(newTexCoord, prevTexCoords, weight); -#endif -} diff --git a/materials/textures/Panels_Diffuse.png b/materials/textures/Panels_Diffuse.png new file mode 100644 index 000000000..15b242c4a Binary files /dev/null and b/materials/textures/Panels_Diffuse.png differ diff --git a/source/entities/Creature.cpp b/source/entities/Creature.cpp index 6a6e029ab..a740ba4cd 100755 --- a/source/entities/Creature.cpp +++ b/source/entities/Creature.cpp @@ -1962,13 +1962,17 @@ void Creature::createStatsWindow() mStatsWindow = wmgr->createWindow("OD/FrameWindow", std::string("CreatureStatsWindows_") + getName()); mStatsWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.3, 0), CEGUI::UDim(0.3, 0))); - mStatsWindow->setSize(CEGUI::USize(CEGUI::UDim(0, 380), CEGUI::UDim(0, 400))); + mStatsWindow->setSize(CEGUI::USize(CEGUI::UDim(0, 380), CEGUI::UDim(0, 460))); + // The frame's client area already excludes the titlebar, so the text pane + // can use almost all of it; the scrollbar keeps the tail of the stats + // reachable when the text is still taller than the window. CEGUI::Window* textWindow = wmgr->createWindow("OD/StaticText", "TextDisplay"); - textWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.05, 0), CEGUI::UDim(0.1, 0))); - textWindow->setSize(CEGUI::USize(CEGUI::UDim(0.9, 0), CEGUI::UDim(0.85, 0))); + textWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.05, 0), CEGUI::UDim(0.02, 0))); + textWindow->setSize(CEGUI::USize(CEGUI::UDim(0.9, 0), CEGUI::UDim(0.96, 0))); textWindow->setProperty("FrameEnabled", "False"); textWindow->setProperty("BackgroundEnabled", "False"); + textWindow->setProperty("VertScrollbar", "True"); // We want to close the window when the cross is clicked mStatsWindow->subscribeEvent(CEGUI::FrameWindow::EventCloseClicked, diff --git a/source/entities/Tile.cpp b/source/entities/Tile.cpp index b7e7dc3f0..b931f214e 100755 --- a/source/entities/Tile.cpp +++ b/source/entities/Tile.cpp @@ -2323,11 +2323,15 @@ void Tile::createStatsWindow() mStatsWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.3, 0), CEGUI::UDim(0.3, 0))); mStatsWindow->setSize(CEGUI::USize(CEGUI::UDim(0, 380), CEGUI::UDim(0, 400))); + // The frame's client area already excludes the titlebar, so the text pane + // can use almost all of it; the scrollbar keeps the tail of the stats + // reachable when the text is still taller than the window. CEGUI::Window* textWindow = wmgr->createWindow("OD/StaticText", "TextDisplay"); - textWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.05, 0), CEGUI::UDim(0.1, 0))); - textWindow->setSize(CEGUI::USize(CEGUI::UDim(0.9, 0), CEGUI::UDim(0.85, 0))); + textWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.05, 0), CEGUI::UDim(0.02, 0))); + textWindow->setSize(CEGUI::USize(CEGUI::UDim(0.9, 0), CEGUI::UDim(0.96, 0))); textWindow->setProperty("FrameEnabled", "False"); textWindow->setProperty("BackgroundEnabled", "False"); + textWindow->setProperty("VertScrollbar", "True"); // We want to close the window when the cross is clicked mStatsWindow->subscribeEvent(CEGUI::FrameWindow::EventCloseClicked, diff --git a/source/modes/InputManager.cpp b/source/modes/InputManager.cpp index 785700088..43535df8d 100755 --- a/source/modes/InputManager.cpp +++ b/source/modes/InputManager.cpp @@ -91,14 +91,13 @@ InputManager::InputManager(Ogre::RenderWindow* renderWindow): paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string(keyboardGrab ? "DISCL_EXCLUSIVE" : "DISCL_NONEXCLUSIVE"))); #elif defined OIS_LINUX_PLATFORM paramList.insert(std::make_pair(std::string("x11_mouse_grab"), std::string(mouseGrab ? "true" : "false"))); - // When grabbing, OIS tracks the pointer by accumulating relative motion and warps - // the real pointer back to the window centre near the edges. Leaving the system - // cursor visible then shows it drifting away from the one CEGUI draws, so hide it. - /* paramList.insert(std::make_pair(std::string("x11_mouse_hide"), std::string(mouseGrab ? "true" : "false"))); - * FIXME: While I was debugging cursor issues I found that hiding actually hides the cursor, - * not showing the system one. It works somehow. Did not have much time to think about - * this yet. Need to return to it later. - */ + // Always hide the X11 cursor over the window: CEGUI draws its own cursor, + // so a visible system cursor would show as a second pointer — and when + // grabbing, OIS additionally warps the real pointer back to the window + // centre, which makes the system cursor visibly drift away from CEGUI's. + // (OIS implements this by setting a blank cursor on the window, so if the + // CEGUI cursor ever fails to render there is no pointer at all — that is + // the trade-off being made here.) paramList.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("true"))); paramList.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string(keyboardGrab ? "true" : "false"))); paramList.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true"))); diff --git a/source/modes/SettingsWindow.cpp b/source/modes/SettingsWindow.cpp index 7827d7f44..610435076 100755 --- a/source/modes/SettingsWindow.cpp +++ b/source/modes/SettingsWindow.cpp @@ -115,6 +115,7 @@ SettingsWindow::SettingsWindow(CEGUI::Window* rootWindow): mSettingsWindow(nullptr), mApplyWindow(nullptr), mRootWindow(rootWindow), + mUiScale(1.0f), dynamicShadowsChanged(false) { if (rootWindow == nullptr) @@ -214,22 +215,58 @@ SettingsWindow::SettingsWindow(CEGUI::Window* rootWindow): initConfig(); // The SettingsWindow layout was designed for 800x600. Scale and center it - // on higher resolutions so it stays usable. - CEGUI::Sizef displaySize = CEGUI::System::getSingleton().getRenderer()->getDisplaySize(); + // on higher resolutions so it stays usable, and re-fit it whenever the + // display size changes (this is the window resolutions are applied from). + mSettingsWindowOriginalArea = mSettingsWindow->getProperty("Area"); + applyUiScale(); + addEventConnection( + CEGUI::System::getSingleton().subscribeEvent( + CEGUI::System::EventDisplaySizeChanged, + CEGUI::Event::Subscriber(&SettingsWindow::onDisplaySizeChanged, this) + ) + ); +} + +void SettingsWindow::applyUiScale() +{ float scale = computeUiScale(); - if (scale > 1.0f) + + // Every widget below the two top-level windows only ever gets its pixel + // offsets multiplied, so moving from the currently applied scale to the + // new one is a plain ratio. This also covers the video-settings widgets + // initConfig() recreates, as long as they are brought to mUiScale when + // they are created. + float ratio = scale / mUiScale; + if (ratio != 1.0f) { - centerAndScaleWindow(mSettingsWindow, scale, displaySize); - scaleWindowTree(mSettingsWindow, scale); + scaleWindowTree(mSettingsWindow, ratio); CEGUI::Window* tabControl = mSettingsWindow->getChild("MainTabControl"); if (tabControl) - scaleDimProperty(tabControl, "TabHeight", scale); + scaleDimProperty(tabControl, "TabHeight", ratio); // The apply-changes popup also uses pixel offsets relative to the centre. - scaleWindowArea(mApplyWindow, scale); - scaleWindowTree(mApplyWindow, scale); + scaleWindowArea(mApplyWindow, ratio); + scaleWindowTree(mApplyWindow, ratio); } + + // The top-level window is re-centered from its designed area rather than + // by ratio, because centering flattens it to pixel offsets for the current + // display size. + mSettingsWindow->setProperty("Area", mSettingsWindowOriginalArea); + if (scale > 1.0f) + { + CEGUI::Sizef displaySize = CEGUI::System::getSingleton().getRenderer()->getDisplaySize(); + centerAndScaleWindow(mSettingsWindow, scale, displaySize); + } + + mUiScale = scale; +} + +bool SettingsWindow::onDisplaySizeChanged(const CEGUI::EventArgs&) +{ + applyUiScale(); + return true; } SettingsWindow::~SettingsWindow() @@ -457,6 +494,15 @@ void SettingsWindow::initConfig() videoCb->setReadOnly(true); videoCb->setSortingEnabled(true); + // These are laid out in design-space pixels; bring them to the UI + // scale the rest of the window is currently at (applyUiScale() then + // keeps them in sync through its ratio scaling). + if (mUiScale != 1.0f) + { + scaleWindowArea(videoCbText, mUiScale); + scaleWindowArea(videoCb, mUiScale); + } + // Register the widgets for potential later deletion. mCustomVideoTexts.push_back(videoCbText); mCustomVideoComboBoxes.push_back(videoCb); diff --git a/source/modes/SettingsWindow.h b/source/modes/SettingsWindow.h index cbb51fa06..4fe4fb4e5 100755 --- a/source/modes/SettingsWindow.h +++ b/source/modes/SettingsWindow.h @@ -75,9 +75,26 @@ class SettingsWindow std::vector mCustomVideoComboBoxes; std::vector mCustomVideoTexts; + //! \brief The UI scale currently applied to the settings/apply windows + //! (1.0 means the layout is at its designed 800x600 geometry). + float mUiScale; + + //! \brief The settings window "Area" as designed in the layout, kept so the + //! window can be re-centered from scratch when the display size changes. + CEGUI::String mSettingsWindowOriginalArea; + //! \brief Set the different widget values according to current config. void initConfig(); + //! \brief Scale and center the settings/apply windows for the current + //! display size. Safe to call again whenever the display size changes. + void applyUiScale(); + + //! \brief Called when the display size changes (e.g. after applying a new + //! resolution) so the windows are re-fitted instead of staying pinned to + //! the pixel geometry of the old resolution. + bool onDisplaySizeChanged(const CEGUI::EventArgs&); + //! \brief Save the config, potentially stopping the application if it needs to. void saveConfig(); diff --git a/source/network/ODServer.cpp b/source/network/ODServer.cpp index 1b50117dc..a4da31a5d 100755 --- a/source/network/ODServer.cpp +++ b/source/network/ODServer.cpp @@ -126,9 +126,15 @@ bool ODServer::startServer(const std::string& creator, const std::string& levelF return false; } - // Set up the socket to listen on the specified port + // Set up the socket to listen on the specified port. Local games (single + // player, editor, loaded saves) reach the server through getNetworkPort(), + // which reports the port actually bound, so they may fall back to an + // ephemeral port when the configured one is busy. A multiplayer host must + // not: remote clients dial the advertised port, so failing loudly here + // beats a connection that never establishes. int32_t port = getNetworkPort(); - if (!createServer(port)) + bool allowPortFallback = (mode != ServerMode::ModeGameMultiPlayer); + if (!createServer(port, allowPortFallback)) { mServerMode = ServerMode::ModeNone; mServerState = ServerState::StateNone; diff --git a/source/network/ODSocketServer.cpp b/source/network/ODSocketServer.cpp index e2d17f654..10037dc1b 100755 --- a/source/network/ODSocketServer.cpp +++ b/source/network/ODSocketServer.cpp @@ -37,7 +37,7 @@ ODSocketServer::~ODSocketServer() stopServer(); } -bool ODSocketServer::createServer(int listeningPort) +bool ODSocketServer::createServer(int listeningPort, bool allowPortFallback) { mIsConnected = false; mActualPort = 0; @@ -46,6 +46,12 @@ bool ODSocketServer::createServer(int listeningPort) sf::Socket::Status status = mSockListener.listen(listeningPort); if (status != sf::Socket::Done) { + if (!allowPortFallback) + { + OD_LOG_ERR("Could not listen to server port " + Helper::toString(listeningPort) + + " status=" + Helper::toString(status)); + return false; + } OD_LOG_WRN("Could not listen to server port " + Helper::toString(listeningPort) + " status=" + Helper::toString(status) + ", trying ephemeral port."); status = mSockListener.listen(0); diff --git a/source/network/ODSocketServer.h b/source/network/ODSocketServer.h index 9f3d713e7..db4bb5e84 100755 --- a/source/network/ODSocketServer.h +++ b/source/network/ODSocketServer.h @@ -39,7 +39,13 @@ class ODSocketServer { return mActualPort; } // Data Transimission - virtual bool createServer(int listeningPort); + //! \brief Starts listening on listeningPort. If the port is busy and + //! allowPortFallback is true, falls back to an ephemeral port (callers + //! can retrieve it with getActualPort()). Hosting a multiplayer game + //! must NOT allow the fallback: remote clients dial the advertised + //! port, and silently binding another one would make them fail to + //! connect with no error on the host side. + virtual bool createServer(int listeningPort, bool allowPortFallback); virtual void stopServer(); protected: diff --git a/source/render/ODFrameListener.cpp b/source/render/ODFrameListener.cpp index 984446b69..42fe96574 100755 --- a/source/render/ODFrameListener.cpp +++ b/source/render/ODFrameListener.cpp @@ -294,6 +294,10 @@ bool ODFrameListener::frameStarted(const Ogre::FrameEvent& evt) return true; } +// Since Ogre 14 (OgreRenderQueueListener.h), the second parameter carries the +// name of the camera being rendered; SceneManager passes +// mCameraInProgress->getName(). The comparison below decides whether CEGUI is +// drawn for this render, so the parameter's meaning matters. void ODFrameListener::renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String& cameraName, bool&) {