mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-29 22:20:33 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor
This commit is contained in:
commit
162ac452cf
@ -133,6 +133,7 @@
|
|||||||
Feature #7625: Add some missing console error outputs
|
Feature #7625: Add some missing console error outputs
|
||||||
Feature #7634: Support NiParticleBomb
|
Feature #7634: Support NiParticleBomb
|
||||||
Feature #7652: Sort inactive post processing shaders list properly
|
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 #5896: Do not use deprecated MyGUI properties
|
||||||
Task #7113: Move from std::atoi to std::from_char
|
Task #7113: Move from std::atoi to std::from_char
|
||||||
Task #7117: Replace boost::scoped_array with std::vector
|
Task #7117: Replace boost::scoped_array with std::vector
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "sdlinit.hpp"
|
#include "sdlinit.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/display.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@ -16,22 +17,6 @@
|
|||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
|
||||||
#include <array>
|
#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)
|
Launcher::GraphicsPage::GraphicsPage(QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
@ -117,7 +102,7 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||||||
|
|
||||||
const int width = Settings::video().mResolutionX;
|
const int width = Settings::video().mResolutionX;
|
||||||
const int height = Settings::video().mResolutionY;
|
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);
|
screenComboBox->setCurrentIndex(Settings::video().mScreen);
|
||||||
|
|
||||||
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
||||||
@ -204,7 +189,7 @@ void Launcher::GraphicsPage::saveSettings()
|
|||||||
int cHeight = 0;
|
int cHeight = 0;
|
||||||
if (standardRadioButton->isChecked())
|
if (standardRadioButton->isChecked())
|
||||||
{
|
{
|
||||||
QRegularExpression resolutionRe("^(\\d+) x (\\d+)");
|
QRegularExpression resolutionRe("^(\\d+) × (\\d+)");
|
||||||
QRegularExpressionMatch match = resolutionRe.match(resolutionComboBox->currentText().simplified());
|
QRegularExpressionMatch match = resolutionRe.match(resolutionComboBox->currentText().simplified());
|
||||||
if (match.hasMatch())
|
if (match.hasMatch())
|
||||||
{
|
{
|
||||||
@ -304,19 +289,8 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
|
auto str = Misc::getResolutionText(mode.w, mode.h, "%i × %i (%i:%i)");
|
||||||
|
result.append(QString(str.c_str()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.removeDuplicates();
|
result.removeDuplicates();
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <numeric>
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#include <unicode/locid.h>
|
#include <unicode/locid.h>
|
||||||
@ -21,6 +20,7 @@
|
|||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/lua_ui/scriptsettings.hpp>
|
#include <components/lua_ui/scriptsettings.hpp>
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/misc/display.hpp>
|
||||||
#include <components/misc/pathhelpers.hpp>
|
#include <components/misc/pathhelpers.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/misc/strings/format.hpp>
|
#include <components/misc/strings/format.hpp>
|
||||||
@ -93,20 +93,6 @@ namespace
|
|||||||
return left.first > right.first;
|
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 checkButtonType = "CheckButton";
|
||||||
const std::string_view sliderType = "Slider";
|
const std::string_view sliderType = "Slider";
|
||||||
|
|
||||||
@ -366,11 +352,7 @@ namespace MWGui
|
|||||||
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
|
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
|
||||||
for (std::pair<int, int>& resolution : resolutions)
|
for (std::pair<int, int>& resolution : resolutions)
|
||||||
{
|
{
|
||||||
std::string str
|
std::string str = Misc::getResolutionText(resolution.first, resolution.second, "%i x %i (%i:%i)");
|
||||||
= 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 + ")";
|
|
||||||
|
|
||||||
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
|
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
|
||||||
mResolutionList->addItem(str);
|
mResolutionList->addItem(str);
|
||||||
|
@ -417,20 +417,23 @@ namespace MWLua
|
|||||||
|
|
||||||
using DelayedRemovalFn = std::function<void(MWWorld::Ptr)>;
|
using DelayedRemovalFn = std::function<void(MWWorld::Ptr)>;
|
||||||
auto removeFn = [](const MWWorld::Ptr ptr, int countToRemove) -> std::optional<DelayedRemovalFn> {
|
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)
|
if (countToRemove <= 0 || countToRemove > currentCount)
|
||||||
throw std::runtime_error("Can't remove " + std::to_string(countToRemove) + " of "
|
throw std::runtime_error("Can't remove " + std::to_string(countToRemove) + " of "
|
||||||
+ std::to_string(currentCount) + " items");
|
+ 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)
|
if (!ptr.getContainerStore() && currentCount > countToRemove)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
// Delayed action to trigger side effects
|
// Delayed action to trigger side effects
|
||||||
return [countToRemove](MWWorld::Ptr ptr) {
|
return [signedCountToRemove](MWWorld::Ptr ptr) {
|
||||||
// Restore the original count
|
// Restore the original count
|
||||||
ptr.getRefData().setCount(ptr.getRefData().getCount() + countToRemove);
|
ptr.getRefData().setCount(ptr.getRefData().getCount(false) + signedCountToRemove);
|
||||||
// And now remove properly
|
// And now remove properly
|
||||||
if (ptr.getContainerStore())
|
if (ptr.getContainerStore())
|
||||||
ptr.getContainerStore()->remove(ptr, countToRemove, false);
|
ptr.getContainerStore()->remove(ptr, std::abs(signedCountToRemove), false);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->disable(ptr);
|
MWBase::Environment::get().getWorld()->disable(ptr);
|
||||||
|
@ -273,7 +273,7 @@ add_component_dir (esm4
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (misc
|
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
|
guarded math mathutil messageformatparser notnullptr objectpool osguservalues progressreporter resourcehelpers rng
|
||||||
strongtypedef thread timeconvert timer tuplehelpers tuplemeta utf8stream weakcache windows
|
strongtypedef thread timeconvert timer tuplehelpers tuplemeta utf8stream weakcache windows
|
||||||
)
|
)
|
||||||
|
@ -99,7 +99,7 @@ namespace LuaUi
|
|||||||
if (parent)
|
if (parent)
|
||||||
{
|
{
|
||||||
auto children = parent->children();
|
auto children = parent->children();
|
||||||
std::remove(children.begin(), children.end(), root);
|
std::erase(children, root);
|
||||||
parent->setChildren(children);
|
parent->setChildren(children);
|
||||||
root->widget()->detachFromWidget();
|
root->widget()->detachFromWidget();
|
||||||
}
|
}
|
||||||
|
82
components/misc/display.cpp
Normal file
82
components/misc/display.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
11
components/misc/display.hpp
Normal file
11
components/misc/display.hpp
Normal file
@ -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 <cstdint>
|
||||||
|
|
||||||
|
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
||||||
|
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
|
#include <components/resource/bulletshape.hpp>
|
||||||
|
|
||||||
#include "data.hpp"
|
#include "data.hpp"
|
||||||
#include "exception.hpp"
|
#include "exception.hpp"
|
||||||
#include "physics.hpp"
|
#include "physics.hpp"
|
||||||
#include "property.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
|
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)
|
void BSSegmentedTriShape::SegmentData::read(NIFStream* nif)
|
||||||
{
|
{
|
||||||
nif->read(mFlags);
|
nif->read(mFlags);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "base.hpp"
|
#include "base.hpp"
|
||||||
|
|
||||||
|
class btCollisionShape;
|
||||||
|
|
||||||
namespace Nif
|
namespace Nif
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -146,6 +148,11 @@ namespace Nif
|
|||||||
|
|
||||||
void read(NIFStream* nif) override;
|
void read(NIFStream* nif) override;
|
||||||
void post(Reader& 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
|
// Abstract triangle-based geometry
|
||||||
@ -155,6 +162,7 @@ namespace Nif
|
|||||||
|
|
||||||
struct NiTriShape : NiTriBasedGeom
|
struct NiTriShape : NiTriBasedGeom
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BSSegmentedTriShape : NiTriShape
|
struct BSSegmentedTriShape : NiTriShape
|
||||||
@ -175,17 +183,20 @@ namespace Nif
|
|||||||
|
|
||||||
struct NiTriStrips : NiTriBasedGeom
|
struct NiTriStrips : NiTriBasedGeom
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NiLines : NiTriBasedGeom
|
struct NiLines : NiTriBasedGeom
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NiParticles : NiGeometry
|
struct NiParticles : NiGeometry
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BSLODTriShape : NiTriBasedGeom
|
struct BSLODTriShape : NiTriShape
|
||||||
{
|
{
|
||||||
std::array<uint32_t, 3> mLOD;
|
std::array<uint32_t, 3> mLOD;
|
||||||
void read(NIFStream* nif) override;
|
void read(NIFStream* nif) override;
|
||||||
|
@ -6,22 +6,15 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/files/conversion.hpp>
|
||||||
#include <components/misc/convert.hpp>
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
|
|
||||||
#include <components/nif/data.hpp>
|
|
||||||
#include <components/nif/extra.hpp>
|
#include <components/nif/extra.hpp>
|
||||||
#include <components/nif/nifstream.hpp>
|
#include <components/nif/nifstream.hpp>
|
||||||
#include <components/nif/node.hpp>
|
#include <components/nif/node.hpp>
|
||||||
#include <components/nif/parent.hpp>
|
#include <components/nif/parent.hpp>
|
||||||
|
|
||||||
#include <components/files/conversion.hpp>
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -32,111 +25,6 @@ namespace
|
|||||||
return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X');
|
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
|
namespace NifBullet
|
||||||
@ -336,8 +224,8 @@ namespace NifBullet
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Otherwise we'll want to notify the user.
|
// Otherwise we'll want to notify the user.
|
||||||
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << mShape->mFileName
|
Log(Debug::Info) << "BulletNifLoader: RootCollisionNode is not attached to the root node in "
|
||||||
<< ". Treating it as a common NiTriShape.";
|
<< mShape->mFileName << ". Treating it as a NiNode.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -349,8 +237,12 @@ namespace NifBullet
|
|||||||
if (node.recType == Nif::RC_AvoidNode)
|
if (node.recType == Nif::RC_AvoidNode)
|
||||||
args.mAvoid = true;
|
args.mAvoid = true;
|
||||||
|
|
||||||
if ((args.mAutogenerated || args.mIsCollisionNode) && isTypeNiGeometry(node.recType))
|
if (args.mAutogenerated || args.mIsCollisionNode)
|
||||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, args);
|
{
|
||||||
|
auto geometry = dynamic_cast<const Nif::NiGeometry*>(&node);
|
||||||
|
if (geometry)
|
||||||
|
handleGeometry(*geometry, parent, args);
|
||||||
|
}
|
||||||
|
|
||||||
// For NiNodes, loop through children
|
// For NiNodes, loop through children
|
||||||
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
|
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)
|
const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent, HandleNodeArgs args)
|
||||||
{
|
{
|
||||||
// This flag comes from BSXFlags
|
// This flag comes from BSXFlags
|
||||||
@ -378,20 +270,14 @@ namespace NifBullet
|
|||||||
if (args.mHasTriMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "Tri EditorMarker"))
|
if (args.mHasTriMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "Tri EditorMarker"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (niGeometry.mData.empty() || niGeometry.mData->mVertices.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!niGeometry.mSkin.empty())
|
if (!niGeometry.mSkin.empty())
|
||||||
args.mAnimated = false;
|
args.mAnimated = false;
|
||||||
// TODO: handle NiSkinPartition
|
// TODO: handle NiSkinPartition
|
||||||
|
|
||||||
std::unique_ptr<btTriangleMesh> childMesh = makeChildMesh(niGeometry);
|
std::unique_ptr<btCollisionShape> childShape = niGeometry.getCollisionShape();
|
||||||
if (childMesh == nullptr || childMesh->getNumTriangles() == 0)
|
if (childShape == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto childShape = std::make_unique<Resource::TriangleMeshShape>(childMesh.get(), true);
|
|
||||||
std::ignore = childMesh.release();
|
|
||||||
|
|
||||||
osg::Matrixf transform = niGeometry.mTransform.toMatrix();
|
osg::Matrixf transform = niGeometry.mTransform.toMatrix();
|
||||||
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
|
for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent)
|
||||||
transform *= parent->mNiNode.mTransform.toMatrix();
|
transform *= parent->mNiNode.mTransform.toMatrix();
|
||||||
|
@ -62,7 +62,7 @@ namespace NifBullet
|
|||||||
|
|
||||||
void handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args);
|
void handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args);
|
||||||
void handleNode(const Nif::NiAVObject& node, const Nif::Parent* parent, 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> mCompoundShape;
|
||||||
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mAvoidCompoundShape;
|
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mAvoidCompoundShape;
|
||||||
|
@ -22,7 +22,7 @@ local function useItem(obj, actor, force)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
world._runStandardUseAction(obj, actor, force)
|
world._runStandardUseAction(obj, actor, options.force)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,0">
|
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="screenLabel">
|
<widget class="QLabel" name="screenLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user