Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor

pull/3235/head
Zackhasacat 1 year ago
commit 162ac452cf

@ -133,6 +133,7 @@
Feature #7625: Add some missing console error outputs
Feature #7634: Support NiParticleBomb
Feature #7652: Sort inactive post processing shaders list properly
Feature #7709: Improve resolution selection in Launcher
Task #5896: Do not use deprecated MyGUI properties
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector

@ -2,6 +2,7 @@
#include "sdlinit.hpp"
#include <components/misc/display.hpp>
#include <components/settings/values.hpp>
#include <QMessageBox>
@ -16,22 +17,6 @@
#include <SDL_video.h>
#include <array>
#include <numeric>
QString getAspect(int x, int y)
{
int gcd = std::gcd(x, y);
if (gcd == 0)
return QString();
int xaspect = x / gcd;
int yaspect = y / gcd;
// special case: 8 : 5 is usually referred to as 16:10
if (xaspect == 8 && yaspect == 5)
return QString("16:10");
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
}
Launcher::GraphicsPage::GraphicsPage(QWidget* parent)
: QWidget(parent)
@ -117,7 +102,7 @@ bool Launcher::GraphicsPage::loadSettings()
const int width = Settings::video().mResolutionX;
const int height = Settings::video().mResolutionY;
QString resolution = QString::number(width) + QString(" x ") + QString::number(height);
QString resolution = QString::number(width) + QString(" × ") + QString::number(height);
screenComboBox->setCurrentIndex(Settings::video().mScreen);
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
@ -204,7 +189,7 @@ void Launcher::GraphicsPage::saveSettings()
int cHeight = 0;
if (standardRadioButton->isChecked())
{
QRegularExpression resolutionRe("^(\\d+) x (\\d+)");
QRegularExpression resolutionRe("^(\\d+) × (\\d+)");
QRegularExpressionMatch match = resolutionRe.match(resolutionComboBox->currentText().simplified());
if (match.hasMatch())
{
@ -304,19 +289,8 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
return result;
}
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
QString aspect = getAspect(mode.w, mode.h);
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10"))
{
resolution.append(tr(" (Wide ") + aspect + ")");
}
else if (aspect == QLatin1String("4:3"))
{
resolution.append(tr(" (Standard 4:3)"));
}
result.append(resolution);
auto str = Misc::getResolutionText(mode.w, mode.h, "%i × %i (%i:%i)");
result.append(QString(str.c_str()));
}
result.removeDuplicates();

@ -2,7 +2,6 @@
#include <array>
#include <iomanip>
#include <numeric>
#include <regex>
#include <unicode/locid.h>
@ -21,6 +20,7 @@
#include <components/debug/debuglog.hpp>
#include <components/lua_ui/scriptsettings.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/display.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/format.hpp>
@ -93,20 +93,6 @@ namespace
return left.first > right.first;
}
std::string getAspect(int x, int y)
{
int gcd = std::gcd(x, y);
if (gcd == 0)
return std::string();
int xaspect = x / gcd;
int yaspect = y / gcd;
// special case: 8 : 5 is usually referred to as 16:10
if (xaspect == 8 && yaspect == 5)
return "16 : 10";
return MyGUI::utility::toString(xaspect) + " : " + MyGUI::utility::toString(yaspect);
}
const std::string_view checkButtonType = "CheckButton";
const std::string_view sliderType = "Slider";
@ -366,11 +352,7 @@ namespace MWGui
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
for (std::pair<int, int>& resolution : resolutions)
{
std::string str
= MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second);
std::string aspect = getAspect(resolution.first, resolution.second);
if (!aspect.empty())
str = str + " (" + aspect + ")";
std::string str = Misc::getResolutionText(resolution.first, resolution.second, "%i x %i (%i:%i)");
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
mResolutionList->addItem(str);

@ -417,20 +417,23 @@ namespace MWLua
using DelayedRemovalFn = std::function<void(MWWorld::Ptr)>;
auto removeFn = [](const MWWorld::Ptr ptr, int countToRemove) -> std::optional<DelayedRemovalFn> {
int currentCount = ptr.getRefData().getCount();
int rawCount = ptr.getRefData().getCount(false);
int currentCount = std::abs(rawCount);
int signedCountToRemove = (rawCount < 0 ? -1 : 1) * countToRemove;
if (countToRemove <= 0 || countToRemove > currentCount)
throw std::runtime_error("Can't remove " + std::to_string(countToRemove) + " of "
+ std::to_string(currentCount) + " items");
ptr.getRefData().setCount(currentCount - countToRemove); // Immediately change count
ptr.getRefData().setCount(rawCount - signedCountToRemove); // Immediately change count
if (!ptr.getContainerStore() && currentCount > countToRemove)
return std::nullopt;
// Delayed action to trigger side effects
return [countToRemove](MWWorld::Ptr ptr) {
return [signedCountToRemove](MWWorld::Ptr ptr) {
// Restore the original count
ptr.getRefData().setCount(ptr.getRefData().getCount() + countToRemove);
ptr.getRefData().setCount(ptr.getRefData().getCount(false) + signedCountToRemove);
// And now remove properly
if (ptr.getContainerStore())
ptr.getContainerStore()->remove(ptr, countToRemove, false);
ptr.getContainerStore()->remove(ptr, std::abs(signedCountToRemove), false);
else
{
MWBase::Environment::get().getWorld()->disable(ptr);

@ -273,7 +273,7 @@ add_component_dir (esm4
)
add_component_dir (misc
barrier budgetmeasurement color compression constants convert coordinateconverter endianness float16 frameratelimiter
barrier budgetmeasurement color compression constants convert coordinateconverter display endianness float16 frameratelimiter
guarded math mathutil messageformatparser notnullptr objectpool osguservalues progressreporter resourcehelpers rng
strongtypedef thread timeconvert timer tuplehelpers tuplemeta utf8stream weakcache windows
)

@ -99,7 +99,7 @@ namespace LuaUi
if (parent)
{
auto children = parent->children();
std::remove(children.begin(), children.end(), root);
std::erase(children, root);
parent->setChildren(children);
root->widget()->detachFromWidget();
}

@ -0,0 +1,82 @@
#include "display.hpp"
#include <numeric>
#include <string>
#include <components/misc/strings/format.hpp>
namespace Misc
{
std::string getResolutionText(int x, int y, const std::string& format)
{
int gcd = std::gcd(x, y);
if (gcd == 0)
return std::string();
int xaspect = x / gcd;
int yaspect = y / gcd;
// It is unclear how to handle 90-degree screen rotation properly.
// So far only swap aspects, apply custom formatting logic and then swap back.
// As result, 1920 x 1200 is displayed as "1200 x 1920 (10:16)"
bool flipped = false;
if (yaspect > xaspect)
{
flipped = true;
std::swap(xaspect, yaspect);
}
// 683:384 (used in 1366 x 768) is usually referred as 16:9
if (xaspect == 683 && yaspect == 384)
{
xaspect = 16;
yaspect = 9;
}
// 85:48 (used in 1360 x 768) is usually referred as 16:9
else if (xaspect == 85 && yaspect == 48)
{
xaspect = 16;
yaspect = 9;
}
// 49:36 (used in 1176 x 864) is usually referred as 4:3
else if (xaspect == 49 && yaspect == 36)
{
xaspect = 4;
yaspect = 3;
}
// 39:29 (used in 624 x 484) is usually referred as 4:3
else if (xaspect == 39 && yaspect == 29)
{
xaspect = 4;
yaspect = 3;
}
// 8:5 (used in 1440 x 900) is usually referred as 16:10
else if (xaspect == 8 && yaspect == 5)
{
xaspect = 16;
yaspect = 10;
}
// 5:3 (used in 1280 x 768) is usually referred as 15:9
else if (xaspect == 5 && yaspect == 3)
{
xaspect = 15;
yaspect = 9;
}
else
{
// everything between 21:9 and 22:9
// is usually referred as 21:9
float ratio = static_cast<float>(xaspect) / yaspect;
if (ratio >= 21 / 9.f && ratio < 22 / 9.f)
{
xaspect = 21;
yaspect = 9;
}
}
if (flipped)
std::swap(xaspect, yaspect);
return Misc::StringUtils::format(format, x, y, xaspect, yaspect);
}
}

@ -0,0 +1,11 @@
#ifndef OPENMW_COMPONENTS_MISC_DISPLAY_H
#define OPENMW_COMPONENTS_MISC_DISPLAY_H
#include <string>
namespace Misc
{
std::string getResolutionText(int x, int y, const std::string& format);
}
#endif

@ -2,13 +2,33 @@
#include <cstdint>
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <components/misc/convert.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/resource/bulletshape.hpp>
#include "data.hpp"
#include "exception.hpp"
#include "physics.hpp"
#include "property.hpp"
namespace
{
void triBasedGeomToBtTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
{
// FIXME: copying vertices/indices individually is unreasonable
const std::vector<osg::Vec3f>& vertices = data.mVertices;
mesh.preallocateVertices(static_cast<int>(vertices.size()));
for (const osg::Vec3f& vertex : vertices)
mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);
mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3);
}
}
namespace Nif
{
@ -218,6 +238,82 @@ namespace Nif
}
}
std::unique_ptr<btCollisionShape> NiTriShape::getCollisionShape() const
{
if (mData.empty() || mData->mVertices.empty())
return nullptr;
auto data = static_cast<const NiTriShapeData*>(mData.getPtr());
if (data->mNumTriangles == 0 || data->mTriangles.empty())
return nullptr;
auto mesh = std::make_unique<btTriangleMesh>();
triBasedGeomToBtTriangleMesh(*mesh, *data);
const std::vector<unsigned short>& triangles = data->mTriangles;
for (std::size_t i = 0; i < triangles.size(); i += 3)
mesh->addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]);
if (mesh->getNumTriangles() == 0)
return nullptr;
auto shape = std::make_unique<Resource::TriangleMeshShape>(mesh.get(), true);
std::ignore = mesh.release();
return shape;
}
std::unique_ptr<btCollisionShape> NiTriStrips::getCollisionShape() const
{
if (mData.empty() || mData->mVertices.empty())
return nullptr;
auto data = static_cast<const NiTriStripsData*>(mData.getPtr());
if (data->mNumTriangles == 0 || data->mStrips.empty())
return nullptr;
auto mesh = std::make_unique<btTriangleMesh>();
triBasedGeomToBtTriangleMesh(*mesh, *data);
for (const std::vector<unsigned short>& strip : data->mStrips)
{
if (strip.size() < 3)
continue;
unsigned short a;
unsigned short b = strip[0];
unsigned short c = strip[1];
for (size_t i = 2; i < strip.size(); i++)
{
a = b;
b = c;
c = strip[i];
if (a == b || b == c || a == c)
continue;
if (i % 2 == 0)
mesh->addTriangleIndices(a, b, c);
else
mesh->addTriangleIndices(a, c, b);
}
}
if (mesh->getNumTriangles() == 0)
return nullptr;
auto shape = std::make_unique<Resource::TriangleMeshShape>(mesh.get(), true);
std::ignore = mesh.release();
return shape;
}
std::unique_ptr<btCollisionShape> NiLines::getCollisionShape() const
{
return nullptr;
}
std::unique_ptr<btCollisionShape> NiParticles::getCollisionShape() const
{
return nullptr;
}
void BSSegmentedTriShape::SegmentData::read(NIFStream* nif)
{
nif->read(mFlags);

@ -8,6 +8,8 @@
#include "base.hpp"
class btCollisionShape;
namespace Nif
{
@ -146,6 +148,11 @@ namespace Nif
void read(NIFStream* nif) override;
void post(Reader& nif) override;
virtual std::unique_ptr<btCollisionShape> getCollisionShape() const
{
throw std::runtime_error("NiGeometry::getCollisionShape() called on base class");
}
};
// Abstract triangle-based geometry
@ -155,6 +162,7 @@ namespace Nif
struct NiTriShape : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};
struct BSSegmentedTriShape : NiTriShape
@ -175,17 +183,20 @@ namespace Nif
struct NiTriStrips : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};
struct NiLines : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};
struct NiParticles : NiGeometry
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};
struct BSLODTriShape : NiTriBasedGeom
struct BSLODTriShape : NiTriShape
{
std::array<uint32_t, 3> mLOD;
void read(NIFStream* nif) override;

@ -6,22 +6,15 @@
#include <variant>
#include <vector>
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <components/debug/debuglog.hpp>
#include <components/files/conversion.hpp>
#include <components/misc/convert.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/nif/data.hpp>
#include <components/nif/extra.hpp>
#include <components/nif/nifstream.hpp>
#include <components/nif/node.hpp>
#include <components/nif/parent.hpp>
#include <components/files/conversion.hpp>
namespace
{
@ -32,111 +25,6 @@ namespace
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
}
bool isTypeNiGeometry(int type)
{
switch (type)
{
case Nif::RC_NiTriShape:
case Nif::RC_NiTriStrips:
case Nif::RC_BSLODTriShape:
case Nif::RC_BSSegmentedTriShape:
return true;
}
return false;
}
bool isTypeTriShape(int type)
{
switch (type)
{
case Nif::RC_NiTriShape:
case Nif::RC_BSLODTriShape:
case Nif::RC_BSSegmentedTriShape:
return true;
}
return false;
}
void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data)
{
// FIXME: copying vertices/indices individually is unreasonable
const std::vector<osg::Vec3f>& vertices = data.mVertices;
mesh.preallocateVertices(static_cast<int>(vertices.size()));
for (const osg::Vec3f& vertex : vertices)
mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);
mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3);
}
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data)
{
prepareTriangleMesh(mesh, data);
const std::vector<unsigned short>& triangles = data.mTriangles;
for (std::size_t i = 0; i < triangles.size(); i += 3)
mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]);
}
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data)
{
prepareTriangleMesh(mesh, data);
for (const std::vector<unsigned short>& strip : data.mStrips)
{
if (strip.size() < 3)
continue;
unsigned short a;
unsigned short b = strip[0];
unsigned short c = strip[1];
for (size_t i = 2; i < strip.size(); i++)
{
a = b;
b = c;
c = strip[i];
if (a == b || b == c || a == c)
continue;
if (i % 2 == 0)
mesh.addTriangleIndices(a, b, c);
else
mesh.addTriangleIndices(a, c, b);
}
}
}
template <class Function>
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.mData.get())))
{
if (isTypeTriShape(geometry.recType))
{
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.mData.getPtr());
if (data->mTriangles.empty())
return {};
return function(static_cast<const Nif::NiTriShapeData&>(*data));
}
if (geometry.recType == Nif::RC_NiTriStrips)
{
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.mData.getPtr());
if (data->mStrips.empty())
return {};
return function(static_cast<const Nif::NiTriStripsData&>(*data));
}
return {};
}
std::unique_ptr<btTriangleMesh> makeChildMesh(const Nif::NiGeometry& geometry)
{
return handleNiGeometry(geometry, [&](const auto& data) {
auto mesh = std::make_unique<btTriangleMesh>();
fillTriangleMesh(*mesh, data);
return mesh;
});
}
}
namespace NifBullet
@ -336,8 +224,8 @@ namespace NifBullet
return;
// Otherwise we'll want to notify the user.
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << mShape->mFileName
<< ". Treating it as a common NiTriShape.";
Log(Debug::Info) << "BulletNifLoader: RootCollisionNode is not attached to the root node in "
<< mShape->mFileName << ". Treating it as a NiNode.";
}
else
{
@ -349,8 +237,12 @@ namespace NifBullet
if (node.recType == Nif::RC_AvoidNode)
args.mAvoid = true;
if ((args.mAutogenerated || args.mIsCollisionNode) && isTypeNiGeometry(node.recType))
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, args);
if (args.mAutogenerated || args.mIsCollisionNode)
{
auto geometry = dynamic_cast<const Nif::NiGeometry*>(&node);
if (geometry)
handleGeometry(*geometry, parent, args);
}
// For NiNodes, loop through children
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
@ -367,7 +259,7 @@ namespace NifBullet
}
}
void BulletNifLoader::handleNiTriShape(
void BulletNifLoader::handleGeometry(
const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent, HandleNodeArgs args)
{
// This flag comes from BSXFlags
@ -378,20 +270,14 @@ namespace NifBullet
if (args.mHasTriMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "Tri EditorMarker"))
return;
if (niGeometry.mData.empty() || niGeometry.mData->mVertices.empty())
return;
if (!niGeometry.mSkin.empty())
args.mAnimated = false;
// TODO: handle NiSkinPartition
std::unique_ptr<btTriangleMesh> childMesh = makeChildMesh(niGeometry);
if (childMesh == nullptr || childMesh->getNumTriangles() == 0)
std::unique_ptr<btCollisionShape> childShape = niGeometry.getCollisionShape();
if (childShape == nullptr)
return;
auto childShape = std::make_unique<Resource::TriangleMeshShape>(childMesh.get(), true);
std::ignore = childMesh.release();
osg::Matrixf transform = niGeometry.mTransform.toMatrix();
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
transform *= parent->mNiNode.mTransform.toMatrix();

@ -62,7 +62,7 @@ namespace NifBullet
void handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args);
void handleNode(const Nif::NiAVObject& node, const Nif::Parent* parent, HandleNodeArgs args);
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args);
void handleGeometry(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args);
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mAvoidCompoundShape;

@ -22,7 +22,7 @@ local function useItem(obj, actor, force)
end
end
end
world._runStandardUseAction(obj, actor, force)
world._runStandardUseAction(obj, actor, options.force)
end
return {

@ -22,7 +22,7 @@
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,0">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
<item row="5" column="0">
<widget class="QLabel" name="screenLabel">
<property name="text">

Loading…
Cancel
Save