1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-19 19:11:32 +00:00

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

This commit is contained in:
Zackhasacat 2023-09-04 10:49:51 -05:00
commit f287b2f436
59 changed files with 1835 additions and 932 deletions

View file

@ -70,6 +70,7 @@
Bug #7472: Crash when enchanting last projectiles Bug #7472: Crash when enchanting last projectiles
Bug #7505: Distant terrain does not support sample size greater than cell size Bug #7505: Distant terrain does not support sample size greater than cell size
Bug #7553: Faction reaction loading is incorrect Bug #7553: Faction reaction loading is incorrect
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
Feature #3537: Shader-based water ripples Feature #3537: Shader-based water ripples
Feature #5492: Let rain and snow collide with statics Feature #5492: Let rain and snow collide with statics
Feature #6149: Dehardcode Lua API_REVISION Feature #6149: Dehardcode Lua API_REVISION
@ -77,6 +78,7 @@
Feature #6491: Add support for Qt6 Feature #6491: Add support for Qt6
Feature #6556: Lua API for sounds Feature #6556: Lua API for sounds
Feature #6726: Lua API for creating new objects Feature #6726: Lua API for creating new objects
Feature #6864: Lua file access API
Feature #6922: Improve launcher appearance Feature #6922: Improve launcher appearance
Feature #6933: Support high-resolution cursor textures Feature #6933: Support high-resolution cursor textures
Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData

View file

@ -71,7 +71,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 45) set(OPENMW_LUA_API_REVISION 46)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION_TAGHASH "")

View file

@ -61,7 +61,7 @@ add_openmw_dir (mwscript
add_openmw_dir (mwlua add_openmw_dir (mwlua
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
camerabindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal
worker magicbindings worker magicbindings
) )

View file

@ -2,6 +2,7 @@
#include <MyGUI_TextIterator.h> #include <MyGUI_TextIterator.h>
#include <cassert>
#include <memory> #include <memory>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
@ -93,8 +94,10 @@ namespace
int level = creatureStats.getLevel(); int level = creatureStats.getLevel();
for (const ESM::Attribute& attribute : attributes) for (const ESM::Attribute& attribute : attributes)
{ {
const ESM::Race::MaleFemale& value auto index = ESM::Attribute::refIdToIndex(attribute.mId);
= race->mData.mAttributeValues[static_cast<size_t>(ESM::Attribute::refIdToIndex(attribute.mId))]; assert(index >= 0);
const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[static_cast<size_t>(index)];
creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }

View file

@ -44,6 +44,7 @@
#include "soundbindings.hpp" #include "soundbindings.hpp"
#include "types/types.hpp" #include "types/types.hpp"
#include "uibindings.hpp" #include "uibindings.hpp"
#include "vfsbindings.hpp"
namespace MWLua namespace MWLua
{ {
@ -347,6 +348,7 @@ namespace MWLua
{ "openmw.core", initCorePackage(context) }, { "openmw.core", initCorePackage(context) },
{ "openmw.types", initTypesPackage(context) }, { "openmw.types", initTypesPackage(context) },
{ "openmw.util", LuaUtil::initUtilPackage(lua) }, { "openmw.util", LuaUtil::initUtilPackage(lua) },
{ "openmw.vfs", initVFSPackage(context) },
}; };
} }

View file

@ -0,0 +1,346 @@
#include "vfsbindings.hpp"
#include <components/files/istreamptr.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/settings/values.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/pathutil.hpp>
#include "../mwbase/environment.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
namespace MWLua
{
namespace
{
// Too many arguments may cause stack corruption and crash.
constexpr std::size_t sMaximumReadArguments = 20;
// Print a message if we read a large chunk of file to string.
constexpr std::size_t sFileSizeWarningThreshold = 1024 * 1024;
struct FileHandle
{
public:
FileHandle(Files::IStreamPtr stream, std::string_view fileName)
{
mFilePtr = std::move(stream);
mFileName = fileName;
}
Files::IStreamPtr mFilePtr;
std::string mFileName;
};
std::ios_base::seekdir getSeekDir(FileHandle& self, std::string_view whence)
{
if (whence == "cur")
return std::ios_base::cur;
if (whence == "set")
return std::ios_base::beg;
if (whence == "end")
return std::ios_base::end;
throw std::runtime_error(
"Error when handling '" + self.mFileName + "': invalid seek direction: '" + std::string(whence) + "'.");
}
size_t getBytesLeftInStream(Files::IStreamPtr& file)
{
auto oldPos = file->tellg();
file->seekg(0, std::ios_base::end);
auto newPos = file->tellg();
file->seekg(oldPos, std::ios_base::beg);
return newPos - oldPos;
}
void printLargeDataMessage(FileHandle& file, size_t size)
{
if (!file.mFilePtr || !Settings::lua().mLuaDebug || size < sFileSizeWarningThreshold)
return;
Log(Debug::Verbose) << "Read a large data chunk (" << size << " bytes) from '" << file.mFileName << "'.";
}
sol::object readFile(LuaUtil::LuaState* lua, FileHandle& file)
{
std::ostringstream os;
if (file.mFilePtr && file.mFilePtr->peek() != EOF)
os << file.mFilePtr->rdbuf();
auto result = os.str();
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), std::move(result));
}
sol::object readLineFromFile(LuaUtil::LuaState* lua, FileHandle& file)
{
std::string result;
if (file.mFilePtr && std::getline(*file.mFilePtr, result))
{
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result);
}
return sol::nil;
}
sol::object readNumberFromFile(LuaUtil::LuaState* lua, Files::IStreamPtr& file)
{
double number = 0;
if (file && *file >> number)
return sol::make_object<double>(lua->sol(), number);
return sol::nil;
}
sol::object readCharactersFromFile(LuaUtil::LuaState* lua, FileHandle& file, size_t count)
{
if (count <= 0 && file.mFilePtr->peek() != EOF)
return sol::make_object<std::string>(lua->sol(), std::string());
auto bytesLeft = getBytesLeftInStream(file.mFilePtr);
if (bytesLeft <= 0)
return sol::nil;
if (count > bytesLeft)
count = bytesLeft;
std::string result(count, '\0');
if (file.mFilePtr->read(&result[0], count))
{
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result);
}
return sol::nil;
}
void validateFile(const FileHandle& self)
{
if (self.mFilePtr)
return;
throw std::runtime_error("Error when handling '" + self.mFileName + "': attempt to use a closed file.");
}
sol::variadic_results seek(
LuaUtil::LuaState* lua, FileHandle& self, std::ios_base::seekdir dir, std::streamoff off)
{
sol::variadic_results values;
try
{
self.mFilePtr->seekg(off, dir);
if (self.mFilePtr->fail() || self.mFilePtr->bad())
{
auto msg = "Failed to seek in file '" + self.mFileName + "'";
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
else
values.push_back(sol::make_object<std::streampos>(lua->sol(), self.mFilePtr->tellg()));
}
catch (std::exception& e)
{
auto msg = "Failed to seek in file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
return values;
}
}
sol::table initVFSPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
sol::usertype<FileHandle> handle = context.mLua->sol().new_usertype<FileHandle>("FileHandle");
handle["fileName"] = sol::readonly_property([](const FileHandle& self) { return self.mFileName; });
handle[sol::meta_function::to_string] = [](const FileHandle& self) {
return "FileHandle{'" + self.mFileName + "'" + (!self.mFilePtr ? ", closed" : "") + "}";
};
handle["seek"] = sol::overload(
[lua = context.mLua](FileHandle& self, std::string_view whence, sol::optional<long> offset) {
validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0));
auto dir = getSeekDir(self, whence);
return seek(lua, self, dir, off);
},
[lua = context.mLua](FileHandle& self, sol::optional<long> offset) {
validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0));
return seek(lua, self, std::ios_base::cur, off);
});
handle["lines"] = [lua = context.mLua](FileHandle& self) {
return sol::as_function([&lua, &self]() mutable {
validateFile(self);
return readLineFromFile(lua, self);
});
};
api["lines"] = [lua = context.mLua, vfs](std::string_view fileName) {
auto normalizedName = VFS::Path::normalizeFilename(fileName);
return sol::as_function(
[lua, file = FileHandle(vfs->getNormalized(normalizedName), normalizedName)]() mutable {
validateFile(file);
auto result = readLineFromFile(lua, file);
if (result == sol::nil)
file.mFilePtr.reset();
return result;
});
};
handle["close"] = [lua = context.mLua](FileHandle& self) {
sol::variadic_results values;
try
{
self.mFilePtr.reset();
if (self.mFilePtr)
{
auto msg = "Can not close file '" + self.mFileName + "': file handle is still opened.";
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
else
values.push_back(sol::make_object<bool>(lua->sol(), true));
}
catch (std::exception& e)
{
auto msg = "Can not close file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
return values;
};
handle["read"] = [lua = context.mLua](FileHandle& self, const sol::variadic_args args) {
validateFile(self);
if (args.size() > sMaximumReadArguments)
throw std::runtime_error(
"Error when handling '" + self.mFileName + "': too many arguments for 'read'.");
sol::variadic_results values;
// If there are no arguments, read a string
if (args.size() == 0)
{
values.push_back(readLineFromFile(lua, self));
return values;
}
bool success = true;
size_t i = 0;
for (i = 0; i < args.size() && success; i++)
{
if (args[i].is<std::string_view>())
{
auto format = args[i].as<std::string_view>();
if (format == "*a" || format == "*all")
{
values.push_back(readFile(lua, self));
continue;
}
if (format == "*n" || format == "*number")
{
auto result = readNumberFromFile(lua, self.mFilePtr);
values.push_back(result);
if (result == sol::nil)
success = false;
continue;
}
if (format == "*l" || format == "*line")
{
auto result = readLineFromFile(lua, self);
values.push_back(result);
if (result == sol::nil)
success = false;
continue;
}
throw std::runtime_error("Error when handling '" + self.mFileName + "': bad argument #"
+ std::to_string(i + 1) + " to 'read' (invalid format)");
}
else if (args[i].is<int>())
{
int number = args[i].as<int>();
auto result = readCharactersFromFile(lua, self, number);
values.push_back(result);
if (result == sol::nil)
success = false;
}
}
// We should return nil if we just reached the end of stream
if (!success && self.mFilePtr->eof())
return values;
if (!success && (self.mFilePtr->fail() || self.mFilePtr->bad()))
{
auto msg = "Error when handling '" + self.mFileName + "': can not read data for argument #"
+ std::to_string(i);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
return values;
};
api["open"] = [lua = context.mLua, vfs](std::string_view fileName) {
sol::variadic_results values;
try
{
auto normalizedName = VFS::Path::normalizeFilename(fileName);
auto handle = FileHandle(vfs->getNormalized(normalizedName), normalizedName);
values.push_back(sol::make_object<FileHandle>(lua->sol(), std::move(handle)));
}
catch (std::exception& e)
{
auto msg = "Can not open file: " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
}
return values;
};
api["type"] = sol::overload(
[](const FileHandle& handle) -> std::string {
if (handle.mFilePtr)
return "file";
return "closed file";
},
[](const sol::object&) -> sol::object { return sol::nil; });
api["fileExists"] = [vfs](std::string_view fileName) -> bool { return vfs->exists(fileName); };
api["pathsWithPrefix"] = [vfs](std::string_view prefix) {
auto iterator = vfs->getRecursiveDirectoryIterator(prefix);
return sol::as_function([iterator, current = iterator.begin()]() mutable -> sol::optional<std::string> {
if (current != iterator.end())
{
const std::string& result = *current;
++current;
return result;
}
return sol::nullopt;
});
};
return LuaUtil::makeReadOnly(api);
}
}

View file

@ -0,0 +1,13 @@
#ifndef MWLUA_VFSBINDINGS_H
#define MWLUA_VFSBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initVFSPackage(const Context&);
}
#endif // MWLUA_VFSBINDINGS_H

View file

@ -48,9 +48,9 @@ namespace MWMechanics
std::string EffectKey::toString() const std::string EffectKey::toString() const
{ {
const auto& store = MWBase::Environment::get().getESMStore(); const auto& store = MWBase::Environment::get().getESMStore();
const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().search(mId); const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().find(mId);
return getMagicEffectString( return getMagicEffectString(
*magicEffect, store->get<ESM::Attribute>().search(mArg), store->get<ESM::Skill>().search(mArg)); *magicEffect, store->get<ESM::Attribute>().find(mArg), store->get<ESM::Skill>().find(mArg));
} }
bool operator<(const EffectKey& left, const EffectKey& right) bool operator<(const EffectKey& left, const EffectKey& right)

View file

@ -1,5 +1,7 @@
#include "mechanicsmanagerimp.hpp" #include "mechanicsmanagerimp.hpp"
#include <cassert>
#include <osg/Stats> #include <osg/Stats>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
@ -150,9 +152,10 @@ namespace MWMechanics
for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>()) for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>())
{ {
const ESM::Race::MaleFemale& value auto index = ESM::Attribute::refIdToIndex(attribute.mId);
= race->mData.mAttributeValues[static_cast<size_t>(ESM::Attribute::refIdToIndex(attribute.mId))]; assert(index >= 0);
const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[static_cast<size_t>(index)];
creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }

View file

@ -488,7 +488,12 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
state.mSkillIncrease.fill(0); state.mSkillIncrease.fill(0);
for (const auto& [key, value] : mSkillIncreases) for (const auto& [key, value] : mSkillIncreases)
state.mSkillIncrease[static_cast<size_t>(ESM::Attribute::refIdToIndex(key))] = value; {
// TODO extend format
auto index = ESM::Attribute::refIdToIndex(key);
assert(index >= 0);
state.mSkillIncrease[static_cast<size_t>(index)] = value;
}
for (size_t i = 0; i < state.mSpecIncreases.size(); ++i) for (size_t i = 0; i < state.mSpecIncreases.size(); ++i)
state.mSpecIncreases[i] = mSpecIncreases[i]; state.mSpecIncreases[i] = mSpecIncreases[i];

View file

@ -556,7 +556,7 @@ namespace MWWorld
return false; return false;
} }
CellStore::CellStore(MWWorld::Cell cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers) CellStore::CellStore(MWWorld::Cell&& cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers)
: mStore(esmStore) : mStore(esmStore)
, mReaders(readers) , mReaders(readers)
, mCellVariant(std::move(cell)) , mCellVariant(std::move(cell))

View file

@ -140,7 +140,7 @@ namespace MWWorld
} }
/// @param readerList The readers to use for loading of the cell on-demand. /// @param readerList The readers to use for loading of the cell on-demand.
CellStore(MWWorld::Cell cell, const MWWorld::ESMStore& store, ESM::ReadersCache& readers); CellStore(MWWorld::Cell&& cell, const MWWorld::ESMStore& store, ESM::ReadersCache& readers);
CellStore(const CellStore&) = delete; CellStore(const CellStore&) = delete;

View file

@ -159,9 +159,16 @@ MWWorld::ContainerStore::ContainerStore()
MWWorld::ContainerStore::~ContainerStore() MWWorld::ContainerStore::~ContainerStore()
{ {
MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel(); try
for (MWWorld::ContainerStoreIterator iter(begin()); iter != end(); ++iter) {
worldModel->deregisterPtr(*iter); MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel();
for (MWWorld::ContainerStoreIterator iter(begin()); iter != end(); ++iter)
worldModel->deregisterPtr(*iter);
}
catch (const std::exception& e)
{
Log(Debug::Error) << "Failed to deregister container store: " << e.what();
}
} }
MWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::cbegin(int mask) const MWWorld::ConstContainerStoreIterator MWWorld::ContainerStore::cbegin(int mask) const

View file

@ -834,9 +834,6 @@ namespace MWWorld
mPreloader = std::make_unique<CellPreloader>(rendering.getResourceSystem(), physics->getShapeManager(), mPreloader = std::make_unique<CellPreloader>(rendering.getResourceSystem(), physics->getShapeManager(),
rendering.getTerrain(), rendering.getLandManager()); rendering.getTerrain(), rendering.getLandManager());
mPreloader->setWorkQueue(mRendering.getWorkQueue()); mPreloader->setWorkQueue(mRendering.getWorkQueue());
rendering.getResourceSystem()->setExpiryDelay(Settings::cells().mCacheExpiryDelay);
mPreloader->setExpiryDelay(Settings::cells().mPreloadCellExpiryDelay); mPreloader->setExpiryDelay(Settings::cells().mPreloadCellExpiryDelay);
mPreloader->setMinCacheSize(Settings::cells().mPreloadCellCacheMin); mPreloader->setMinCacheSize(Settings::cells().mPreloadCellCacheMin);
mPreloader->setMaxCacheSize(Settings::cells().mPreloadCellCacheMax); mPreloader->setMaxCacheSize(Settings::cells().mPreloadCellCacheMax);

View file

@ -1017,11 +1017,12 @@ namespace MWWorld
void Store<ESM::GameSetting>::setUp() void Store<ESM::GameSetting>::setUp()
{ {
auto addSetting = [&](const std::string& key, ESM::Variant value) { auto addSetting = [&](const std::string& key, ESM::Variant value) {
auto id = ESM::RefId::stringRefId(key);
ESM::GameSetting setting; ESM::GameSetting setting;
setting.blank(); setting.blank();
setting.mId = ESM::RefId::stringRefId(key); setting.mId = id;
setting.mValue = std::move(value); setting.mValue = std::move(value);
auto [iter, inserted] = mStatic.insert_or_assign(setting.mId, std::move(setting)); auto [iter, inserted] = mStatic.insert_or_assign(id, std::move(setting));
if (inserted) if (inserted)
mShared.push_back(&iter->second); mShared.push_back(&iter->second);
}; };

View file

@ -13,7 +13,7 @@ namespace Nif::Testing
inline void init(Extra& value) inline void init(Extra& value)
{ {
value.next = ExtraPtr(nullptr); value.mNext = ExtraPtr(nullptr);
} }
inline void init(Named& value) inline void init(Named& value)

View file

@ -996,7 +996,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision) for_tri_shape_child_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
{ {
mNiStringExtraData.string = "NCC__"; mNiStringExtraData.mData = "NCC__";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1024,8 +1024,8 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision) for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
{ {
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NCC__"; mNiStringExtraData2.mData = "NCC__";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1052,7 +1052,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision) for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
{ {
mNiStringExtraData.string = "NC___"; mNiStringExtraData.mData = "NC___";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1079,8 +1079,8 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision) for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
{ {
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NC___"; mNiStringExtraData2.mData = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1141,7 +1141,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape) for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)
{ {
mNiStringExtraData.string = "MRK"; mNiStringExtraData.mData = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1160,7 +1160,7 @@ namespace
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers) TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers)
{ {
mNiIntegerExtraData.data = 32; // BSX flag "editor marker" mNiIntegerExtraData.mData = 32; // BSX flag "editor marker"
mNiIntegerExtraData.recType = Nif::RC_BSXFlags; mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData)); mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
mNiTriShape.parents.push_back(&mNiNode); mNiTriShape.parents.push_back(&mNiNode);
@ -1181,7 +1181,7 @@ namespace
TEST_F(TestBulletNifLoader, TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes) for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
{ {
mNiStringExtraData.string = "MRK"; mNiStringExtraData.mData = "MRK";
mNiStringExtraData.recType = Nif::RC_NiStringExtraData; mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode2); mNiTriShape.parents.push_back(&mNiNode2);

View file

@ -42,7 +42,7 @@ list (APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${VERSION_CPP_FILE}")
# source files # source files
add_component_dir (lua add_component_dir (lua
luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8
shapes/box shapes/box
) )
@ -111,7 +111,7 @@ add_component_dir (sceneutil
) )
add_component_dir (nif add_component_dir (nif
controlled effect niftypes record controller extra node record_ptr data niffile property nifkey base nifstream physics base controller data effect extra niffile nifkey nifstream niftypes node particle physics property record record_ptr texture
) )
add_component_dir (nifosg add_component_dir (nifosg

View file

@ -12,6 +12,7 @@
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include "scriptscontainer.hpp" #include "scriptscontainer.hpp"
#include "utf8.hpp"
namespace LuaUtil namespace LuaUtil
{ {
@ -51,7 +52,7 @@ namespace LuaUtil
static const std::string safeFunctions[] = { "assert", "error", "ipairs", "next", "pairs", "pcall", "select", static const std::string safeFunctions[] = { "assert", "error", "ipairs", "next", "pairs", "pcall", "select",
"tonumber", "tostring", "type", "unpack", "xpcall", "rawequal", "rawget", "rawset", "setmetatable" }; "tonumber", "tostring", "type", "unpack", "xpcall", "rawequal", "rawget", "rawset", "setmetatable" };
static const std::string safePackages[] = { "coroutine", "math", "string", "table" }; static const std::string safePackages[] = { "coroutine", "math", "string", "table", "utf8" };
static constexpr int64_t countHookStep = 1000; static constexpr int64_t countHookStep = 1000;
@ -181,6 +182,8 @@ namespace LuaUtil
mSol["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr))); mSol["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
mSol["math"]["randomseed"] = [] {}; mSol["math"]["randomseed"] = [] {};
mSol["utf8"] = LuaUtf8::initUtf8Package(mSol);
mSol["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; }; mSol["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
mSol["setEnvironment"] mSol["setEnvironment"]

233
components/lua/utf8.cpp Normal file
View file

@ -0,0 +1,233 @@
#include <codecvt>
#include <components/misc/strings/format.hpp>
#include "utf8.hpp"
namespace
{
constexpr std::string_view UTF8PATT = "[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*"; // %z is deprecated in Lua5.2
constexpr uint32_t MAXUTF = 0x7FFFFFFFu;
// constexpr uint32_t MAXUNICODE = 0x10FFFFu;
inline bool isNilOrNone(const sol::stack_proxy arg)
{
return (arg.get_type() == sol::type::lua_nil || arg.get_type() == sol::type::none);
}
inline double getInteger(const sol::stack_proxy arg, const size_t n, std::string_view name)
{
double integer;
if (!arg.is<double>())
throw std::runtime_error(Misc::StringUtils::format("bad argument #%i to '%s' (number expected, got %s)", n,
name, sol::type_name(arg.lua_state(), arg.get_type())));
if (std::modf(arg, &integer) != 0)
throw std::runtime_error(
Misc::StringUtils::format("bad argument #{} to '{}' (number has no integer representation)", n, name));
return integer;
}
// If the input 'pos' is negative, it is treated as counting from the end of the string,
// where -1 represents the last character position, -2 represents the second-to-last position,
// and so on. If 'pos' is non-negative, it is used as-is.
inline void relativePosition(int64_t& pos, const size_t len)
{
if (pos < 0)
pos = std::max<int64_t>(0, pos + len + 1);
}
// returns: first - character pos in bytes, second - character codepoint
std::pair<int64_t, int64_t> decodeNextUTF8Character(std::string_view s, std::vector<int64_t>& pos_byte)
{
const int64_t pos = pos_byte.back() - 1;
const unsigned char ch = static_cast<unsigned char>(s[pos]);
int64_t codepoint = -1;
size_t byteSize = 0;
if ((ch & 0b10000000) == 0)
{
codepoint = ch;
byteSize = 1;
}
else if ((ch & 0b11100000) == 0b11000000)
{
codepoint = ch & 0b00011111;
byteSize = 2;
}
else if ((ch & 0b11110000) == 0b11100000)
{
codepoint = ch & 0b00001111;
byteSize = 3;
}
else if ((ch & 0b11111000) == 0b11110000)
{
codepoint = ch & 0b00000111;
byteSize = 4;
}
// construct codepoint for non-ascii
for (size_t i = 1; i < byteSize; ++i)
{
// if not a continuation byte
if ((pos + i) >= s.size() || (static_cast<unsigned char>(s[pos + i]) & 0b11000000) != 0b10000000)
{
return std::make_pair(0, -1);
}
codepoint = (codepoint << 6) | (static_cast<unsigned char>(s[pos + i]) & 0b00111111);
}
std::pair<size_t, int64_t> res = std::make_pair(pos_byte.back(), codepoint);
pos_byte.push_back(pos_byte.back() + byteSize); /* the next character (if exists) starts at this byte */
return res;
}
}
namespace LuaUtf8
{
sol::table initUtf8Package(sol::state_view& lua)
{
sol::table utf8(lua, sol::create);
utf8["charpattern"] = UTF8PATT;
utf8["char"] = [](const sol::variadic_args args) -> std::string {
std::string result{};
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
for (size_t i = 0; i < args.size(); ++i)
{
int64_t codepoint = getInteger(args[i], (i + 1), "char");
if (codepoint < 0 || codepoint > MAXUTF)
throw std::runtime_error(
Misc::StringUtils::format("bad argument #{} to 'char' (value out of range)", (i + 1)));
result += converter.to_bytes(codepoint);
}
return result;
};
utf8["codes"] = [](std::string_view s) {
std::vector<int64_t> pos_byte{ 1 };
return sol::as_function([s, pos_byte]() mutable -> sol::optional<std::pair<int64_t, int64_t>> {
if (pos_byte.back() <= static_cast<int64_t>(s.size()))
{
const auto pair = decodeNextUTF8Character(s, pos_byte);
if (pair.second == -1)
throw std::runtime_error("Invalid UTF-8 code at position " + std::to_string(pos_byte.size()));
return pair;
}
return sol::nullopt;
});
};
utf8["len"] = [](std::string_view s,
const sol::variadic_args args) -> std::variant<size_t, std::pair<sol::object, int64_t>> {
const size_t len = s.size();
int64_t iv = isNilOrNone(args[0]) ? 1 : getInteger(args[0], 2, "len");
int64_t fv = isNilOrNone(args[1]) ? -1 : getInteger(args[1], 3, "len");
relativePosition(iv, len);
relativePosition(fv, len);
if (iv <= 0)
throw std::runtime_error("bad argument #2 to 'len' (initial position out of bounds)");
if (fv > static_cast<int64_t>(len))
throw std::runtime_error("bad argument #3 to 'len' (final position out of bounds)");
if (len == 0)
return len;
std::vector<int64_t> pos_byte = { iv };
while (pos_byte.back() <= fv)
{
if (decodeNextUTF8Character(s, pos_byte).second == -1)
return std::pair(sol::lua_nil, pos_byte.back());
}
return pos_byte.size() - 1;
};
utf8["codepoint"]
= [](std::string_view s, const sol::variadic_args args) -> sol::as_returns_t<std::vector<int64_t>> {
size_t len = s.size();
int64_t iv = isNilOrNone(args[0]) ? 1 : getInteger(args[0], 2, "codepoint");
int64_t fv = isNilOrNone(args[1]) ? iv : getInteger(args[1], 3, "codepoint");
relativePosition(iv, len);
relativePosition(fv, len);
if (iv <= 0)
throw std::runtime_error("bad argument #2 to 'codepoint' (initial position out of bounds)");
if (fv > static_cast<int64_t>(len))
throw std::runtime_error("bad argument #3 to 'codepoint' (final position out of bounds)");
if (iv > fv)
return sol::as_returns(std::vector<int64_t>{}); /* empty interval; return nothing */
std::vector<int64_t> pos_byte = { iv };
std::vector<int64_t> codepoints;
while (pos_byte.back() <= fv)
{
codepoints.push_back(decodeNextUTF8Character(s, pos_byte).second);
if (codepoints.back() == -1)
throw std::runtime_error("Invalid UTF-8 code at position " + std::to_string(pos_byte.size()));
}
return sol::as_returns(std::move(codepoints));
};
utf8["offset"]
= [](std::string_view s, const int64_t n, const sol::variadic_args args) -> sol::optional<int64_t> {
size_t len = s.size();
int64_t iv;
if (isNilOrNone(args[0]))
{
if (n >= 0)
iv = 1;
else
iv = s.size() + 1;
}
else
iv = getInteger(args[0], 3, "offset");
std::vector<int64_t> pos_byte = { 1 };
relativePosition(iv, len);
if (iv > static_cast<int64_t>(len) + 1)
throw std::runtime_error("bad argument #3 to 'offset' (position out of bounds)");
while (pos_byte.back() <= static_cast<int64_t>(len))
decodeNextUTF8Character(s, pos_byte);
for (auto it = pos_byte.begin(); it != pos_byte.end(); ++it)
{
if (*it == iv)
{
if (n <= 0 && it + n >= pos_byte.begin())
return *(it + n);
if (n > 0 && it + n - 1 < pos_byte.end())
return *(it + n - 1);
break;
}
else if (*it > iv) /* a continuation byte */
{
if (n == 0)
return *(it - 1); /* special case */
else
throw std::runtime_error("initial position is a continuation byte");
}
}
return sol::nullopt;
};
return utf8;
}
}

11
components/lua/utf8.hpp Normal file
View file

@ -0,0 +1,11 @@
#ifndef COMPONENTS_LUA_UTF8_H
#define COMPONENTS_LUA_UTF8_H
#include <sol/sol.hpp>
namespace LuaUtf8
{
sol::table initUtf8Package(sol::state_view&);
}
#endif

View file

@ -5,11 +5,11 @@ namespace Nif
void Extra::read(NIFStream* nif) void Extra::read(NIFStream* nif)
{ {
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
name = nif->getString(); nif->read(mName);
else if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) else if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
{ {
next.read(nif); mNext.read(nif);
recordSize = nif->getUInt(); nif->read(mRecordSize);
} }
} }

View file

@ -13,12 +13,12 @@ namespace Nif
// An extra data record. All the extra data connected to an object form a linked list. // An extra data record. All the extra data connected to an object form a linked list.
struct Extra : public Record struct Extra : public Record
{ {
std::string name; std::string mName;
ExtraPtr next; // Next extra data record in the list ExtraPtr mNext; // Next extra data record in the list
unsigned int recordSize{ 0u }; uint32_t mRecordSize{ 0u };
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override { next.post(nif); } void post(Reader& nif) override { mNext.post(nif); }
}; };
struct Controller : public Record struct Controller : public Record

View file

@ -1,140 +0,0 @@
#include "controlled.hpp"
#include "data.hpp"
namespace Nif
{
void NiSourceTexture::read(NIFStream* nif)
{
Named::read(nif);
external = nif->getChar() != 0;
bool internal = false;
if (external)
filename = nif->getString();
else
{
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 3))
internal = nif->getChar();
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
filename = nif->getString(); // Original file path of the internal texture
}
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 3))
{
if (!external && internal)
data.read(nif);
}
else
{
data.read(nif);
}
pixel = nif->getUInt();
mipmap = nif->getUInt();
alpha = nif->getUInt();
// Renderer hints, typically of no use for us
/* bool mIsStatic = */ nif->getChar();
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 103))
/* bool mDirectRendering = */ nif->getBoolean();
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 4))
/* bool mPersistRenderData = */ nif->getBoolean();
}
void NiSourceTexture::post(Reader& nif)
{
Named::post(nif);
data.post(nif);
}
void BSShaderTextureSet::read(NIFStream* nif)
{
nif->getSizedStrings(textures, nif->getUInt());
}
void NiParticleModifier::read(NIFStream* nif)
{
next.read(nif);
controller.read(nif);
}
void NiParticleModifier::post(Reader& nif)
{
next.post(nif);
controller.post(nif);
}
void NiParticleGrowFade::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
growTime = nif->getFloat();
fadeTime = nif->getFloat();
}
void NiParticleColorModifier::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
data.read(nif);
}
void NiParticleColorModifier::post(Reader& nif)
{
NiParticleModifier::post(nif);
data.post(nif);
}
void NiGravity::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
mDecay = nif->getFloat();
mForce = nif->getFloat();
mType = nif->getUInt();
mPosition = nif->getVector3();
mDirection = nif->getVector3();
}
void NiParticleCollider::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
mBounceFactor = nif->getFloat();
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 0, 2))
{
// Unused in NifSkope. Need to figure out what these do.
/*bool mSpawnOnCollision = */ nif->getBoolean();
/*bool mDieOnCollision = */ nif->getBoolean();
}
}
void NiPlanarCollider::read(NIFStream* nif)
{
NiParticleCollider::read(nif);
mExtents = nif->getVector2();
mPosition = nif->getVector3();
mXVector = nif->getVector3();
mYVector = nif->getVector3();
mPlaneNormal = nif->getVector3();
mPlaneDistance = nif->getFloat();
}
void NiParticleRotation::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
/* bool mRandomInitialAxis = */ nif->getChar();
/* osg::Vec3f mInitialAxis = */ nif->getVector3();
/* float mRotationSpeed = */ nif->getFloat();
}
void NiSphericalCollider::read(NIFStream* nif)
{
NiParticleCollider::read(nif);
mRadius = nif->getFloat();
mCenter = nif->getVector3();
}
}

View file

@ -1,157 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: https://openmw.org/
This file (controlled.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
https://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
#define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
#include "base.hpp"
namespace Nif
{
struct NiSourceTexture : public Named
{
// Is this an external (references a separate texture file) or
// internal (data is inside the nif itself) texture?
bool external;
std::string filename; // In case of external textures
NiPixelDataPtr data; // In case of internal textures
/* Pixel layout
0 - Palettised
1 - High color 16
2 - True color 32
3 - Compressed
4 - Bumpmap
5 - Default */
unsigned int pixel;
/* Mipmap format
0 - no
1 - yes
2 - default */
unsigned int mipmap;
/* Alpha
0 - none
1 - binary
2 - smooth
3 - default (use material alpha, or multiply material with texture if present)
*/
unsigned int alpha;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct BSShaderTextureSet : public Record
{
enum TextureType
{
TextureType_Base = 0,
TextureType_Normal = 1,
TextureType_Glow = 2,
TextureType_Parallax = 3,
TextureType_Env = 4,
TextureType_EnvMask = 5,
TextureType_Subsurface = 6,
TextureType_BackLighting = 7
};
std::vector<std::string> textures;
void read(NIFStream* nif) override;
};
struct NiParticleModifier : public Record
{
NiParticleModifierPtr next;
ControllerPtr controller;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiParticleGrowFade : public NiParticleModifier
{
float growTime;
float fadeTime;
void read(NIFStream* nif) override;
};
struct NiParticleColorModifier : public NiParticleModifier
{
NiColorDataPtr data;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiGravity : public NiParticleModifier
{
float mForce;
/* 0 - Wind (fixed direction)
* 1 - Point (fixed origin)
*/
int mType;
float mDecay;
osg::Vec3f mPosition;
osg::Vec3f mDirection;
void read(NIFStream* nif) override;
};
struct NiParticleCollider : public NiParticleModifier
{
float mBounceFactor;
void read(NIFStream* nif) override;
};
// NiPinaColada
struct NiPlanarCollider : public NiParticleCollider
{
osg::Vec2f mExtents;
osg::Vec3f mPosition;
osg::Vec3f mXVector, mYVector;
osg::Vec3f mPlaneNormal;
float mPlaneDistance;
void read(NIFStream* nif) override;
};
struct NiSphericalCollider : public NiParticleCollider
{
float mRadius;
osg::Vec3f mCenter;
void read(NIFStream* nif) override;
};
struct NiParticleRotation : public NiParticleModifier
{
void read(NIFStream* nif) override;
};
} // Namespace
#endif

View file

@ -1,9 +1,9 @@
#include "controller.hpp" #include "controller.hpp"
#include "controlled.hpp"
#include "data.hpp" #include "data.hpp"
#include "node.hpp" #include "node.hpp"
#include "recordptr.hpp" #include "particle.hpp"
#include "texture.hpp"
namespace Nif namespace Nif
{ {

View file

@ -1,7 +1,7 @@
#include "effect.hpp" #include "effect.hpp"
#include "controlled.hpp"
#include "node.hpp" #include "node.hpp"
#include "texture.hpp"
namespace Nif namespace Nif
{ {
@ -9,55 +9,61 @@ namespace Nif
void NiDynamicEffect::read(NIFStream* nif) void NiDynamicEffect::read(NIFStream* nif)
{ {
Node::read(nif); Node::read(nif);
if (nif->getVersion() >= nif->generateVersion(10, 1, 0, 106)
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) if (nif->getVersion() > NIFFile::VER_MW && nif->getVersion() < nif->generateVersion(10, 1, 0, 0))
nif->getBoolean(); // Switch state return;
if (nif->getVersion() <= NIFFile::VER_MW
|| (nif->getVersion() >= nif->generateVersion(10, 1, 0, 0) if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)) return;
{
size_t numAffectedNodes = nif->get<uint32_t>(); if (nif->getVersion() >= nif->generateVersion(10, 1, 0, 106))
nif->skip(numAffectedNodes * 4); nif->read(mSwitchState);
} size_t numAffectedNodes = nif->get<uint32_t>();
nif->skip(numAffectedNodes * 4);
} }
void NiLight::read(NIFStream* nif) void NiLight::read(NIFStream* nif)
{ {
NiDynamicEffect::read(nif); NiDynamicEffect::read(nif);
dimmer = nif->getFloat(); mDimmer = nif->getFloat();
ambient = nif->getVector3(); mAmbient = nif->getVector3();
diffuse = nif->getVector3(); mDiffuse = nif->getVector3();
specular = nif->getVector3(); mSpecular = nif->getVector3();
}
void NiPointLight::read(NIFStream* nif)
{
NiLight::read(nif);
mConstantAttenuation = nif->getFloat();
mLinearAttenuation = nif->getFloat();
mQuadraticAttenuation = nif->getFloat();
}
void NiSpotLight::read(NIFStream* nif)
{
NiPointLight::read(nif);
mCutoff = nif->getFloat();
mExponent = nif->getFloat();
} }
void NiTextureEffect::read(NIFStream* nif) void NiTextureEffect::read(NIFStream* nif)
{ {
NiDynamicEffect::read(nif); NiDynamicEffect::read(nif);
// Model Projection Matrix nif->read(mProjectionRotation);
nif->skip(3 * 3 * sizeof(float)); nif->read(mProjectionPosition);
nif->read(mFilterMode);
// Model Projection Transform
nif->skip(3 * sizeof(float));
// Texture Filtering
nif->skip(4);
// Max anisotropy samples
if (nif->getVersion() >= NIFStream::generateVersion(20, 5, 0, 4)) if (nif->getVersion() >= NIFStream::generateVersion(20, 5, 0, 4))
nif->skip(2); nif->read(mMaxAnisotropy);
nif->read(mClampMode);
clamp = nif->getUInt(); mTextureType = static_cast<TextureType>(nif->get<uint32_t>());
mCoordGenType = static_cast<CoordGenType>(nif->get<uint32_t>());
textureType = (TextureType)nif->getUInt(); mTexture.read(nif);
nif->read(mEnableClipPlane);
coordGenType = (CoordGenType)nif->getUInt(); mClipPlane = osg::Plane(nif->get<osg::Vec4f>());
texture.read(nif);
nif->skip(1); // Use clipping plane
nif->skip(16); // Clipping plane dimensions vector
if (nif->getVersion() <= NIFStream::generateVersion(10, 2, 0, 0)) if (nif->getVersion() <= NIFStream::generateVersion(10, 2, 0, 0))
nif->skip(4); // PS2-specific shorts nif->skip(4); // PS2-specific shorts
if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12)) if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12))
@ -67,24 +73,8 @@ namespace Nif
void NiTextureEffect::post(Reader& nif) void NiTextureEffect::post(Reader& nif)
{ {
NiDynamicEffect::post(nif); NiDynamicEffect::post(nif);
texture.post(nif);
}
void NiPointLight::read(NIFStream* nif) mTexture.post(nif);
{
NiLight::read(nif);
constantAttenuation = nif->getFloat();
linearAttenuation = nif->getFloat();
quadraticAttenuation = nif->getFloat();
}
void NiSpotLight::read(NIFStream* nif)
{
NiPointLight::read(nif);
cutoff = nif->getFloat();
exponent = nif->getFloat();
} }
} }

View file

@ -29,67 +29,75 @@
namespace Nif namespace Nif
{ {
// Abstract
struct NiDynamicEffect : public Node struct NiDynamicEffect : public Node
{ {
bool mSwitchState{ true };
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
// Used as base for NiAmbientLight, NiDirectionalLight, NiPointLight and NiSpotLight. // Abstract light source
struct NiLight : NiDynamicEffect struct NiLight : NiDynamicEffect
{ {
float dimmer; float mDimmer;
osg::Vec3f ambient; osg::Vec3f mAmbient;
osg::Vec3f diffuse; osg::Vec3f mDiffuse;
osg::Vec3f specular; osg::Vec3f mSpecular;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiPointLight : public NiLight struct NiPointLight : public NiLight
{ {
float constantAttenuation; float mConstantAttenuation;
float linearAttenuation; float mLinearAttenuation;
float quadraticAttenuation; float mQuadraticAttenuation;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiSpotLight : public NiPointLight struct NiSpotLight : public NiPointLight
{ {
float cutoff; float mCutoff;
float exponent; float mExponent;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiTextureEffect : NiDynamicEffect struct NiTextureEffect : NiDynamicEffect
{ {
NiSourceTexturePtr texture; enum class TextureType : uint32_t
unsigned int clamp;
enum TextureType
{ {
Projected_Light = 0, ProjectedLight = 0,
Projected_Shadow = 1, ProjectedShadow = 1,
Environment_Map = 2, EnvironmentMap = 2,
Fog_Map = 3 FogMap = 3,
}; };
TextureType textureType;
enum CoordGenType enum class CoordGenType : uint32_t
{ {
World_Parallel = 0, WorldParallel = 0,
World_Perspective, WorldPerspective = 1,
Sphere_Map, SphereMap = 2,
Specular_Cube_Map, SpecularCubeMap = 3,
Diffuse_Cube_Map DiffuseCubeMap = 4,
}; };
CoordGenType coordGenType;
Matrix3 mProjectionRotation;
osg::Vec3f mProjectionPosition;
uint32_t mFilterMode;
NiSourceTexturePtr mTexture;
uint16_t mMaxAnisotropy{ 0 };
uint32_t mClampMode;
TextureType mTextureType;
CoordGenType mCoordGenType;
uint8_t mEnableClipPlane;
osg::Plane mClipPlane;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
bool wrapT() const { return clamp & 1; } bool wrapT() const { return mClampMode & 1; }
bool wrapS() const { return (clamp >> 1) & 1; } bool wrapS() const { return mClampMode & 2; }
}; };
} // Namespace } // Namespace

View file

@ -6,25 +6,21 @@ namespace Nif
void NiExtraData::read(NIFStream* nif) void NiExtraData::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
nif->readVector(data, recordSize);
}
void NiStringExtraData::read(NIFStream* nif) nif->readVector(mData, mRecordSize);
{
Extra::read(nif);
string = nif->getString();
} }
void NiTextKeyExtraData::read(NIFStream* nif) void NiTextKeyExtraData::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
int keynum = nif->getInt(); uint32_t numKeys;
list.resize(keynum); nif->read(numKeys);
for (int i = 0; i < keynum; i++) mList.resize(numKeys);
for (TextKey& key : mList)
{ {
list[i].time = nif->getFloat(); nif->read(key.mTime);
list[i].text = nif->getString(); nif->read(key.mText);
} }
} }
@ -32,81 +28,39 @@ namespace Nif
{ {
Extra::read(nif); Extra::read(nif);
nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess nif->skip(nif->get<uint16_t>() * sizeof(float)); // vertex weights I guess
}
void NiIntegerExtraData::read(NIFStream* nif)
{
Extra::read(nif);
data = nif->getUInt();
}
void NiIntegersExtraData::read(NIFStream* nif)
{
Extra::read(nif);
nif->readVector(data, nif->getUInt());
}
void NiBinaryExtraData::read(NIFStream* nif)
{
Extra::read(nif);
nif->readVector(data, nif->getUInt());
}
void NiBooleanExtraData::read(NIFStream* nif)
{
Extra::read(nif);
data = nif->getBoolean();
}
void NiVectorExtraData::read(NIFStream* nif)
{
Extra::read(nif);
data = nif->getVector4();
}
void NiFloatExtraData::read(NIFStream* nif)
{
Extra::read(nif);
data = nif->getFloat();
}
void NiFloatsExtraData::read(NIFStream* nif)
{
Extra::read(nif);
nif->readVector(data, nif->getUInt());
} }
void BSBound::read(NIFStream* nif) void BSBound::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
center = nif->getVector3();
halfExtents = nif->getVector3(); nif->read(mCenter);
nif->read(mExtents);
} }
void BSFurnitureMarker::LegacyFurniturePosition::read(NIFStream* nif) void BSFurnitureMarker::LegacyFurniturePosition::read(NIFStream* nif)
{ {
mOffset = nif->getVector3(); nif->read(mOffset);
mOrientation = nif->getUShort(); nif->read(mOrientation);
mPositionRef = nif->getChar(); nif->read(mPositionRef);
nif->skip(1); // Position ref 2 nif->skip(1); // Position ref 2
} }
void BSFurnitureMarker::FurniturePosition::read(NIFStream* nif) void BSFurnitureMarker::FurniturePosition::read(NIFStream* nif)
{ {
mOffset = nif->getVector3(); nif->read(mOffset);
mHeading = nif->getFloat(); nif->read(mHeading);
mType = nif->getUShort(); nif->read(mType);
mEntryPoint = nif->getUShort(); nif->read(mEntryPoint);
} }
void BSFurnitureMarker::read(NIFStream* nif) void BSFurnitureMarker::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
unsigned int num = nif->getUInt();
uint32_t num;
nif->read(num);
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
{ {
mLegacyMarkers.resize(num); mLegacyMarkers.resize(num);
@ -124,19 +78,20 @@ namespace Nif
void BSInvMarker::read(NIFStream* nif) void BSInvMarker::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
float rotX = nif->getUShort() / 1000.0;
float rotY = nif->getUShort() / 1000.0;
float rotZ = nif->getUShort() / 1000.0;
mScale = nif->getFloat();
float rotX = nif->get<uint16_t>() / 1000.f;
float rotY = nif->get<uint16_t>() / 1000.f;
float rotZ = nif->get<uint16_t>() / 1000.f;
mRotation = osg::Quat(rotX, osg::X_AXIS, rotY, osg::Y_AXIS, rotZ, osg::Z_AXIS); mRotation = osg::Quat(rotX, osg::X_AXIS, rotY, osg::Y_AXIS, rotZ, osg::Z_AXIS);
nif->read(mScale);
} }
void BSBehaviorGraphExtraData::read(NIFStream* nif) void BSBehaviorGraphExtraData::read(NIFStream* nif)
{ {
Extra::read(nif); Extra::read(nif);
mFile = nif->getString();
mControlsBaseSkeleton = nif->getBoolean(); nif->read(mFile);
nif->read(mControlsBaseSkeleton);
} }
} }

View file

@ -1,26 +1,3 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: https://openmw.org/
This file (extra.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
https://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_EXTRA_HPP #ifndef OPENMW_COMPONENTS_NIF_EXTRA_HPP
#define OPENMW_COMPONENTS_NIF_EXTRA_HPP #define OPENMW_COMPONENTS_NIF_EXTRA_HPP
@ -29,9 +6,46 @@
namespace Nif namespace Nif
{ {
template <typename T>
struct TypedExtra : public Extra
{
T mData;
void read(NIFStream* nif) override
{
Extra::read(nif);
nif->read(mData);
}
};
template <typename T>
struct TypedVectorExtra : public Extra
{
std::vector<T> mData;
void read(NIFStream* nif) override
{
Extra::read(nif);
nif->readVector(mData, nif->get<uint32_t>());
}
};
using NiBooleanExtraData = TypedExtra<bool>;
using NiFloatExtraData = TypedExtra<float>;
using NiIntegerExtraData = TypedExtra<uint32_t>;
using NiStringExtraData = TypedExtra<std::string>;
using NiVectorExtraData = TypedExtra<osg::Vec4f>;
using NiBinaryExtraData = TypedVectorExtra<uint8_t>;
using NiFloatsExtraData = TypedVectorExtra<float>;
using NiIntegersExtraData = TypedVectorExtra<uint32_t>;
// Distinct from NiBinaryExtraData, uses mRecordSize as its size
struct NiExtraData : public Extra struct NiExtraData : public Extra
{ {
std::vector<char> data; std::vector<uint8_t> mData;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -45,78 +59,17 @@ namespace Nif
{ {
struct TextKey struct TextKey
{ {
float time; float mTime;
std::string text; std::string mText;
}; };
std::vector<TextKey> list; std::vector<TextKey> mList;
void read(NIFStream* nif) override;
};
struct NiStringExtraData : public Extra
{
/* Known meanings:
"MRK" - marker, only visible in the editor, not rendered in-game
"NCC" - no collision except with the camera
Anything else starting with "NC" - no collision
*/
std::string string;
void read(NIFStream* nif) override;
};
struct NiIntegerExtraData : public Extra
{
unsigned int data;
void read(NIFStream* nif) override;
};
struct NiIntegersExtraData : public Extra
{
std::vector<unsigned int> data;
void read(NIFStream* nif) override;
};
struct NiBinaryExtraData : public Extra
{
std::vector<char> data;
void read(NIFStream* nif) override;
};
struct NiBooleanExtraData : public Extra
{
bool data;
void read(NIFStream* nif) override;
};
struct NiVectorExtraData : public Extra
{
osg::Vec4f data;
void read(NIFStream* nif) override;
};
struct NiFloatExtraData : public Extra
{
float data;
void read(NIFStream* nif) override;
};
struct NiFloatsExtraData : public Extra
{
std::vector<float> data;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct BSBound : public Extra struct BSBound : public Extra
{ {
osg::Vec3f center, halfExtents; osg::Vec3f mCenter, mExtents;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -149,7 +102,7 @@ namespace Nif
struct BSInvMarker : public Extra struct BSInvMarker : public Extra
{ {
osg::Quat mRotation; osg::Quat mRotation;
float mScale = 1.0f; float mScale;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -162,5 +115,5 @@ namespace Nif
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
} // Namespace }
#endif #endif

View file

@ -10,15 +10,16 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "controlled.hpp"
#include "controller.hpp" #include "controller.hpp"
#include "data.hpp" #include "data.hpp"
#include "effect.hpp" #include "effect.hpp"
#include "exception.hpp" #include "exception.hpp"
#include "extra.hpp" #include "extra.hpp"
#include "node.hpp" #include "node.hpp"
#include "particle.hpp"
#include "physics.hpp" #include "physics.hpp"
#include "property.hpp" #include "property.hpp"
#include "texture.hpp"
namespace Nif namespace Nif
{ {

View file

@ -0,0 +1,95 @@
#include "particle.hpp"
#include "data.hpp"
namespace Nif
{
void NiParticleModifier::read(NIFStream* nif)
{
mNext.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
mController.read(nif);
}
void NiParticleModifier::post(Reader& nif)
{
mNext.post(nif);
mController.post(nif);
}
void NiParticleGrowFade::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
nif->read(mGrowTime);
nif->read(mFadeTime);
}
void NiParticleColorModifier::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
mData.read(nif);
}
void NiParticleColorModifier::post(Reader& nif)
{
NiParticleModifier::post(nif);
mData.post(nif);
}
void NiGravity::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
nif->read(mDecay);
nif->read(mForce);
mType = static_cast<ForceType>(nif->get<uint32_t>());
nif->read(mPosition);
nif->read(mDirection);
}
void NiParticleCollider::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
nif->read(mBounceFactor);
if (nif->getVersion() >= NIFStream::generateVersion(4, 2, 0, 2))
{
nif->read(mSpawnOnCollision);
nif->read(mDieOnCollision);
}
}
void NiPlanarCollider::read(NIFStream* nif)
{
NiParticleCollider::read(nif);
nif->read(mExtents);
nif->read(mPosition);
nif->read(mXVector);
nif->read(mYVector);
nif->read(mPlaneNormal);
nif->read(mPlaneDistance);
}
void NiSphericalCollider::read(NIFStream* nif)
{
NiParticleCollider::read(nif);
nif->read(mRadius);
nif->read(mCenter);
}
void NiParticleRotation::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
nif->read(mRandomInitialAxis);
nif->read(mInitialAxis);
nif->read(mRotationSpeed);
}
}

View file

@ -0,0 +1,90 @@
#ifndef OPENMW_COMPONENTS_NIF_PARTICLE_HPP
#define OPENMW_COMPONENTS_NIF_PARTICLE_HPP
#include "base.hpp"
namespace Nif
{
struct NiParticleModifier : public Record
{
NiParticleModifierPtr mNext;
ControllerPtr mController;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiParticleGrowFade : public NiParticleModifier
{
float mGrowTime;
float mFadeTime;
void read(NIFStream* nif) override;
};
struct NiParticleColorModifier : public NiParticleModifier
{
NiColorDataPtr mData;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiGravity : public NiParticleModifier
{
enum class ForceType : uint32_t
{
Wind = 0, // Fixed direction
Point = 1, // Fixed origin
};
float mDecay{ 0.f };
float mForce;
ForceType mType;
osg::Vec3f mPosition;
osg::Vec3f mDirection;
void read(NIFStream* nif) override;
};
struct NiParticleCollider : public NiParticleModifier
{
float mBounceFactor;
bool mSpawnOnCollision{ false };
bool mDieOnCollision{ false };
void read(NIFStream* nif) override;
};
// NiPinaColada
struct NiPlanarCollider : public NiParticleCollider
{
osg::Vec2f mExtents;
osg::Vec3f mPosition;
osg::Vec3f mXVector, mYVector;
osg::Vec3f mPlaneNormal;
float mPlaneDistance;
void read(NIFStream* nif) override;
};
struct NiSphericalCollider : public NiParticleCollider
{
float mRadius;
osg::Vec3f mCenter;
void read(NIFStream* nif) override;
};
struct NiParticleRotation : public NiParticleModifier
{
uint8_t mRandomInitialAxis;
osg::Vec3f mInitialAxis;
float mRotationSpeed;
void read(NIFStream* nif) override;
};
}
#endif

View file

@ -12,15 +12,15 @@ namespace Nif
void bhkWorldObjCInfoProperty::read(NIFStream* nif) void bhkWorldObjCInfoProperty::read(NIFStream* nif)
{ {
mData = nif->getUInt(); nif->read(mData);
mSize = nif->getUInt(); nif->read(mSize);
mCapacityAndFlags = nif->getUInt(); nif->read(mCapacityAndFlags);
} }
void bhkWorldObjectCInfo::read(NIFStream* nif) void bhkWorldObjectCInfo::read(NIFStream* nif)
{ {
nif->skip(4); // Unused nif->skip(4); // Unused
mPhaseType = static_cast<BroadPhaseType>(nif->getChar()); mPhaseType = static_cast<BroadPhaseType>(nif->get<uint8_t>());
nif->skip(3); // Unused nif->skip(3); // Unused
mProperty.read(nif); mProperty.read(nif);
} }
@ -29,47 +29,47 @@ namespace Nif
{ {
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
nif->skip(4); // Unknown nif->skip(4); // Unknown
mMaterial = nif->getUInt(); nif->read(mMaterial);
} }
void HavokFilter::read(NIFStream* nif) void HavokFilter::read(NIFStream* nif)
{ {
mLayer = nif->getChar(); nif->read(mLayer);
mFlags = nif->getChar(); nif->read(mFlags);
mGroup = nif->getUShort(); nif->read(mGroup);
} }
void hkSubPartData::read(NIFStream* nif) void hkSubPartData::read(NIFStream* nif)
{ {
mHavokFilter.read(nif); mHavokFilter.read(nif);
mNumVertices = nif->getUInt(); nif->read(mNumVertices);
mHavokMaterial.read(nif); mHavokMaterial.read(nif);
} }
void hkpMoppCode::read(NIFStream* nif)
{
unsigned int size = nif->getUInt();
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
mOffset = nif->getVector4();
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->getChar(); // MOPP data build type
nif->readVector(mData, size);
}
void bhkEntityCInfo::read(NIFStream* nif) void bhkEntityCInfo::read(NIFStream* nif)
{ {
mResponseType = static_cast<hkResponseType>(nif->getChar()); mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
nif->skip(1); // Unused nif->skip(1); // Unused
mProcessContactDelay = nif->getUShort(); nif->read(mProcessContactDelay);
}
void hkpMoppCode::read(NIFStream* nif)
{
uint32_t dataSize;
nif->read(dataSize);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mOffset);
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
nif->read(mBuildType);
nif->readVector(mData, dataSize);
} }
void TriangleData::read(NIFStream* nif) void TriangleData::read(NIFStream* nif)
{ {
for (int i = 0; i < 3; i++) nif->readArray(mTriangle);
mTriangle[i] = nif->getUShort(); nif->read(mWeldingInfo);
mWeldingInfo = nif->getUShort();
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
mNormal = nif->getVector3(); nif->read(mNormal);
} }
void bhkMeshMaterial::read(NIFStream* nif) void bhkMeshMaterial::read(NIFStream* nif)
@ -80,28 +80,27 @@ namespace Nif
void bhkQsTransform::read(NIFStream* nif) void bhkQsTransform::read(NIFStream* nif)
{ {
mTranslation = nif->getVector4(); nif->read(mTranslation);
mRotation = nif->getQuaternion(); nif->read(mRotation);
} }
void bhkCMSBigTri::read(NIFStream* nif) void bhkCMSBigTri::read(NIFStream* nif)
{ {
for (int i = 0; i < 3; i++) nif->readArray(mTriangle);
mTriangle[i] = nif->getUShort(); nif->read(mMaterial);
mMaterial = nif->getUInt(); nif->read(mWeldingInfo);
mWeldingInfo = nif->getUShort();
} }
void bhkCMSChunk::read(NIFStream* nif) void bhkCMSChunk::read(NIFStream* nif)
{ {
mTranslation = nif->getVector4(); nif->read(mTranslation);
mMaterialIndex = nif->getUInt(); nif->read(mMaterialIndex);
mReference = nif->getUShort(); nif->read(mReference);
mTransformIndex = nif->getUShort(); nif->read(mTransformIndex);
nif->readVector(mVertices, nif->getUInt()); nif->readVector(mVertices, nif->get<uint32_t>());
nif->readVector(mIndices, nif->getUInt()); nif->readVector(mIndices, nif->get<uint32_t>());
nif->readVector(mStrips, nif->getUInt()); nif->readVector(mStrips, nif->get<uint32_t>());
nif->readVector(mWeldingInfos, nif->getUInt()); nif->readVector(mWeldingInfos, nif->get<uint32_t>());
} }
void bhkRigidBodyCInfo::read(NIFStream* nif) void bhkRigidBodyCInfo::read(NIFStream* nif)
@ -115,64 +114,67 @@ namespace Nif
{ {
if (nif->getBethVersion() >= 83) if (nif->getBethVersion() >= 83)
nif->skip(4); // Unused nif->skip(4); // Unused
mResponseType = static_cast<hkResponseType>(nif->getChar()); mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
nif->skip(1); // Unused nif->skip(1); // Unused
mProcessContactDelay = nif->getUShort(); nif->read(mProcessContactDelay);
} }
} }
if (nif->getBethVersion() < 83) if (nif->getBethVersion() < 83)
nif->skip(4); // Unused nif->skip(4); // Unused
mTranslation = nif->getVector4(); nif->read(mTranslation);
mRotation = nif->getQuaternion(); nif->read(mRotation);
mLinearVelocity = nif->getVector4(); nif->read(mLinearVelocity);
mAngularVelocity = nif->getVector4(); nif->read(mAngularVelocity);
// A bit hacky, but this is the only instance where a 3x3 matrix has padding.
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
for (int j = 0; j < 4; j++) {
mInertiaTensor[i][j] = nif->getFloat(); nif->read(mInertiaTensor.mValues[i], 3);
mCenter = nif->getVector4(); nif->skip(4); // Padding
mMass = nif->getFloat(); }
mLinearDamping = nif->getFloat(); nif->read(mCenter);
mAngularDamping = nif->getFloat(); nif->read(mMass);
nif->read(mLinearDamping);
nif->read(mAngularDamping);
if (nif->getBethVersion() >= 83) if (nif->getBethVersion() >= 83)
{ {
if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_FO4) if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_FO4)
mTimeFactor = nif->getFloat(); nif->read(mTimeFactor);
mGravityFactor = nif->getFloat(); nif->read(mGravityFactor);
} }
mFriction = nif->getFloat(); nif->read(mFriction);
if (nif->getBethVersion() >= 83) if (nif->getBethVersion() >= 83)
mRollingFrictionMult = nif->getFloat(); nif->read(mRollingFrictionMult);
mRestitution = nif->getFloat(); nif->read(mRestitution);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
{ {
mMaxLinearVelocity = nif->getFloat(); nif->read(mMaxLinearVelocity);
mMaxAngularVelocity = nif->getFloat(); nif->read(mMaxAngularVelocity);
if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_FO4) if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_FO4)
mPenetrationDepth = nif->getFloat(); nif->read(mPenetrationDepth);
} }
mMotionType = static_cast<hkMotionType>(nif->getChar()); mMotionType = static_cast<hkMotionType>(nif->get<uint8_t>());
if (nif->getBethVersion() < 83) if (nif->getBethVersion() < 83)
mDeactivatorType = static_cast<hkDeactivatorType>(nif->getChar()); mDeactivatorType = static_cast<hkDeactivatorType>(nif->get<uint8_t>());
else else
mEnableDeactivation = nif->getBoolean(); nif->read(mEnableDeactivation);
mSolverDeactivation = static_cast<hkSolverDeactivation>(nif->getChar()); mSolverDeactivation = static_cast<hkSolverDeactivation>(nif->get<uint8_t>());
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4)
{ {
nif->skip(1); nif->skip(1);
mPenetrationDepth = nif->getFloat(); nif->read(mPenetrationDepth);
mTimeFactor = nif->getFloat(); nif->read(mTimeFactor);
nif->skip(4); nif->skip(4);
mResponseType = static_cast<hkResponseType>(nif->getChar()); mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
nif->skip(1); // Unused nif->skip(1); // Unused
mProcessContactDelay = nif->getUShort(); nif->read(mProcessContactDelay);
} }
mQualityType = static_cast<hkQualityType>(nif->getChar()); mQualityType = static_cast<hkQualityType>(nif->get<uint8_t>());
if (nif->getBethVersion() >= 83) if (nif->getBethVersion() >= 83)
{ {
mAutoRemoveLevel = nif->getChar(); nif->read(mAutoRemoveLevel);
mResponseModifierFlags = nif->getChar(); nif->read(mResponseModifierFlags);
mNumContactPointShapeKeys = nif->getChar(); nif->read(mNumContactPointShapeKeys);
mForceCollidedOntoPPU = nif->getBoolean(); nif->read(mForceCollidedOntoPPU);
} }
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4) if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4)
nif->skip(3); // Unused nif->skip(3); // Unused
@ -182,7 +184,7 @@ namespace Nif
void bhkConstraintCInfo::read(NIFStream* nif) void bhkConstraintCInfo::read(NIFStream* nif)
{ {
nif->get<unsigned int>(); // Number of entities, unused nif->get<uint32_t>(); // Number of entities, unused
mEntityA.read(nif); mEntityA.read(nif);
mEntityB.read(nif); mEntityB.read(nif);
@ -203,7 +205,7 @@ namespace Nif
nif->read(mDamping); nif->read(mDamping);
nif->read(mProportionalRecoveryVelocity); nif->read(mProportionalRecoveryVelocity);
nif->read(mConstantRecoveryVelocity); nif->read(mConstantRecoveryVelocity);
mEnabled = nif->getBoolean(); nif->read(mEnabled);
} }
void bhkVelocityConstraintMotor::read(NIFStream* nif) void bhkVelocityConstraintMotor::read(NIFStream* nif)
@ -212,8 +214,8 @@ namespace Nif
nif->read(mMaxForce); nif->read(mMaxForce);
nif->read(mTau); nif->read(mTau);
nif->read(mTargetVelocity); nif->read(mTargetVelocity);
mUseVelocityTarget = nif->getBoolean(); nif->read(mUseVelocityTarget);
mEnabled = nif->getBoolean(); nif->read(mEnabled);
} }
void bhkSpringDamperConstraintMotor::read(NIFStream* nif) void bhkSpringDamperConstraintMotor::read(NIFStream* nif)
@ -222,7 +224,7 @@ namespace Nif
nif->read(mMaxForce); nif->read(mMaxForce);
nif->read(mSpringConstant); nif->read(mSpringConstant);
nif->read(mSpringDamping); nif->read(mSpringDamping);
mEnabled = nif->getBoolean(); nif->read(mEnabled);
} }
void bhkConstraintMotorCInfo::read(NIFStream* nif) void bhkConstraintMotorCInfo::read(NIFStream* nif)
@ -335,7 +337,8 @@ namespace Nif
void bhkCollisionObject::read(NIFStream* nif) void bhkCollisionObject::read(NIFStream* nif)
{ {
NiCollisionObject::read(nif); NiCollisionObject::read(nif);
mFlags = nif->getUShort();
nif->read(mFlags);
mBody.read(nif); mBody.read(nif);
} }
@ -356,6 +359,7 @@ namespace Nif
void bhkEntity::read(NIFStream* nif) void bhkEntity::read(NIFStream* nif)
{ {
bhkWorldObject::read(nif); bhkWorldObject::read(nif);
mInfo.read(nif); mInfo.read(nif);
} }
@ -372,21 +376,26 @@ namespace Nif
void bhkMoppBvTreeShape::read(NIFStream* nif) void bhkMoppBvTreeShape::read(NIFStream* nif)
{ {
bhkBvTreeShape::read(nif); bhkBvTreeShape::read(nif);
nif->skip(12); // Unused nif->skip(12); // Unused
mScale = nif->getFloat(); nif->read(mScale);
mMopp.read(nif); mMopp.read(nif);
} }
void bhkNiTriStripsShape::read(NIFStream* nif) void bhkNiTriStripsShape::read(NIFStream* nif)
{ {
mHavokMaterial.read(nif); mHavokMaterial.read(nif);
mRadius = nif->getFloat(); nif->read(mRadius);
nif->skip(20); // Unused nif->skip(20); // Unused
mGrowBy = nif->getUInt(); nif->read(mGrowBy);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
mScale = nif->getVector4(); nif->read(mScale);
readRecordList(nif, mData); readRecordList(nif, mData);
nif->readVector(mFilters, nif->getUInt()); uint32_t numFilters;
nif->read(numFilters);
mHavokFilters.resize(numFilters);
for (HavokFilter& filter : mHavokFilters)
filter.read(nif);
} }
void bhkNiTriStripsShape::post(Reader& nif) void bhkNiTriStripsShape::post(Reader& nif)
@ -398,15 +407,17 @@ namespace Nif
{ {
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{ {
mSubshapes.resize(nif->getUShort()); uint16_t numSubshapes;
nif->read(numSubshapes);
mSubshapes.resize(numSubshapes);
for (hkSubPartData& subshape : mSubshapes) for (hkSubPartData& subshape : mSubshapes)
subshape.read(nif); subshape.read(nif);
} }
mUserData = nif->getUInt(); nif->read(mUserData);
nif->skip(4); // Unused nif->skip(4); // Unused
mRadius = nif->getFloat(); nif->read(mRadius);
nif->skip(4); // Unused nif->skip(4); // Unused
mScale = nif->getVector4(); nif->read(mScale);
nif->skip(20); // Duplicates of the two previous fields nif->skip(20); // Duplicates of the two previous fields
mData.read(nif); mData.read(nif);
} }
@ -418,22 +429,26 @@ namespace Nif
void hkPackedNiTriStripsData::read(NIFStream* nif) void hkPackedNiTriStripsData::read(NIFStream* nif)
{ {
unsigned int numTriangles = nif->getUInt(); uint32_t numTriangles;
nif->read(numTriangles);
mTriangles.resize(numTriangles); mTriangles.resize(numTriangles);
for (unsigned int i = 0; i < numTriangles; i++) for (uint32_t i = 0; i < numTriangles; i++)
mTriangles[i].read(nif); mTriangles[i].read(nif);
unsigned int numVertices = nif->getUInt(); uint32_t numVertices;
nif->read(numVertices);
bool compressed = false; bool compressed = false;
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
compressed = nif->getBoolean(); nif->read(compressed);
if (!compressed) if (!compressed)
nif->readVector(mVertices, numVertices); nif->readVector(mVertices, numVertices);
else else
nif->skip(6 * numVertices); // Half-precision vectors are not currently supported nif->skip(6 * numVertices); // Half-precision vectors are not currently supported
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
{ {
mSubshapes.resize(nif->getUShort()); uint16_t numSubshapes;
nif->read(numSubshapes);
mSubshapes.resize(numSubshapes);
for (hkSubPartData& subshape : mSubshapes) for (hkSubPartData& subshape : mSubshapes)
subshape.read(nif); subshape.read(nif);
} }
@ -447,23 +462,25 @@ namespace Nif
void bhkConvexShape::read(NIFStream* nif) void bhkConvexShape::read(NIFStream* nif)
{ {
bhkSphereRepShape::read(nif); bhkSphereRepShape::read(nif);
mRadius = nif->getFloat();
nif->read(mRadius);
} }
void bhkConvexVerticesShape::read(NIFStream* nif) void bhkConvexVerticesShape::read(NIFStream* nif)
{ {
bhkConvexShape::read(nif); bhkConvexShape::read(nif);
mVerticesProperty.read(nif); mVerticesProperty.read(nif);
mNormalsProperty.read(nif); mNormalsProperty.read(nif);
nif->readVector(mVertices, nif->getUInt()); nif->readVector(mVertices, nif->get<uint32_t>());
nif->readVector(mNormals, nif->getUInt()); nif->readVector(mNormals, nif->get<uint32_t>());
} }
void bhkConvexTransformShape::read(NIFStream* nif) void bhkConvexTransformShape::read(NIFStream* nif)
{ {
mShape.read(nif); mShape.read(nif);
mHavokMaterial.read(nif); mHavokMaterial.read(nif);
mRadius = nif->getFloat(); nif->read(mRadius);
nif->skip(8); // Unused nif->skip(8); // Unused
std::array<float, 16> mat; std::array<float, 16> mat;
nif->readArray(mat); nif->readArray(mat);
@ -478,19 +495,21 @@ namespace Nif
void bhkBoxShape::read(NIFStream* nif) void bhkBoxShape::read(NIFStream* nif)
{ {
bhkConvexShape::read(nif); bhkConvexShape::read(nif);
nif->skip(8); // Unused nif->skip(8); // Unused
mExtents = nif->getVector3(); nif->read(mExtents);
nif->skip(4); // Unused nif->skip(4); // Unused
} }
void bhkCapsuleShape::read(NIFStream* nif) void bhkCapsuleShape::read(NIFStream* nif)
{ {
bhkConvexShape::read(nif); bhkConvexShape::read(nif);
nif->skip(8); // Unused nif->skip(8); // Unused
mPoint1 = nif->getVector3(); nif->read(mPoint1);
mRadius1 = nif->getFloat(); nif->read(mRadius1);
mPoint2 = nif->getVector3(); nif->read(mPoint2);
mRadius2 = nif->getFloat(); nif->read(mRadius2);
} }
void bhkListShape::read(NIFStream* nif) void bhkListShape::read(NIFStream* nif)
@ -499,7 +518,8 @@ namespace Nif
mHavokMaterial.read(nif); mHavokMaterial.read(nif);
mChildShapeProperty.read(nif); mChildShapeProperty.read(nif);
mChildFilterProperty.read(nif); mChildFilterProperty.read(nif);
unsigned int numFilters = nif->getUInt(); uint32_t numFilters;
nif->read(numFilters);
mHavokFilters.resize(numFilters); mHavokFilters.resize(numFilters);
for (HavokFilter& filter : mHavokFilters) for (HavokFilter& filter : mHavokFilters)
filter.read(nif); filter.read(nif);
@ -508,12 +528,12 @@ namespace Nif
void bhkCompressedMeshShape::read(NIFStream* nif) void bhkCompressedMeshShape::read(NIFStream* nif)
{ {
mTarget.read(nif); mTarget.read(nif);
mUserData = nif->getUInt(); nif->read(mUserData);
mRadius = nif->getFloat(); nif->read(mRadius);
nif->getFloat(); // Unknown nif->skip(4); // Unknown
mScale = nif->getVector4(); nif->read(mScale);
nif->getFloat(); // Radius nif->skip(4); // Radius
nif->getVector4(); // Scale nif->skip(16); // Scale
mData.read(nif); mData.read(nif);
} }
@ -525,60 +545,66 @@ namespace Nif
void bhkCompressedMeshShapeData::read(NIFStream* nif) void bhkCompressedMeshShapeData::read(NIFStream* nif)
{ {
mBitsPerIndex = nif->getUInt(); nif->read(mBitsPerIndex);
mBitsPerWIndex = nif->getUInt(); nif->read(mBitsPerWIndex);
mMaskWIndex = nif->getUInt(); nif->read(mMaskWIndex);
mMaskIndex = nif->getUInt(); nif->read(mMaskIndex);
mError = nif->getFloat(); nif->read(mError);
mAabbMin = nif->getVector4(); nif->read(mAabbMin);
mAabbMax = nif->getVector4(); nif->read(mAabbMax);
mWeldingType = nif->getChar(); nif->read(mWeldingType);
mMaterialType = nif->getChar(); nif->read(mMaterialType);
nif->skip(nif->getUInt() * 4); // Unused nif->skip(nif->get<uint32_t>() * 4); // Unused
nif->skip(nif->getUInt() * 4); // Unused nif->skip(nif->get<uint32_t>() * 4); // Unused
nif->skip(nif->getUInt() * 4); // Unused nif->skip(nif->get<uint32_t>() * 4); // Unused
size_t numMaterials = nif->getUInt(); uint32_t numMaterials;
nif->read(numMaterials);
mMaterials.resize(numMaterials); mMaterials.resize(numMaterials);
for (bhkMeshMaterial& material : mMaterials) for (bhkMeshMaterial& material : mMaterials)
material.read(nif); material.read(nif);
nif->getUInt(); // Unused nif->skip(4); // Unused
size_t numTransforms = nif->getUInt();
uint32_t numTransforms;
nif->read(numTransforms);
mChunkTransforms.resize(numTransforms); mChunkTransforms.resize(numTransforms);
for (bhkQsTransform& transform : mChunkTransforms) for (bhkQsTransform& transform : mChunkTransforms)
transform.read(nif); transform.read(nif);
nif->readVector(mBigVerts, nif->getUInt()); nif->readVector(mBigVerts, nif->get<uint32_t>());
size_t numBigTriangles = nif->getUInt(); uint32_t numBigTriangles;
nif->read(numBigTriangles);
mBigTris.resize(numBigTriangles); mBigTris.resize(numBigTriangles);
for (bhkCMSBigTri& tri : mBigTris) for (bhkCMSBigTri& tri : mBigTris)
tri.read(nif); tri.read(nif);
size_t numChunks = nif->getUInt(); uint32_t numChunks;
nif->read(numChunks);
mChunks.resize(numChunks); mChunks.resize(numChunks);
for (bhkCMSChunk& chunk : mChunks) for (bhkCMSChunk& chunk : mChunks)
chunk.read(nif); chunk.read(nif);
nif->getUInt(); // Unused nif->skip(4); // Unused
} }
void bhkRigidBody::read(NIFStream* nif) void bhkRigidBody::read(NIFStream* nif)
{ {
bhkEntity::read(nif); bhkEntity::read(nif);
mInfo.read(nif); mInfo.read(nif);
readRecordList(nif, mConstraints); readRecordList(nif, mConstraints);
if (nif->getBethVersion() < 76) if (nif->getBethVersion() < 76)
mBodyFlags = nif->getUInt(); nif->read(mBodyFlags);
else else
mBodyFlags = nif->getUShort(); mBodyFlags = nif->get<uint16_t>();
} }
void bhkSimpleShapePhantom::read(NIFStream* nif) void bhkSimpleShapePhantom::read(NIFStream* nif)
{ {
bhkWorldObject::read(nif); bhkWorldObject::read(nif);
nif->skip(8); // Unused nif->skip(8); // Unused
std::array<float, 16> mat; std::array<float, 16> mat;
nif->readArray(mat); nif->readArray(mat);
@ -598,18 +624,21 @@ namespace Nif
void bhkRagdollConstraint::read(NIFStream* nif) void bhkRagdollConstraint::read(NIFStream* nif)
{ {
bhkConstraint::read(nif); bhkConstraint::read(nif);
mConstraint.read(nif); mConstraint.read(nif);
} }
void bhkHingeConstraint::read(NIFStream* nif) void bhkHingeConstraint::read(NIFStream* nif)
{ {
bhkConstraint::read(nif); bhkConstraint::read(nif);
mConstraint.read(nif); mConstraint.read(nif);
} }
void bhkLimitedHingeConstraint::read(NIFStream* nif) void bhkLimitedHingeConstraint::read(NIFStream* nif)
{ {
bhkConstraint::read(nif); bhkConstraint::read(nif);
mConstraint.read(nif); mConstraint.read(nif);
} }

View file

@ -1,6 +1,7 @@
#ifndef OPENMW_COMPONENTS_NIF_PHYSICS_HPP #ifndef OPENMW_COMPONENTS_NIF_PHYSICS_HPP
#define OPENMW_COMPONENTS_NIF_PHYSICS_HPP #define OPENMW_COMPONENTS_NIF_PHYSICS_HPP
#include "niftypes.hpp"
#include "record.hpp" #include "record.hpp"
#include "recordptr.hpp" #include "recordptr.hpp"
@ -23,9 +24,10 @@ namespace Nif
struct bhkWorldObjCInfoProperty struct bhkWorldObjCInfoProperty
{ {
unsigned int mData; uint32_t mData;
unsigned int mSize; uint32_t mSize;
unsigned int mCapacityAndFlags; uint32_t mCapacityAndFlags;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -41,28 +43,32 @@ namespace Nif
{ {
BroadPhaseType mPhaseType; BroadPhaseType mPhaseType;
bhkWorldObjCInfoProperty mProperty; bhkWorldObjCInfoProperty mProperty;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct HavokMaterial struct HavokMaterial
{ {
unsigned int mMaterial; uint32_t mMaterial;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct HavokFilter struct HavokFilter
{ {
unsigned char mLayer; uint8_t mLayer;
unsigned char mFlags; uint8_t mFlags;
unsigned short mGroup; uint16_t mGroup;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct hkSubPartData struct hkSubPartData
{ {
HavokMaterial mHavokMaterial; HavokMaterial mHavokMaterial;
unsigned int mNumVertices; uint32_t mNumVertices;
HavokFilter mHavokFilter; HavokFilter mHavokFilter;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -77,22 +83,26 @@ namespace Nif
struct bhkEntityCInfo struct bhkEntityCInfo
{ {
hkResponseType mResponseType; hkResponseType mResponseType;
unsigned short mProcessContactDelay; uint16_t mProcessContactDelay;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct hkpMoppCode struct hkpMoppCode
{ {
osg::Vec4f mOffset; osg::Vec4f mOffset;
std::vector<char> mData; uint8_t mBuildType;
std::vector<uint8_t> mData;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct TriangleData struct TriangleData
{ {
unsigned short mTriangle[3]; std::array<uint16_t, 3> mTriangle;
unsigned short mWeldingInfo; uint16_t mWeldingInfo;
osg::Vec3f mNormal; osg::Vec3f mNormal;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -100,6 +110,7 @@ namespace Nif
{ {
HavokMaterial mHavokMaterial; HavokMaterial mHavokMaterial;
HavokFilter mHavokFilter; HavokFilter mHavokFilter;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -107,27 +118,30 @@ namespace Nif
{ {
osg::Vec4f mTranslation; osg::Vec4f mTranslation;
osg::Quat mRotation; osg::Quat mRotation;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct bhkCMSBigTri struct bhkCMSBigTri
{ {
unsigned short mTriangle[3]; std::array<uint16_t, 3> mTriangle;
unsigned int mMaterial; uint32_t mMaterial;
unsigned short mWeldingInfo; uint16_t mWeldingInfo;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
struct bhkCMSChunk struct bhkCMSChunk
{ {
osg::Vec4f mTranslation; osg::Vec4f mTranslation;
unsigned int mMaterialIndex; uint32_t mMaterialIndex;
unsigned short mReference; uint16_t mReference;
unsigned short mTransformIndex; uint16_t mTransformIndex;
std::vector<unsigned short> mVertices; std::vector<uint16_t> mVertices;
std::vector<unsigned short> mIndices; std::vector<uint16_t> mIndices;
std::vector<unsigned short> mStrips; std::vector<uint16_t> mStrips;
std::vector<unsigned short> mWeldingInfos; std::vector<uint16_t> mWeldingInfos;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -180,12 +194,12 @@ namespace Nif
{ {
HavokFilter mHavokFilter; HavokFilter mHavokFilter;
hkResponseType mResponseType; hkResponseType mResponseType;
unsigned short mProcessContactDelay; uint16_t mProcessContactDelay;
osg::Vec4f mTranslation; osg::Vec4f mTranslation;
osg::Quat mRotation; osg::Quat mRotation;
osg::Vec4f mLinearVelocity; osg::Vec4f mLinearVelocity;
osg::Vec4f mAngularVelocity; osg::Vec4f mAngularVelocity;
float mInertiaTensor[3][4]; Matrix3 mInertiaTensor;
osg::Vec4f mCenter; osg::Vec4f mCenter;
float mMass; float mMass;
float mLinearDamping; float mLinearDamping;
@ -203,10 +217,11 @@ namespace Nif
bool mEnableDeactivation{ true }; bool mEnableDeactivation{ true };
hkSolverDeactivation mSolverDeactivation; hkSolverDeactivation mSolverDeactivation;
hkQualityType mQualityType; hkQualityType mQualityType;
unsigned char mAutoRemoveLevel; uint8_t mAutoRemoveLevel;
unsigned char mResponseModifierFlags; uint8_t mResponseModifierFlags;
unsigned char mNumContactPointShapeKeys; uint8_t mNumContactPointShapeKeys;
bool mForceCollidedOntoPPU; bool mForceCollidedOntoPPU;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -222,6 +237,7 @@ namespace Nif
bhkEntityPtr mEntityA; bhkEntityPtr mEntityA;
bhkEntityPtr mEntityB; bhkEntityPtr mEntityB;
ConstraintPriority mPriority; ConstraintPriority mPriority;
void read(NIFStream* nif); void read(NIFStream* nif);
void post(Reader& nif); void post(Reader& nif);
}; };
@ -242,6 +258,7 @@ namespace Nif
float mProportionalRecoveryVelocity; float mProportionalRecoveryVelocity;
float mConstantRecoveryVelocity; float mConstantRecoveryVelocity;
bool mEnabled; bool mEnabled;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -252,6 +269,7 @@ namespace Nif
float mTargetVelocity; float mTargetVelocity;
bool mUseVelocityTarget; bool mUseVelocityTarget;
bool mEnabled; bool mEnabled;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -261,6 +279,7 @@ namespace Nif
float mSpringConstant; float mSpringConstant;
float mSpringDamping; float mSpringDamping;
bool mEnabled; bool mEnabled;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -270,6 +289,7 @@ namespace Nif
bhkPositionConstraintMotor mPositionMotor; bhkPositionConstraintMotor mPositionMotor;
bhkVelocityConstraintMotor mVelocityMotor; bhkVelocityConstraintMotor mVelocityMotor;
bhkSpringDamperConstraintMotor mSpringDamperMotor; bhkSpringDamperConstraintMotor mSpringDamperMotor;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -289,6 +309,7 @@ namespace Nif
float mTwistMinAngle, mTwistMaxAngle; float mTwistMinAngle, mTwistMaxAngle;
float mMaxFriction; float mMaxFriction;
bhkConstraintMotorCInfo mMotor; bhkConstraintMotorCInfo mMotor;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -301,8 +322,10 @@ namespace Nif
osg::Vec4f mPerpAxis1; osg::Vec4f mPerpAxis1;
osg::Vec4f mPerpAxis2; osg::Vec4f mPerpAxis2;
}; };
HingeData mDataA; HingeData mDataA;
HingeData mDataB; HingeData mDataB;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -315,11 +338,13 @@ namespace Nif
osg::Vec4f mPerpAxis1; osg::Vec4f mPerpAxis1;
osg::Vec4f mPerpAxis2; osg::Vec4f mPerpAxis2;
}; };
HingeData mDataA; HingeData mDataA;
HingeData mDataB; HingeData mDataB;
float mMinAngle, mMaxAngle; float mMinAngle, mMaxAngle;
float mMaxFriction; float mMaxFriction;
bhkConstraintMotorCInfo mMotor; bhkConstraintMotorCInfo mMotor;
void read(NIFStream* nif); void read(NIFStream* nif);
}; };
@ -358,7 +383,7 @@ namespace Nif
// Bethesda Havok-specific collision object // Bethesda Havok-specific collision object
struct bhkCollisionObject : public NiCollisionObject struct bhkCollisionObject : public NiCollisionObject
{ {
unsigned short mFlags; uint16_t mFlags;
bhkWorldObjectPtr mBody; bhkWorldObjectPtr mBody;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -375,6 +400,7 @@ namespace Nif
bhkShapePtr mShape; bhkShapePtr mShape;
HavokFilter mHavokFilter; HavokFilter mHavokFilter;
bhkWorldObjectCInfo mWorldObjectInfo; bhkWorldObjectCInfo mWorldObjectInfo;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -383,6 +409,7 @@ namespace Nif
struct bhkEntity : public bhkWorldObject struct bhkEntity : public bhkWorldObject
{ {
bhkEntityCInfo mInfo; bhkEntityCInfo mInfo;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -391,6 +418,7 @@ namespace Nif
struct bhkBvTreeShape : public bhkShape struct bhkBvTreeShape : public bhkShape
{ {
bhkShapePtr mShape; bhkShapePtr mShape;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -400,6 +428,7 @@ namespace Nif
{ {
float mScale; float mScale;
hkpMoppCode mMopp; hkpMoppCode mMopp;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -408,10 +437,11 @@ namespace Nif
{ {
HavokMaterial mHavokMaterial; HavokMaterial mHavokMaterial;
float mRadius; float mRadius;
unsigned int mGrowBy; uint32_t mGrowBy;
osg::Vec4f mScale{ 1.f, 1.f, 1.f, 0.f }; osg::Vec4f mScale{ 1.f, 1.f, 1.f, 0.f };
NiTriStripsDataList mData; NiTriStripsDataList mData;
std::vector<unsigned int> mFilters; std::vector<HavokFilter> mHavokFilters;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -420,7 +450,7 @@ namespace Nif
struct bhkPackedNiTriStripsShape : public bhkShapeCollection struct bhkPackedNiTriStripsShape : public bhkShapeCollection
{ {
std::vector<hkSubPartData> mSubshapes; std::vector<hkSubPartData> mSubshapes;
unsigned int mUserData; uint32_t mUserData;
float mRadius; float mRadius;
osg::Vec4f mScale; osg::Vec4f mScale;
hkPackedNiTriStripsDataPtr mData; hkPackedNiTriStripsDataPtr mData;
@ -435,6 +465,7 @@ namespace Nif
std::vector<TriangleData> mTriangles; std::vector<TriangleData> mTriangles;
std::vector<osg::Vec3f> mVertices; std::vector<osg::Vec3f> mVertices;
std::vector<hkSubPartData> mSubshapes; std::vector<hkSubPartData> mSubshapes;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -442,6 +473,7 @@ namespace Nif
struct bhkSphereRepShape : public bhkShape struct bhkSphereRepShape : public bhkShape
{ {
HavokMaterial mHavokMaterial; HavokMaterial mHavokMaterial;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -449,6 +481,7 @@ namespace Nif
struct bhkConvexShape : public bhkSphereRepShape struct bhkConvexShape : public bhkSphereRepShape
{ {
float mRadius; float mRadius;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -459,6 +492,7 @@ namespace Nif
bhkWorldObjCInfoProperty mNormalsProperty; bhkWorldObjCInfoProperty mNormalsProperty;
std::vector<osg::Vec4f> mVertices; std::vector<osg::Vec4f> mVertices;
std::vector<osg::Vec4f> mNormals; std::vector<osg::Vec4f> mNormals;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -468,6 +502,7 @@ namespace Nif
HavokMaterial mHavokMaterial; HavokMaterial mHavokMaterial;
float mRadius; float mRadius;
osg::Matrixf mTransform; osg::Matrixf mTransform;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -476,6 +511,7 @@ namespace Nif
struct bhkBoxShape : public bhkConvexShape struct bhkBoxShape : public bhkConvexShape
{ {
osg::Vec3f mExtents; osg::Vec3f mExtents;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -499,28 +535,30 @@ namespace Nif
bhkWorldObjCInfoProperty mChildShapeProperty; bhkWorldObjCInfoProperty mChildShapeProperty;
bhkWorldObjCInfoProperty mChildFilterProperty; bhkWorldObjCInfoProperty mChildFilterProperty;
std::vector<HavokFilter> mHavokFilters; std::vector<HavokFilter> mHavokFilters;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct bhkCompressedMeshShape : public bhkShape struct bhkCompressedMeshShape : public bhkShape
{ {
NodePtr mTarget; NodePtr mTarget;
unsigned int mUserData; uint32_t mUserData;
float mRadius; float mRadius;
osg::Vec4f mScale; osg::Vec4f mScale;
bhkCompressedMeshShapeDataPtr mData; bhkCompressedMeshShapeDataPtr mData;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct bhkCompressedMeshShapeData : public bhkRefObject struct bhkCompressedMeshShapeData : public bhkRefObject
{ {
unsigned int mBitsPerIndex, mBitsPerWIndex; uint32_t mBitsPerIndex, mBitsPerWIndex;
unsigned int mMaskWIndex, mMaskIndex; uint32_t mMaskWIndex, mMaskIndex;
float mError; float mError;
osg::Vec4f mAabbMin, mAabbMax; osg::Vec4f mAabbMin, mAabbMax;
char mWeldingType; uint8_t mWeldingType;
char mMaterialType; uint8_t mMaterialType;
std::vector<bhkMeshMaterial> mMaterials; std::vector<bhkMeshMaterial> mMaterials;
std::vector<bhkQsTransform> mChunkTransforms; std::vector<bhkQsTransform> mChunkTransforms;
std::vector<osg::Vec4f> mBigVerts; std::vector<osg::Vec4f> mBigVerts;
@ -534,7 +572,7 @@ namespace Nif
{ {
bhkRigidBodyCInfo mInfo; bhkRigidBodyCInfo mInfo;
bhkSerializableList mConstraints; bhkSerializableList mConstraints;
unsigned int mBodyFlags; uint32_t mBodyFlags;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -542,6 +580,7 @@ namespace Nif
struct bhkSimpleShapePhantom : public bhkWorldObject struct bhkSimpleShapePhantom : public bhkWorldObject
{ {
osg::Matrixf mTransform; osg::Matrixf mTransform;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
@ -549,6 +588,7 @@ namespace Nif
struct bhkConstraint : public bhkSerializable struct bhkConstraint : public bhkSerializable
{ {
bhkConstraintCInfo mInfo; bhkConstraintCInfo mInfo;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
@ -556,18 +596,21 @@ namespace Nif
struct bhkRagdollConstraint : public bhkConstraint struct bhkRagdollConstraint : public bhkConstraint
{ {
bhkRagdollConstraintCInfo mConstraint; bhkRagdollConstraintCInfo mConstraint;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct bhkHingeConstraint : public bhkConstraint struct bhkHingeConstraint : public bhkConstraint
{ {
bhkHingeConstraintCInfo mConstraint; bhkHingeConstraintCInfo mConstraint;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct bhkLimitedHingeConstraint : public bhkConstraint struct bhkLimitedHingeConstraint : public bhkConstraint
{ {
bhkLimitedHingeConstraintCInfo mConstraint; bhkLimitedHingeConstraintCInfo mConstraint;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };

View file

@ -1,7 +1,7 @@
#include "property.hpp" #include "property.hpp"
#include "controlled.hpp"
#include "data.hpp" #include "data.hpp"
#include "texture.hpp"
namespace Nif namespace Nif
{ {

View file

@ -0,0 +1,47 @@
#include "texture.hpp"
#include "data.hpp"
namespace Nif
{
void NiSourceTexture::read(NIFStream* nif)
{
NiTexture::read(nif);
nif->read(mExternal);
if (mExternal || nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
nif->read(mFile);
bool hasData = nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 4);
if (!hasData && !mExternal)
nif->read(hasData);
if (hasData)
mData.read(nif);
mPrefs.mPixelLayout = static_cast<PixelLayout>(nif->get<uint32_t>());
mPrefs.mUseMipMaps = static_cast<MipMapFormat>(nif->get<uint32_t>());
mPrefs.mAlphaFormat = static_cast<AlphaFormat>(nif->get<uint32_t>());
nif->read(mIsStatic);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 103))
{
nif->read(mDirectRendering);
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 4))
nif->read(mPersistRenderData);
}
}
void NiSourceTexture::post(Reader& nif)
{
NiTexture::post(nif);
mData.post(nif);
}
void BSShaderTextureSet::read(NIFStream* nif)
{
nif->getSizedStrings(mTextures, nif->get<uint32_t>());
}
}

View file

@ -0,0 +1,81 @@
#ifndef OPENMW_COMPONENTS_NIF_TEXTURE_HPP
#define OPENMW_COMPONENTS_NIF_TEXTURE_HPP
#include "base.hpp"
namespace Nif
{
struct NiTexture : public Named
{
};
struct NiSourceTexture : public NiTexture
{
enum class PixelLayout : uint32_t
{
Palette = 0,
HighColor = 1,
TrueColor = 2,
Compressed = 3,
BumpMap = 4,
Default = 5,
};
enum class MipMapFormat : uint32_t
{
No = 0,
Yes = 1,
Default = 2,
};
enum class AlphaFormat : uint32_t
{
None = 0,
Binary = 1,
Smooth = 2,
Default = 3,
};
struct FormatPrefs
{
PixelLayout mPixelLayout;
MipMapFormat mUseMipMaps;
AlphaFormat mAlphaFormat;
};
char mExternal; // References external file
std::string mFile;
NiPixelDataPtr mData;
FormatPrefs mPrefs;
char mIsStatic{ 1 };
bool mDirectRendering{ true };
bool mPersistRenderData{ false };
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct BSShaderTextureSet : public Record
{
enum class TextureType : uint32_t
{
Base = 0,
Normal = 1,
Glow = 2,
Parallax = 3,
Environment = 4,
EnvironmentMask = 5,
Subsurface = 6,
BackLighting = 7,
};
std::vector<std::string> mTextures;
void read(NIFStream* nif) override;
};
}
#endif

View file

@ -305,7 +305,7 @@ namespace NifBullet
// Check for extra data // Check for extra data
std::vector<Nif::ExtraPtr> extraCollection; std::vector<Nif::ExtraPtr> extraCollection;
for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->next) for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->mNext)
extraCollection.emplace_back(e); extraCollection.emplace_back(e);
for (const auto& extraNode : node.extralist) for (const auto& extraNode : node.extralist)
if (!extraNode.empty()) if (!extraNode.empty())
@ -316,29 +316,30 @@ namespace NifBullet
{ {
// String markers may contain important information // String markers may contain important information
// affecting the entire subtree of this node // affecting the entire subtree of this node
Nif::NiStringExtraData* sd = (Nif::NiStringExtraData*)e.getPtr(); auto sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
if (Misc::StringUtils::ciStartsWith(sd->string, "NC")) if (Misc::StringUtils::ciStartsWith(sd->mData, "NC"))
{ {
// NCC flag in vanilla is partly case sensitive: prefix NC is case insensitive but second C needs be // NCC flag in vanilla is partly case sensitive: prefix NC is case insensitive but second C needs be
// uppercase // uppercase
if (sd->string.length() > 2 && sd->string[2] == 'C') if (sd->mData.length() > 2 && sd->mData[2] == 'C')
// Collide only with camera. // Collide only with camera.
visualCollisionType = Resource::VisualCollisionType::Camera; visualCollisionType = Resource::VisualCollisionType::Camera;
else else
// No collision. // No collision.
visualCollisionType = Resource::VisualCollisionType::Default; visualCollisionType = Resource::VisualCollisionType::Default;
} }
else if (sd->string == "MRK" && args.mAutogenerated) // Don't autogenerate collision if MRK is set.
// FIXME: verify if this covers the entire subtree
else if (sd->mData == "MRK" && args.mAutogenerated)
{ {
// Marker can still have collision if the model explicitely specifies it via a RootCollisionNode.
return; return;
} }
} }
else if (e->recType == Nif::RC_BSXFlags) else if (e->recType == Nif::RC_BSXFlags)
{ {
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr()); auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
if (bsxFlags->data & 32) // Editor marker flag if (bsxFlags->mData & 32) // Editor marker flag
args.mHasMarkers = true; args.mHasMarkers = true;
} }
} }

View file

@ -42,13 +42,14 @@
#include <osg/TexEnvCombine> #include <osg/TexEnvCombine>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/nif/controlled.hpp>
#include <components/nif/effect.hpp> #include <components/nif/effect.hpp>
#include <components/nif/exception.hpp> #include <components/nif/exception.hpp>
#include <components/nif/extra.hpp> #include <components/nif/extra.hpp>
#include <components/nif/niffile.hpp> #include <components/nif/niffile.hpp>
#include <components/nif/node.hpp> #include <components/nif/node.hpp>
#include <components/nif/particle.hpp>
#include <components/nif/property.hpp> #include <components/nif/property.hpp>
#include <components/nif/texture.hpp>
#include <components/sceneutil/depth.hpp> #include <components/sceneutil/depth.hpp>
#include <components/sceneutil/morphgeometry.hpp> #include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp> #include <components/sceneutil/riggeometry.hpp>
@ -174,16 +175,16 @@ namespace
void extractTextKeys(const Nif::NiTextKeyExtraData* tk, SceneUtil::TextKeyMap& textkeys) void extractTextKeys(const Nif::NiTextKeyExtraData* tk, SceneUtil::TextKeyMap& textkeys)
{ {
for (size_t i = 0; i < tk->list.size(); i++) for (const Nif::NiTextKeyExtraData::TextKey& key : tk->mList)
{ {
std::vector<std::string> results; std::vector<std::string> results;
Misc::StringUtils::split(tk->list[i].text, results, "\r\n"); Misc::StringUtils::split(key.mText, results, "\r\n");
for (std::string& result : results) for (std::string& result : results)
{ {
Misc::StringUtils::trim(result); Misc::StringUtils::trim(result);
Misc::StringUtils::lowerCaseInPlace(result); Misc::StringUtils::lowerCaseInPlace(result);
if (!result.empty()) if (!result.empty())
textkeys.emplace(tk->list[i].time, std::move(result)); textkeys.emplace(key.mTime, std::move(result));
} }
} }
} }
@ -285,9 +286,9 @@ namespace NifOsg
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys); extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys);
extra = extra->next; extra = extra->mNext;
Nif::ControllerPtr ctrl = seq->controller; Nif::ControllerPtr ctrl = seq->controller;
for (; !extra.empty() && !ctrl.empty(); (extra = extra->next), (ctrl = ctrl->next)) for (; !extra.empty() && !ctrl.empty(); (extra = extra->mNext), (ctrl = ctrl->next))
{ {
if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{ {
@ -315,8 +316,8 @@ namespace NifOsg
osg::ref_ptr<SceneUtil::KeyframeController> callback = new NifOsg::KeyframeController(key); osg::ref_ptr<SceneUtil::KeyframeController> callback = new NifOsg::KeyframeController(key);
setupController(key, callback, /*animflags*/ 0); setupController(key, callback, /*animflags*/ 0);
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second) if (!target.mKeyframeControllers.emplace(strdata->mData, callback).second)
Log(Debug::Verbose) << "Controller " << strdata->string << " present more than once in " Log(Debug::Verbose) << "Controller " << strdata->mData << " present more than once in "
<< nif.getFilename() << ", ignoring later version"; << nif.getFilename() << ", ignoring later version";
} }
} }
@ -509,15 +510,15 @@ namespace NifOsg
return nullptr; return nullptr;
osg::ref_ptr<osg::Image> image; osg::ref_ptr<osg::Image> image;
if (!st->external && !st->data.empty()) if (st->mExternal)
{ {
image = handleInternalTexture(st->data.getPtr()); std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, imageManager->getVFS());
}
else
{
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS());
image = imageManager->getImage(filename); image = imageManager->getImage(filename);
} }
else if (!st->mData.empty())
{
image = handleInternalTexture(st->mData.getPtr());
}
return image; return image;
} }
@ -536,38 +537,41 @@ namespace NifOsg
} }
const Nif::NiTextureEffect* textureEffect = static_cast<const Nif::NiTextureEffect*>(nifNode); const Nif::NiTextureEffect* textureEffect = static_cast<const Nif::NiTextureEffect*>(nifNode);
if (textureEffect->textureType != Nif::NiTextureEffect::Environment_Map) if (!textureEffect->mSwitchState)
return false;
if (textureEffect->mTextureType != Nif::NiTextureEffect::TextureType::EnvironmentMap)
{ {
Log(Debug::Info) << "Unhandled NiTextureEffect type " << textureEffect->textureType << " in " Log(Debug::Info) << "Unhandled NiTextureEffect type "
<< mFilename; << static_cast<uint32_t>(textureEffect->mTextureType) << " in " << mFilename;
return false; return false;
} }
if (textureEffect->texture.empty()) if (textureEffect->mTexture.empty())
{ {
Log(Debug::Info) << "NiTextureEffect missing source texture in " << mFilename; Log(Debug::Info) << "NiTextureEffect missing source texture in " << mFilename;
return false; return false;
} }
osg::ref_ptr<osg::TexGen> texGen(new osg::TexGen); osg::ref_ptr<osg::TexGen> texGen(new osg::TexGen);
switch (textureEffect->coordGenType) switch (textureEffect->mCoordGenType)
{ {
case Nif::NiTextureEffect::World_Parallel: case Nif::NiTextureEffect::CoordGenType::WorldParallel:
texGen->setMode(osg::TexGen::OBJECT_LINEAR); texGen->setMode(osg::TexGen::OBJECT_LINEAR);
break; break;
case Nif::NiTextureEffect::World_Perspective: case Nif::NiTextureEffect::CoordGenType::WorldPerspective:
texGen->setMode(osg::TexGen::EYE_LINEAR); texGen->setMode(osg::TexGen::EYE_LINEAR);
break; break;
case Nif::NiTextureEffect::Sphere_Map: case Nif::NiTextureEffect::CoordGenType::SphereMap:
texGen->setMode(osg::TexGen::SPHERE_MAP); texGen->setMode(osg::TexGen::SPHERE_MAP);
break; break;
default: default:
Log(Debug::Info) << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType Log(Debug::Info) << "Unhandled NiTextureEffect CoordGenType "
<< " in " << mFilename; << static_cast<uint32_t>(textureEffect->mCoordGenType) << " in " << mFilename;
return false; return false;
} }
osg::ref_ptr<osg::Image> image(handleSourceTexture(textureEffect->texture.getPtr(), imageManager)); osg::ref_ptr<osg::Image> image(handleSourceTexture(textureEffect->mTexture.getPtr(), imageManager));
osg::ref_ptr<osg::Texture2D> texture2d(new osg::Texture2D(image)); osg::ref_ptr<osg::Texture2D> texture2d(new osg::Texture2D(image));
if (image) if (image)
texture2d->setTextureSize(image->s(), image->t()); texture2d->setTextureSize(image->s(), image->t());
@ -644,7 +648,7 @@ namespace NifOsg
std::vector<Nif::ExtraPtr> extraCollection; std::vector<Nif::ExtraPtr> extraCollection;
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->mNext)
extraCollection.emplace_back(e); extraCollection.emplace_back(e);
for (const auto& extraNode : nifNode->extralist) for (const auto& extraNode : nifNode->extralist)
@ -666,25 +670,25 @@ namespace NifOsg
// String markers may contain important information // String markers may contain important information
// affecting the entire subtree of this obj // affecting the entire subtree of this obj
if (sd->string == "MRK" && !Loader::getShowMarkers()) if (sd->mData == "MRK" && !Loader::getShowMarkers())
{ {
// Marker objects. These meshes are only visible in the editor. // Marker objects. These meshes are only visible in the editor.
args.mHasMarkers = true; args.mHasMarkers = true;
} }
else if (sd->string == "BONE") else if (sd->mData == "BONE")
{ {
node->getOrCreateUserDataContainer()->addDescription("CustomBone"); node->getOrCreateUserDataContainer()->addDescription("CustomBone");
} }
else if (sd->string.rfind(extraDataIdentifer, 0) == 0) else if (sd->mData.rfind(extraDataIdentifer, 0) == 0)
{ {
node->setUserValue( node->setUserValue(
Misc::OsgUserValues::sExtraData, sd->string.substr(extraDataIdentifer.length())); Misc::OsgUserValues::sExtraData, sd->mData.substr(extraDataIdentifer.length()));
} }
} }
else if (e->recType == Nif::RC_BSXFlags) else if (e->recType == Nif::RC_BSXFlags)
{ {
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr()); auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
if (bsxFlags->data & 32) // Editor marker flag if (bsxFlags->mData & 32) // Editor marker flag
args.mHasMarkers = true; args.mHasMarkers = true;
} }
} }
@ -895,7 +899,7 @@ namespace NifOsg
if (!key->mInterpolator.empty() && key->mInterpolator->recType != Nif::RC_NiTransformInterpolator) if (!key->mInterpolator.empty() && key->mInterpolator->recType != Nif::RC_NiTransformInterpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiKeyframeController " << key->recIndex Log(Debug::Error) << "Unsupported interpolator type for NiKeyframeController " << key->recIndex
<< " in " << mFilename; << " in " << mFilename << ": " << key->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<KeyframeController> callback = new KeyframeController(key); osg::ref_ptr<KeyframeController> callback = new KeyframeController(key);
@ -922,7 +926,7 @@ namespace NifOsg
&& visctrl->mInterpolator->recType != Nif::RC_NiBoolInterpolator) && visctrl->mInterpolator->recType != Nif::RC_NiBoolInterpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiVisController " << visctrl->recIndex Log(Debug::Error) << "Unsupported interpolator type for NiVisController " << visctrl->recIndex
<< " in " << mFilename; << " in " << mFilename << ": " << visctrl->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<VisController> callback(new VisController(visctrl, Loader::getHiddenNodeMask())); osg::ref_ptr<VisController> callback(new VisController(visctrl, Loader::getHiddenNodeMask()));
@ -938,7 +942,7 @@ namespace NifOsg
&& rollctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator) && rollctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiRollController " << rollctrl->recIndex Log(Debug::Error) << "Unsupported interpolator type for NiRollController " << rollctrl->recIndex
<< " in " << mFilename; << " in " << mFilename << ": " << rollctrl->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<RollController> callback = new RollController(rollctrl); osg::ref_ptr<RollController> callback = new RollController(rollctrl);
@ -973,8 +977,9 @@ namespace NifOsg
if (!alphactrl->mInterpolator.empty() if (!alphactrl->mInterpolator.empty()
&& alphactrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator) && alphactrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiAlphaController " Log(Debug::Error)
<< alphactrl->recIndex << " in " << mFilename; << "Unsupported interpolator type for NiAlphaController " << alphactrl->recIndex << " in "
<< mFilename << ": " << alphactrl->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<AlphaController> osgctrl = new AlphaController(alphactrl, baseMaterial); osg::ref_ptr<AlphaController> osgctrl = new AlphaController(alphactrl, baseMaterial);
@ -994,8 +999,9 @@ namespace NifOsg
if (!matctrl->mInterpolator.empty() if (!matctrl->mInterpolator.empty()
&& matctrl->mInterpolator->recType != Nif::RC_NiPoint3Interpolator) && matctrl->mInterpolator->recType != Nif::RC_NiPoint3Interpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiMaterialColorController " Log(Debug::Error)
<< matctrl->recIndex << " in " << mFilename; << "Unsupported interpolator type for NiMaterialColorController " << matctrl->recIndex
<< " in " << mFilename << ": " << matctrl->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<MaterialColorController> osgctrl = new MaterialColorController(matctrl, baseMaterial); osg::ref_ptr<MaterialColorController> osgctrl = new MaterialColorController(matctrl, baseMaterial);
@ -1021,7 +1027,7 @@ namespace NifOsg
&& flipctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator) && flipctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
{ {
Log(Debug::Error) << "Unsupported interpolator type for NiFlipController " << flipctrl->recIndex Log(Debug::Error) << "Unsupported interpolator type for NiFlipController " << flipctrl->recIndex
<< " in " << mFilename; << " in " << mFilename << ": " << flipctrl->mInterpolator->recName;
continue; continue;
} }
std::vector<osg::ref_ptr<osg::Texture2D>> textures; std::vector<osg::ref_ptr<osg::Texture2D>> textures;
@ -1067,12 +1073,12 @@ namespace NifOsg
attachTo->addChild(program); attachTo->addChild(program);
program->setParticleSystem(partsys); program->setParticleSystem(partsys);
program->setReferenceFrame(rf); program->setReferenceFrame(rf);
for (; !affectors.empty(); affectors = affectors->next) for (; !affectors.empty(); affectors = affectors->mNext)
{ {
if (affectors->recType == Nif::RC_NiParticleGrowFade) if (affectors->recType == Nif::RC_NiParticleGrowFade)
{ {
const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(affectors.getPtr()); const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(affectors.getPtr());
program->addOperator(new GrowFadeAffector(gf->growTime, gf->fadeTime)); program->addOperator(new GrowFadeAffector(gf->mGrowTime, gf->mFadeTime));
} }
else if (affectors->recType == Nif::RC_NiGravity) else if (affectors->recType == Nif::RC_NiGravity)
{ {
@ -1083,9 +1089,9 @@ namespace NifOsg
{ {
const Nif::NiParticleColorModifier* cl const Nif::NiParticleColorModifier* cl
= static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr()); = static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr());
if (cl->data.empty()) if (cl->mData.empty())
continue; continue;
const Nif::NiColorData* clrdata = cl->data.getPtr(); const Nif::NiColorData* clrdata = cl->mData.getPtr();
program->addOperator(new ParticleColorAffector(clrdata)); program->addOperator(new ParticleColorAffector(clrdata));
} }
else if (affectors->recType == Nif::RC_NiParticleRotation) else if (affectors->recType == Nif::RC_NiParticleRotation)
@ -1095,7 +1101,7 @@ namespace NifOsg
else else
Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename;
} }
for (; !colliders.empty(); colliders = colliders->next) for (; !colliders.empty(); colliders = colliders->mNext)
{ {
if (colliders->recType == Nif::RC_NiPlanarCollider) if (colliders->recType == Nif::RC_NiPlanarCollider)
{ {
@ -2008,15 +2014,15 @@ namespace NifOsg
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
for (size_t i = 0; i < textureSet->textures.size(); ++i) for (size_t i = 0; i < textureSet->mTextures.size(); ++i)
{ {
if (textureSet->textures[i].empty()) if (textureSet->mTextures[i].empty())
continue; continue;
switch (i) switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
{ {
case Nif::BSShaderTextureSet::TextureType_Base: case Nif::BSShaderTextureSet::TextureType::Base:
case Nif::BSShaderTextureSet::TextureType_Normal: case Nif::BSShaderTextureSet::TextureType::Normal:
case Nif::BSShaderTextureSet::TextureType_Glow: case Nif::BSShaderTextureSet::TextureType::Glow:
break; break;
default: default:
{ {
@ -2026,7 +2032,7 @@ namespace NifOsg
} }
} }
std::string filename std::string filename
= Misc::ResourceHelpers::correctTexturePath(textureSet->textures[i], imageManager->getVFS()); = Misc::ResourceHelpers::correctTexturePath(textureSet->mTextures[i], imageManager->getVFS());
osg::ref_ptr<osg::Image> image = imageManager->getImage(filename); osg::ref_ptr<osg::Image> image = imageManager->getImage(filename);
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image); osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D(image);
if (image) if (image)
@ -2035,17 +2041,19 @@ namespace NifOsg
unsigned int texUnit = boundTextures.size(); unsigned int texUnit = boundTextures.size();
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
// BSShaderTextureSet presence means there's no need for FFP support for the affected node // BSShaderTextureSet presence means there's no need for FFP support for the affected node
switch (i) switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
{ {
case Nif::BSShaderTextureSet::TextureType_Base: case Nif::BSShaderTextureSet::TextureType::Base:
texture2d->setName("diffuseMap"); texture2d->setName("diffuseMap");
break; break;
case Nif::BSShaderTextureSet::TextureType_Normal: case Nif::BSShaderTextureSet::TextureType::Normal:
texture2d->setName("normalMap"); texture2d->setName("normalMap");
break; break;
case Nif::BSShaderTextureSet::TextureType_Glow: case Nif::BSShaderTextureSet::TextureType::Glow:
texture2d->setName("emissiveMap"); texture2d->setName("emissiveMap");
break; break;
default:
break;
} }
boundTextures.emplace_back(uvSet); boundTextures.emplace_back(uvSet);
} }

View file

@ -9,7 +9,6 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/nif/controlled.hpp>
#include <components/nif/data.hpp> #include <components/nif/data.hpp>
#include <components/sceneutil/morphgeometry.hpp> #include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp> #include <components/sceneutil/riggeometry.hpp>
@ -281,20 +280,13 @@ namespace NifOsg
GravityAffector::GravityAffector(const Nif::NiGravity* gravity) GravityAffector::GravityAffector(const Nif::NiGravity* gravity)
: mForce(gravity->mForce) : mForce(gravity->mForce)
, mType(static_cast<ForceType>(gravity->mType)) , mType(gravity->mType)
, mPosition(gravity->mPosition) , mPosition(gravity->mPosition)
, mDirection(gravity->mDirection) , mDirection(gravity->mDirection)
, mDecay(gravity->mDecay) , mDecay(gravity->mDecay)
{ {
} }
GravityAffector::GravityAffector()
: mForce(0)
, mType(Type_Wind)
, mDecay(0.f)
{
}
GravityAffector::GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop) GravityAffector::GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop)
: osgParticle::Operator(copy, copyop) : osgParticle::Operator(copy, copyop)
{ {
@ -311,8 +303,8 @@ namespace NifOsg
{ {
bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF); bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);
if (mType == Type_Point // We don't need the position for Wind gravity, except if decay is being applied
|| mDecay != 0.f) // we don't need the position for Wind gravity, except if decay is being applied if (mType == Nif::NiGravity::ForceType::Point || mDecay != 0.f)
mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition; mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition;
mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection; mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
@ -324,7 +316,7 @@ namespace NifOsg
const float magic = 1.6f; const float magic = 1.6f;
switch (mType) switch (mType)
{ {
case Type_Wind: case Nif::NiGravity::ForceType::Wind:
{ {
float decayFactor = 1.f; float decayFactor = 1.f;
if (mDecay != 0.f) if (mDecay != 0.f)
@ -338,7 +330,7 @@ namespace NifOsg
break; break;
} }
case Type_Point: case Nif::NiGravity::ForceType::Point:
{ {
osg::Vec3f diff = mCachedWorldPosition - particle->getPosition(); osg::Vec3f diff = mCachedWorldPosition - particle->getPosition();

View file

@ -10,15 +10,14 @@
#include <osgParticle/Placer> #include <osgParticle/Placer>
#include <osgParticle/Shooter> #include <osgParticle/Shooter>
#include <components/nif/particle.hpp> // NiGravity::ForceType
#include <components/sceneutil/nodecallback.hpp> #include <components/sceneutil/nodecallback.hpp>
#include "controller.hpp" // ValueInterpolator #include "controller.hpp" // ValueInterpolator
namespace Nif namespace Nif
{ {
struct NiGravity;
struct NiPlanarCollider;
struct NiSphericalCollider;
struct NiColorData; struct NiColorData;
} }
@ -180,7 +179,7 @@ namespace NifOsg
{ {
public: public:
GravityAffector(const Nif::NiGravity* gravity); GravityAffector(const Nif::NiGravity* gravity);
GravityAffector(); GravityAffector() = default;
GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
GravityAffector& operator=(const GravityAffector&) = delete; GravityAffector& operator=(const GravityAffector&) = delete;
@ -191,16 +190,11 @@ namespace NifOsg
void beginOperate(osgParticle::Program*) override; void beginOperate(osgParticle::Program*) override;
private: private:
float mForce; float mForce{ 0.f };
enum ForceType Nif::NiGravity::ForceType mType{ Nif::NiGravity::ForceType::Wind };
{
Type_Wind,
Type_Point
};
ForceType mType;
osg::Vec3f mPosition; osg::Vec3f mPosition;
osg::Vec3f mDirection; osg::Vec3f mDirection;
float mDecay; float mDecay{ 0.f };
osg::Vec3f mCachedWorldPosition; osg::Vec3f mCachedWorldPosition;
osg::Vec3f mCachedWorldDirection; osg::Vec3f mCachedWorldDirection;
}; };

View file

@ -32,7 +32,9 @@ namespace Resource
}; };
NifFileManager::NifFileManager(const VFS::Manager* vfs) NifFileManager::NifFileManager(const VFS::Manager* vfs)
: ResourceManager(vfs) // NIF files aren't needed any more once the converted objects are cached in SceneManager / BulletShapeManager,
// so no point in using an expiry delay.
: ResourceManager(vfs, 0)
{ {
} }

View file

@ -62,9 +62,9 @@ namespace Resource
{ {
// If ref count is greater than 1, the object has an external reference. // If ref count is greater than 1, the object has an external reference.
// If the timestamp is yet to be initialized, it needs to be updated too. // If the timestamp is yet to be initialized, it needs to be updated too.
if ((itr->second.first != nullptr && itr->second.first->referenceCount() > 1) if ((itr->second.mValue != nullptr && itr->second.mValue->referenceCount() > 1)
|| itr->second.second == 0.0) || itr->second.mLastUsage == 0.0)
itr->second.second = referenceTime; itr->second.mLastUsage = referenceTime;
} }
} }
@ -81,10 +81,10 @@ namespace Resource
typename ObjectCacheMap::iterator oitr = _objectCache.begin(); typename ObjectCacheMap::iterator oitr = _objectCache.begin();
while (oitr != _objectCache.end()) while (oitr != _objectCache.end())
{ {
if (oitr->second.second <= expiryTime) if (oitr->second.mLastUsage <= expiryTime)
{ {
if (oitr->second.first != nullptr) if (oitr->second.mValue != nullptr)
objectsToRemove.push_back(oitr->second.first); objectsToRemove.push_back(std::move(oitr->second.mValue));
_objectCache.erase(oitr++); _objectCache.erase(oitr++);
} }
else else
@ -106,7 +106,7 @@ namespace Resource
void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0) void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0)
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(_objectCacheMutex);
_objectCache[key] = ObjectTimeStampPair(object, timestamp); _objectCache[key] = Item{ object, timestamp };
} }
/** Remove Object from cache.*/ /** Remove Object from cache.*/
@ -124,7 +124,7 @@ namespace Resource
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(_objectCacheMutex);
typename ObjectCacheMap::iterator itr = _objectCache.find(key); typename ObjectCacheMap::iterator itr = _objectCache.find(key);
if (itr != _objectCache.end()) if (itr != _objectCache.end())
return itr->second.first; return itr->second.mValue;
else else
return nullptr; return nullptr;
} }
@ -135,7 +135,7 @@ namespace Resource
const auto it = _objectCache.find(key); const auto it = _objectCache.find(key);
if (it == _objectCache.end()) if (it == _objectCache.end())
return std::nullopt; return std::nullopt;
return it->second.first; return it->second.mValue;
} }
/** Check if an object is in the cache, and if it is, update its usage time stamp. */ /** Check if an object is in the cache, and if it is, update its usage time stamp. */
@ -145,7 +145,7 @@ namespace Resource
typename ObjectCacheMap::iterator itr = _objectCache.find(key); typename ObjectCacheMap::iterator itr = _objectCache.find(key);
if (itr != _objectCache.end()) if (itr != _objectCache.end())
{ {
itr->second.second = timeStamp; itr->second.mLastUsage = timeStamp;
return true; return true;
} }
else else
@ -158,7 +158,7 @@ namespace Resource
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(_objectCacheMutex);
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
{ {
osg::Object* object = itr->second.first.get(); osg::Object* object = itr->second.mValue.get();
object->releaseGLObjects(state); object->releaseGLObjects(state);
} }
} }
@ -169,8 +169,7 @@ namespace Resource
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(_objectCacheMutex);
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
{ {
osg::Object* object = itr->second.first.get(); if (osg::Object* object = itr->second.mValue.get())
if (object)
{ {
osg::Node* node = dynamic_cast<osg::Node*>(object); osg::Node* node = dynamic_cast<osg::Node*>(object);
if (node) if (node)
@ -185,7 +184,7 @@ namespace Resource
{ {
std::lock_guard<std::mutex> lock(_objectCacheMutex); std::lock_guard<std::mutex> lock(_objectCacheMutex);
for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it)
f(it->first, it->second.first.get()); f(it->first, it->second.mValue.get());
} }
/** Get the number of objects in the cache. */ /** Get the number of objects in the cache. */
@ -195,11 +194,26 @@ namespace Resource
return _objectCache.size(); return _objectCache.size();
} }
template <class K>
std::optional<std::pair<KeyType, osg::ref_ptr<osg::Object>>> lowerBound(K&& key)
{
const std::lock_guard<std::mutex> lock(_objectCacheMutex);
const auto it = _objectCache.lower_bound(std::forward<K>(key));
if (it == _objectCache.end())
return std::nullopt;
return std::pair(it->first, it->second.mValue);
}
protected: protected:
struct Item
{
osg::ref_ptr<osg::Object> mValue;
double mLastUsage;
};
virtual ~GenericObjectCache() {} virtual ~GenericObjectCache() {}
typedef std::pair<osg::ref_ptr<osg::Object>, double> ObjectTimeStampPair; using ObjectCacheMap = std::map<KeyType, Item, std::less<>>;
typedef std::map<KeyType, ObjectTimeStampPair> ObjectCacheMap;
ObjectCacheMap _objectCache; ObjectCacheMap _objectCache;
mutable std::mutex _objectCacheMutex; mutable std::mutex _objectCacheMutex;

View file

@ -3,6 +3,8 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <components/settings/values.hpp>
#include "objectcache.hpp" #include "objectcache.hpp"
namespace VFS namespace VFS
@ -23,11 +25,11 @@ namespace Resource
{ {
public: public:
virtual ~BaseResourceManager() = default; virtual ~BaseResourceManager() = default;
virtual void updateCache(double referenceTime) {} virtual void updateCache(double referenceTime) = 0;
virtual void clearCache() {} virtual void clearCache() = 0;
virtual void setExpiryDelay(double expiryDelay) {} virtual void setExpiryDelay(double expiryDelay) = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const = 0;
virtual void releaseGLObjects(osg::State* state) {} virtual void releaseGLObjects(osg::State* state) = 0;
}; };
/// @brief Base class for managers that require a virtual file system and object cache. /// @brief Base class for managers that require a virtual file system and object cache.
@ -39,10 +41,11 @@ namespace Resource
public: public:
typedef GenericObjectCache<KeyType> CacheType; typedef GenericObjectCache<KeyType> CacheType;
GenericResourceManager(const VFS::Manager* vfs) explicit GenericResourceManager(
const VFS::Manager* vfs, double expiryDelay = Settings::cells().mCacheExpiryDelay)
: mVFS(vfs) : mVFS(vfs)
, mCache(new CacheType) , mCache(new CacheType)
, mExpiryDelay(0.0) , mExpiryDelay(expiryDelay)
{ {
} }
@ -59,7 +62,7 @@ namespace Resource
void clearCache() override { mCache->clear(); } void clearCache() override { mCache->clear(); }
/// How long to keep objects in cache after no longer being referenced. /// How long to keep objects in cache after no longer being referenced.
void setExpiryDelay(double expiryDelay) override { mExpiryDelay = expiryDelay; } void setExpiryDelay(double expiryDelay) final { mExpiryDelay = expiryDelay; }
double getExpiryDelay() const { return mExpiryDelay; } double getExpiryDelay() const { return mExpiryDelay; }
const VFS::Manager* getVFS() const { return mVFS; } const VFS::Manager* getVFS() const { return mVFS; }
@ -77,10 +80,15 @@ namespace Resource
class ResourceManager : public GenericResourceManager<std::string> class ResourceManager : public GenericResourceManager<std::string>
{ {
public: public:
ResourceManager(const VFS::Manager* vfs) explicit ResourceManager(const VFS::Manager* vfs)
: GenericResourceManager<std::string>(vfs) : GenericResourceManager<std::string>(vfs)
{ {
} }
explicit ResourceManager(const VFS::Manager* vfs, double expiryDelay)
: GenericResourceManager<std::string>(vfs, expiryDelay)
{
}
}; };
} }

View file

@ -21,7 +21,7 @@ namespace Terrain
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
CompositeMapRenderer* renderer, ESM::RefId worldspace) CompositeMapRenderer* renderer, ESM::RefId worldspace)
: GenericResourceManager<ChunkId>(nullptr) : GenericResourceManager<ChunkKey>(nullptr)
, QuadTreeWorld::ChunkManager(worldspace) , QuadTreeWorld::ChunkManager(worldspace)
, mStorage(storage) , mStorage(storage)
, mSceneManager(sceneMgr) , mSceneManager(sceneMgr)
@ -39,38 +39,26 @@ namespace Terrain
mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON); mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON);
} }
struct FindChunkTemplate
{
void operator()(ChunkId id, osg::Object* obj)
{
if (std::get<0>(id) == std::get<0>(mId) && std::get<1>(id) == std::get<1>(mId))
mFoundTemplate = obj;
}
ChunkId mId;
osg::ref_ptr<osg::Object> mFoundTemplate;
};
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod, osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod,
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{ {
// Override lod with the vertexLodMod adjusted value. // Override lod with the vertexLodMod adjusted value.
// TODO: maybe we can refactor this code by moving all vertexLodMod code into this class. // TODO: maybe we can refactor this code by moving all vertexLodMod code into this class.
lod = static_cast<unsigned char>(lodFlags >> (4 * 4)); lod = static_cast<unsigned char>(lodFlags >> (4 * 4));
ChunkId id = std::make_tuple(center, lod, lodFlags);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id); const ChunkKey key{ .mCenter = center, .mLod = lod, .mLodFlags = lodFlags };
if (obj) if (osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(key))
return static_cast<osg::Node*>(obj.get()); return static_cast<osg::Node*>(obj.get());
else
{ const TerrainDrawable* templateGeometry = nullptr;
FindChunkTemplate find; const TemplateKey templateKey{ .mCenter = center, .mLod = lod };
find.mId = id; const auto pair = mCache->lowerBound(templateKey);
mCache->call(find); if (pair.has_value() && templateKey == TemplateKey{ .mCenter = pair->first.mCenter, .mLod = pair->first.mLod })
TerrainDrawable* templateGeometry templateGeometry = static_cast<const TerrainDrawable*>(pair->second.get());
= find.mFoundTemplate ? static_cast<TerrainDrawable*>(find.mFoundTemplate.get()) : nullptr;
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry); osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry);
mCache->addEntryToObjectCache(id, node.get()); mCache->addEntryToObjectCache(key, node.get());
return node; return node;
}
} }
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
@ -80,14 +68,14 @@ namespace Terrain
void ChunkManager::clearCache() void ChunkManager::clearCache()
{ {
GenericResourceManager<ChunkId>::clearCache(); GenericResourceManager<ChunkKey>::clearCache();
mBufferCache.clearCache(); mBufferCache.clearCache();
} }
void ChunkManager::releaseGLObjects(osg::State* state) void ChunkManager::releaseGLObjects(osg::State* state)
{ {
GenericResourceManager<ChunkId>::releaseGLObjects(state); GenericResourceManager<ChunkKey>::releaseGLObjects(state);
mBufferCache.releaseGLObjects(state); mBufferCache.releaseGLObjects(state);
} }
@ -202,7 +190,7 @@ namespace Terrain
} }
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod, osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod,
unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry) unsigned int lodFlags, bool compile, const TerrainDrawable* templateGeometry)
{ {
osg::ref_ptr<TerrainDrawable> geometry(new TerrainDrawable); osg::ref_ptr<TerrainDrawable> geometry(new TerrainDrawable);

View file

@ -28,10 +28,51 @@ namespace Terrain
class CompositeMap; class CompositeMap;
class TerrainDrawable; class TerrainDrawable;
typedef std::tuple<osg::Vec2f, unsigned char, unsigned int> ChunkId; // Center, Lod, Lod Flags struct TemplateKey
{
osg::Vec2f mCenter;
unsigned char mLod;
};
inline auto tie(const TemplateKey& v)
{
return std::tie(v.mCenter, v.mLod);
}
inline bool operator<(const TemplateKey& l, const TemplateKey& r)
{
return tie(l) < tie(r);
}
inline bool operator==(const TemplateKey& l, const TemplateKey& r)
{
return tie(l) == tie(r);
}
struct ChunkKey
{
osg::Vec2f mCenter;
unsigned char mLod;
unsigned mLodFlags;
};
inline auto tie(const ChunkKey& v)
{
return std::tie(v.mCenter, v.mLod, v.mLodFlags);
}
inline bool operator<(const ChunkKey& l, const ChunkKey& r)
{
return tie(l) < tie(r);
}
inline bool operator<(const ChunkKey& l, const TemplateKey& r)
{
return TemplateKey{ .mCenter = l.mCenter, .mLod = l.mLod } < r;
}
/// @brief Handles loading and caching of terrain chunks /// @brief Handles loading and caching of terrain chunks
class ChunkManager : public Resource::GenericResourceManager<ChunkId>, public QuadTreeWorld::ChunkManager class ChunkManager : public Resource::GenericResourceManager<ChunkKey>, public QuadTreeWorld::ChunkManager
{ {
public: public:
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
@ -55,7 +96,7 @@ namespace Terrain
private: private:
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod, osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod,
unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry); unsigned int lodFlags, bool compile, const TerrainDrawable* templateGeometry);
osg::ref_ptr<osg::Texture2D> createCompositeMapRTT(); osg::ref_ptr<osg::Texture2D> createCompositeMapRTT();

View file

@ -544,37 +544,16 @@ namespace Terrain
vd->setViewPoint(viewPoint); vd->setViewPoint(viewPoint);
vd->setActiveGrid(grid); vd->setActiveGrid(grid);
for (unsigned int pass = 0; pass < 3; ++pass) DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid, cellWorldSize);
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
reporter.addTotal(vd->getNumEntries());
for (unsigned int i = 0, n = vd->getNumEntries(); i < n && !abort; ++i)
{ {
unsigned int startEntry = vd->getNumEntries(); ViewDataEntry& entry = vd->getEntry(i);
loadRenderingNode(entry, vd, cellWorldSize, grid, true);
float distanceModifier = 0.f; reporter.addProgress(1);
if (pass == 1)
distanceModifier = 1024;
else if (pass == 2)
distanceModifier = -1024;
DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid, cellWorldSize, distanceModifier);
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
if (pass == 0)
{
std::size_t progressTotal = 0;
for (unsigned int i = 0, n = vd->getNumEntries(); i < n; ++i)
progressTotal += vd->getEntry(i).mNode->getSize();
reporter.addTotal(progressTotal);
}
for (unsigned int i = startEntry; i < vd->getNumEntries() && !abort; ++i)
{
ViewDataEntry& entry = vd->getEntry(i);
loadRenderingNode(entry, vd, cellWorldSize, grid, true);
if (pass == 0)
reporter.addProgress(entry.mNode->getSize());
vd->removeNodeFromIndex(entry.mNode);
entry.mNode = nullptr; // Clear node lest we break the neighbours search for the next pass
}
} }
} }

View file

@ -60,7 +60,7 @@ namespace Terrain
const osg::BoundingBox& getWaterBoundingBox() const { return mWaterBoundingBox; } const osg::BoundingBox& getWaterBoundingBox() const { return mWaterBoundingBox; }
void setCompositeMap(CompositeMap* map) { mCompositeMap = map; } void setCompositeMap(CompositeMap* map) { mCompositeMap = map; }
CompositeMap* getCompositeMap() { return mCompositeMap; } CompositeMap* getCompositeMap() const { return mCompositeMap; }
void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; } void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; }
private: private:

View file

@ -17,6 +17,7 @@ Lua API reference
openmw_core openmw_core
openmw_types openmw_types
openmw_async openmw_async
openmw_vfs
openmw_world openmw_world
openmw_self openmw_self
openmw_nearby openmw_nearby

View file

@ -0,0 +1,7 @@
Package openmw.vfs
==================
.. include:: version.rst
.. raw:: html
:file: generated_html/openmw_vfs.html

View file

@ -6,7 +6,7 @@ Overview of Lua scripting
Language and sandboxing Language and sandboxing
======================= =======================
OpenMW supports scripts written in Lua 5.1 with some extensions (see below) from Lua 5.2. OpenMW supports scripts written in Lua 5.1 with some extensions (see below) from Lua 5.2 and Lua 5.3.
There are no plans to switch to any newer version of the language, because newer versions are not supported by LuaJIT. There are no plans to switch to any newer version of the language, because newer versions are not supported by LuaJIT.
.. note:: .. note::
@ -40,6 +40,10 @@ Supported Lua 5.2 features:
- ``__pairs`` and ``__ipairs`` metamethods; - ``__pairs`` and ``__ipairs`` metamethods;
- Function ``table.unpack`` (alias to Lua 5.1 ``unpack``). - Function ``table.unpack`` (alias to Lua 5.1 ``unpack``).
Supported Lua 5.3 features:
- All functions in the `UTF-8 Library <https://www.lua.org/manual/5.3/manual.html#6.5>`__
Loading libraries with ``require('library_name')`` is allowed, but limited. It works this way: Loading libraries with ``require('library_name')`` is allowed, but limited. It works this way:
1. If `library_name` is one of the standard libraries, then return the library. 1. If `library_name` is one of the standard libraries, then return the library.

View file

@ -15,6 +15,8 @@
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ +------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers and callbacks. | |:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers and callbacks. |
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ +------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.vfs <Package openmw.vfs>` | everywhere | | Read-only access to data directories via VFS. |
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. | |:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+ +------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. | |:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |

View file

@ -22,6 +22,7 @@ local env = {
storage = require('openmw.storage'), storage = require('openmw.storage'),
core = require('openmw.core'), core = require('openmw.core'),
types = require('openmw.types'), types = require('openmw.types'),
vfs = require('openmw.vfs'),
async = require('openmw.async'), async = require('openmw.async'),
world = require('openmw.world'), world = require('openmw.world'),
aux_util = require('openmw_aux.util'), aux_util = require('openmw_aux.util'),

View file

@ -24,6 +24,7 @@ local env = {
storage = require('openmw.storage'), storage = require('openmw.storage'),
core = require('openmw.core'), core = require('openmw.core'),
types = require('openmw.types'), types = require('openmw.types'),
vfs = require('openmw.vfs'),
async = require('openmw.async'), async = require('openmw.async'),
nearby = require('openmw.nearby'), nearby = require('openmw.nearby'),
self = require('openmw.self'), self = require('openmw.self'),

View file

@ -71,6 +71,8 @@ local env = {
storage = require('openmw.storage'), storage = require('openmw.storage'),
core = require('openmw.core'), core = require('openmw.core'),
types = require('openmw.types'), types = require('openmw.types'),
vfs = require('openmw.vfs'),
ambient = require('openmw.ambient'),
async = require('openmw.async'), async = require('openmw.async'),
nearby = require('openmw.nearby'), nearby = require('openmw.nearby'),
self = require('openmw.self'), self = require('openmw.self'),

View file

@ -12,14 +12,15 @@ set(LUA_API_FILES
openmw/ambient.lua openmw/ambient.lua
openmw/async.lua openmw/async.lua
openmw/core.lua openmw/core.lua
openmw/debug.lua
openmw/nearby.lua openmw/nearby.lua
openmw/postprocessing.lua
openmw/self.lua openmw/self.lua
openmw/types.lua
openmw/ui.lua openmw/ui.lua
openmw/util.lua openmw/util.lua
openmw/vfs.lua
openmw/world.lua openmw/world.lua
openmw/types.lua
openmw/postprocessing.lua
openmw/debug.lua
) )
foreach (f ${LUA_API_FILES}) foreach (f ${LUA_API_FILES})

View file

@ -0,0 +1,159 @@
---
-- `openmw.vfs` provides read-only access to data directories via VFS.
-- Interface is very similar to "io" library.
-- @module vfs
-- @usage local vfs = require('openmw.vfs')
---
-- @type FileHandle
-- @field #string fileName VFS path to related file
---
-- Close a file handle
-- @function [parent=#FileHandle] close
-- @param self
-- @return #boolean true if a call succeeds without errors.
-- @return #nil, #string nil plus the error message in case of any error.
---
-- Get an iterator function to fetch the next line from given file.
-- Throws an exception if file is closed.
--
-- Hint: since garbage collection works once per frame,
-- you will get the whole file in RAM if you read it in one frame.
-- So if you need to read a really large file, it is better to split reading
-- between different frames (e.g. by keeping a current position in file
-- and using a "seek" to read from saved position).
-- @function [parent=#FileHandle] lines
-- @param self
-- @return #function Iterator function to get next line
-- @usage f = vfs.open("Test\\test.txt");
-- for line in f:lines() do
-- print(line);
-- end
---
-- Set new position in file.
-- Throws an exception if file is closed or seek base is incorrect.
-- @function [parent=#FileHandle] seek
-- @param self
-- @param #string whence Seek base (optional, "cur" by default). Can be:
--
-- * "set" - seek from beginning of file;
-- * "cur" - seek from current position;
-- * "end" - seek from end of file (offset needs to be <= 0);
-- @param #number offset Offset from given base (optional, 0 by default)
-- @return #number new position in file if a call succeeds without errors.
-- @return #nil, #string nil plus the error message in case of any error.
-- @usage -- set pointer to beginning of file
-- f = vfs.open("Test\\test.txt");
-- f:seek("set");
-- @usage -- print current position in file
-- f = vfs.open("Test\\test.txt");
-- print(f:seek());
-- @usage -- print file size
-- f = vfs.open("Test\\test.txt");
-- print(f:seek("end"));
---
-- Read data from file to strings.
-- Throws an exception if file is closed, if there is too many arguments or if an invalid format encountered.
--
-- Hint: since garbage collection works once per frame,
-- you will get the whole file in RAM if you read it in one frame.
-- So if you need to read a really large file, it is better to split reading
-- between different frames (e.g. by keeping a current position in file
-- and using a "seek" to read from saved position).
-- @function [parent=#FileHandle] read
-- @param self
-- @param ... Read formats (up to 20 arguments, default value is one "*l"). Can be:
--
-- * "\*a" (or "*all") - reads the whole file, starting at the current position as #string. On end of file, it returns the empty string.
-- * "\*l" (or "*line") - reads the next line (skipping the end of line), returning nil on end of file (nil and error message if error occured);
-- * "\*n" (or "*number") - read a floating point value as #number (nil and error message if error occured);
-- * number - reads a #string with up to this number of characters, returning nil on end of file (nil and error message if error occured). If number is 0 and end of file is not reached, it reads nothing and returns an empty string;
-- @return #string One #string for every format if a call succeeds without errors. One #string for every successfully handled format, nil for first failed format.
-- @usage -- read three numbers from file
-- f = vfs.open("Test\\test.txt");
-- local n1, n2, n3 = f:read("*number", "*number", "*number");
-- @usage -- read 10 bytes from file
-- f = vfs.open("Test\\test.txt");
-- local n4 = f:read(10);
-- @usage -- read until end of file
-- f = vfs.open("Test\\test.txt");
-- local n5 = f:read("*all");
-- @usage -- read a line from file
-- f = vfs.open("Test\\test.txt");
-- local n6 = f:read();
-- @usage -- try to read three numbers from file with "1" content
-- f = vfs.open("one.txt");
-- print(f:read("*number", "*number", "*number"));
-- -- prints(1, nil)
---
-- Check if file exists in VFS
-- @function [parent=#vfs] fileExists
-- @param #string fileName Path to file in VFS
-- @return #boolean (true - exists, false - does not exist)
-- @usage local exists = vfs.fileExists("Test\\test.txt");
---
-- Open a file
-- @function [parent=#vfs] open
-- @param #string fileName Path to file in VFS
-- @return #FileHandle Opened file handle if a call succeeds without errors.
-- @return #nil, #string nil plus the error message in case of any error.
-- @usage f, msg = vfs.open("Test\\test.txt");
-- -- print file name or error message
-- if (f == nil)
-- print(msg);
-- else
-- print(f.fileName);
-- end
---
-- Get an iterator function to fetch the next line from file with given path.
-- Throws an exception if file is closed or file with given path does not exist.
-- Closes file automatically when it fails to read any more bytes.
--
-- Hint: since garbage collection works once per frame,
-- you will get the whole file in RAM if you read it in one frame.
-- So if you need to read a really large file, it is better to split reading
-- between different frames (e.g. by keeping a current position in file
-- and using a "seek" to read from saved position).
-- @function [parent=#vfs] lines
-- @param #string fileName Path to file in VFS
-- @return #function Iterator function to get next line
-- @usage for line in vfs.lines("Test\\test.txt") do
-- print(line);
-- end
---
-- Get iterator function to fetch file names with given path prefix from VFS
-- @function [parent=#vfs] pathsWithPrefix
-- @param #string path Path prefix
-- @return #function Function to get next file name
-- @usage -- get all files with given prefix from VFS index
-- for fileName in vfs.pathsWithPrefix("Music\\Explore") do
-- print(fileName);
-- end
-- @usage -- get some first files
-- local getNextFile = vfs.pathsWithPrefix("Music\\Explore");
-- local firstFile = getNextFile();
-- local secondFile = getNextFile();
---
-- Detect a file handle type
-- @function [parent=#vfs] type
-- @param #any handle Object to check
-- @return #string File handle type. Can be:
--
-- * "file" - an argument is a valid opened @{openmw.vfs#FileHandle};
-- * "closed file" - an argument is a valid closed @{openmw.vfs#FileHandle};
-- * nil - an argument is not a @{openmw.vfs#FileHandle};
-- @usage f = vfs.open("Test\\test.txt");
-- print(vfs.type(f));
return nil

77
files/lua_api/utf8.doclua Normal file
View file

@ -0,0 +1,77 @@
-------------------------------------------------------------------------------
-- UTF-8 Support.
-- This library provides basic support for UTF-8 encoding.
-- It provides all its functions inside the table utf8.
-- This library does not provide any support for Unicode other than the handling of the encoding.
-- Any operation that needs the meaning of a character, such as character classification, is outside its scope.
--
-- Unless stated otherwise, all functions that expect a byte position as a parameter assume that
-- the given position is either the start of a byte sequence or one plus the length of the subject string.
-- As in the string library, negative indices count from the end of the string.
-- @module utf8
-------------------------------------------------------------------------------
-- Receives zero or more integers, converts each one to its
-- corresponding UTF-8 byte sequence, and returns a string with the concatenation
-- of all these sequences.
-- @function [parent=#utf8] char
-- @param ... zero or more integers.
-- @return #string
-------------------------------------------------------------------------------
-- The pattern which matches exactly one UTF-8 byte sequence, assuming that
-- the subject is a valid UTF-8 string.
-- @function [parent=#utf8] charpattern
-- @return #string
-------------------------------------------------------------------------------
-- Returns values so that the construction
--
-- for p, c in utf8.codes(s) do body end
--
-- will iterate over all characters in string s, with p being the position (in bytes)
-- and c the code point of each character.
-- It raises an error if it meets any invalid byte sequence.
-- @function [parent=#utf8] codes
-- @param #string s string to handle.
-------------------------------------------------------------------------------
-- Returns the codepoints (as integers) from all characters in s that start
-- between byte position i and j (both included). The default for i is 1 and for j is i.
-- It raises an error if it meets any invalid byte sequence.
-- @function [parent=#utf8] codepoint
-- @param #string s string to handle
-- @param #number i the initial position (default value is 1)
-- @param #number j the final position (default value is i)
-- @return #number the codepoints of each character in s
-------------------------------------------------------------------------------
-- Returns the number of UTF-8 characters in string s that start
-- between positions i and j (both inclusive).
-- The default for i is 1 and for j is -1.
-- If it finds any invalid byte sequence,
-- returns a false value plus the position of the first invalid byte.
-- @function [parent=#utf8] len
-- @param #string s string to handle
-- @param #number i the initial position (default value is 1)
-- @param #number j the final position (default value is -1)
-- @return #number the number of utf8 characters in s
-------------------------------------------------------------------------------
-- Returns the position (in bytes) where the encoding of the n-th character of s
-- (counting from position i) starts. A negative n gets characters before position i.
-- The default for i is 1 when n is non-negative and #s + 1 otherwise,
-- so that utf8.offset(s, -n) gets the offset of the n-th character from the end of the string.
-- If the specified character is neither in the subject nor right after its end, the function returns nil.
--
-- As a special case, when n is 0 the function returns the
-- start of the encoding of the character that contains the i-th byte of s.
--
-- This function assumes that s is a valid UTF-8 string.
-- @function [parent=#utf8] offset
-- @param #string s string to handle
-- @param #number n the n-th character
-- @param #number i the initial position (default value is 1 if n is is non-negative and #s + 1 otherwise)
-- @return #number
return nil