mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-06 02:19:43 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_record_services
This commit is contained in:
commit
f287b2f436
59 changed files with 1835 additions and 932 deletions
|
@ -70,6 +70,7 @@
|
|||
Bug #7472: Crash when enchanting last projectiles
|
||||
Bug #7505: Distant terrain does not support sample size greater than cell size
|
||||
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 #5492: Let rain and snow collide with statics
|
||||
Feature #6149: Dehardcode Lua API_REVISION
|
||||
|
@ -77,6 +78,7 @@
|
|||
Feature #6491: Add support for Qt6
|
||||
Feature #6556: Lua API for sounds
|
||||
Feature #6726: Lua API for creating new objects
|
||||
Feature #6864: Lua file access API
|
||||
Feature #6922: Improve launcher appearance
|
||||
Feature #6933: Support high-resolution cursor textures
|
||||
Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData
|
||||
|
|
|
@ -71,7 +71,7 @@ message(STATUS "Configuring OpenMW...")
|
|||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 45)
|
||||
set(OPENMW_LUA_API_REVISION 46)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
set(OPENMW_VERSION_TAGHASH "")
|
||||
|
|
|
@ -61,7 +61,7 @@ add_openmw_dir (mwscript
|
|||
add_openmw_dir (mwlua
|
||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||
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
|
||||
worker magicbindings
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <MyGUI_TextIterator.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
@ -93,8 +94,10 @@ namespace
|
|||
int level = creatureStats.getLevel();
|
||||
for (const ESM::Attribute& attribute : attributes)
|
||||
{
|
||||
const ESM::Race::MaleFemale& value
|
||||
= race->mData.mAttributeValues[static_cast<size_t>(ESM::Attribute::refIdToIndex(attribute.mId))];
|
||||
auto index = 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "soundbindings.hpp"
|
||||
#include "types/types.hpp"
|
||||
#include "uibindings.hpp"
|
||||
#include "vfsbindings.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
@ -347,6 +348,7 @@ namespace MWLua
|
|||
{ "openmw.core", initCorePackage(context) },
|
||||
{ "openmw.types", initTypesPackage(context) },
|
||||
{ "openmw.util", LuaUtil::initUtilPackage(lua) },
|
||||
{ "openmw.vfs", initVFSPackage(context) },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
346
apps/openmw/mwlua/vfsbindings.cpp
Normal file
346
apps/openmw/mwlua/vfsbindings.cpp
Normal 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);
|
||||
}
|
||||
}
|
13
apps/openmw/mwlua/vfsbindings.hpp
Normal file
13
apps/openmw/mwlua/vfsbindings.hpp
Normal 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
|
|
@ -48,9 +48,9 @@ namespace MWMechanics
|
|||
std::string EffectKey::toString() const
|
||||
{
|
||||
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(
|
||||
*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)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "mechanicsmanagerimp.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <osg/Stats>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
@ -150,9 +152,10 @@ namespace MWMechanics
|
|||
|
||||
for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>())
|
||||
{
|
||||
const ESM::Race::MaleFemale& value
|
||||
= race->mData.mAttributeValues[static_cast<size_t>(ESM::Attribute::refIdToIndex(attribute.mId))];
|
||||
auto index = 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -488,7 +488,12 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
|
|||
|
||||
state.mSkillIncrease.fill(0);
|
||||
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)
|
||||
state.mSpecIncreases[i] = mSpecIncreases[i];
|
||||
|
|
|
@ -556,7 +556,7 @@ namespace MWWorld
|
|||
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)
|
||||
, mReaders(readers)
|
||||
, mCellVariant(std::move(cell))
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
/// @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;
|
||||
|
||||
|
|
|
@ -159,9 +159,16 @@ MWWorld::ContainerStore::ContainerStore()
|
|||
|
||||
MWWorld::ContainerStore::~ContainerStore()
|
||||
{
|
||||
MWWorld::WorldModel* worldModel = MWBase::Environment::get().getWorldModel();
|
||||
for (MWWorld::ContainerStoreIterator iter(begin()); iter != end(); ++iter)
|
||||
worldModel->deregisterPtr(*iter);
|
||||
try
|
||||
{
|
||||
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
|
||||
|
|
|
@ -834,9 +834,6 @@ namespace MWWorld
|
|||
mPreloader = std::make_unique<CellPreloader>(rendering.getResourceSystem(), physics->getShapeManager(),
|
||||
rendering.getTerrain(), rendering.getLandManager());
|
||||
mPreloader->setWorkQueue(mRendering.getWorkQueue());
|
||||
|
||||
rendering.getResourceSystem()->setExpiryDelay(Settings::cells().mCacheExpiryDelay);
|
||||
|
||||
mPreloader->setExpiryDelay(Settings::cells().mPreloadCellExpiryDelay);
|
||||
mPreloader->setMinCacheSize(Settings::cells().mPreloadCellCacheMin);
|
||||
mPreloader->setMaxCacheSize(Settings::cells().mPreloadCellCacheMax);
|
||||
|
|
|
@ -1017,11 +1017,12 @@ namespace MWWorld
|
|||
void Store<ESM::GameSetting>::setUp()
|
||||
{
|
||||
auto addSetting = [&](const std::string& key, ESM::Variant value) {
|
||||
auto id = ESM::RefId::stringRefId(key);
|
||||
ESM::GameSetting setting;
|
||||
setting.blank();
|
||||
setting.mId = ESM::RefId::stringRefId(key);
|
||||
setting.mId = id;
|
||||
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)
|
||||
mShared.push_back(&iter->second);
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Nif::Testing
|
|||
|
||||
inline void init(Extra& value)
|
||||
{
|
||||
value.next = ExtraPtr(nullptr);
|
||||
value.mNext = ExtraPtr(nullptr);
|
||||
}
|
||||
|
||||
inline void init(Named& value)
|
||||
|
|
|
@ -996,7 +996,7 @@ namespace
|
|||
TEST_F(TestBulletNifLoader,
|
||||
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;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1024,8 +1024,8 @@ namespace
|
|||
TEST_F(TestBulletNifLoader,
|
||||
for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
||||
{
|
||||
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.string = "NCC__";
|
||||
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.mData = "NCC__";
|
||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1052,7 +1052,7 @@ namespace
|
|||
TEST_F(TestBulletNifLoader,
|
||||
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;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1079,8 +1079,8 @@ namespace
|
|||
TEST_F(TestBulletNifLoader,
|
||||
for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
||||
{
|
||||
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.string = "NC___";
|
||||
mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.mData = "NC___";
|
||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1141,7 +1141,7 @@ namespace
|
|||
TEST_F(TestBulletNifLoader,
|
||||
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;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1160,7 +1160,7 @@ namespace
|
|||
|
||||
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;
|
||||
mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1181,7 +1181,7 @@ namespace
|
|||
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)
|
||||
{
|
||||
mNiStringExtraData.string = "MRK";
|
||||
mNiStringExtraData.mData = "MRK";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode2);
|
||||
|
|
|
@ -42,7 +42,7 @@ list (APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${VERSION_CPP_FILE}")
|
|||
# source files
|
||||
|
||||
add_component_dir (lua
|
||||
luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage
|
||||
luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8
|
||||
shapes/box
|
||||
)
|
||||
|
||||
|
@ -111,7 +111,7 @@ add_component_dir (sceneutil
|
|||
)
|
||||
|
||||
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
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include "scriptscontainer.hpp"
|
||||
#include "utf8.hpp"
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
|
@ -51,7 +52,7 @@ namespace LuaUtil
|
|||
|
||||
static const std::string safeFunctions[] = { "assert", "error", "ipairs", "next", "pairs", "pcall", "select",
|
||||
"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;
|
||||
|
||||
|
@ -181,6 +182,8 @@ namespace LuaUtil
|
|||
mSol["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
|
||||
mSol["math"]["randomseed"] = [] {};
|
||||
|
||||
mSol["utf8"] = LuaUtf8::initUtf8Package(mSol);
|
||||
|
||||
mSol["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
|
||||
|
||||
mSol["setEnvironment"]
|
||||
|
|
233
components/lua/utf8.cpp
Normal file
233
components/lua/utf8.cpp
Normal 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
11
components/lua/utf8.hpp
Normal 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
|
|
@ -5,11 +5,11 @@ namespace Nif
|
|||
void Extra::read(NIFStream* nif)
|
||||
{
|
||||
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))
|
||||
{
|
||||
next.read(nif);
|
||||
recordSize = nif->getUInt();
|
||||
mNext.read(nif);
|
||||
nif->read(mRecordSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace Nif
|
|||
// An extra data record. All the extra data connected to an object form a linked list.
|
||||
struct Extra : public Record
|
||||
{
|
||||
std::string name;
|
||||
ExtraPtr next; // Next extra data record in the list
|
||||
unsigned int recordSize{ 0u };
|
||||
std::string mName;
|
||||
ExtraPtr mNext; // Next extra data record in the list
|
||||
uint32_t mRecordSize{ 0u };
|
||||
|
||||
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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -1,9 +1,9 @@
|
|||
#include "controller.hpp"
|
||||
|
||||
#include "controlled.hpp"
|
||||
#include "data.hpp"
|
||||
#include "node.hpp"
|
||||
#include "recordptr.hpp"
|
||||
#include "particle.hpp"
|
||||
#include "texture.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "effect.hpp"
|
||||
|
||||
#include "controlled.hpp"
|
||||
#include "node.hpp"
|
||||
#include "texture.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
@ -9,55 +9,61 @@ namespace Nif
|
|||
void NiDynamicEffect::read(NIFStream* nif)
|
||||
{
|
||||
Node::read(nif);
|
||||
if (nif->getVersion() >= nif->generateVersion(10, 1, 0, 106)
|
||||
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
||||
nif->getBoolean(); // Switch state
|
||||
if (nif->getVersion() <= NIFFile::VER_MW
|
||||
|| (nif->getVersion() >= nif->generateVersion(10, 1, 0, 0)
|
||||
&& nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4))
|
||||
{
|
||||
size_t numAffectedNodes = nif->get<uint32_t>();
|
||||
nif->skip(numAffectedNodes * 4);
|
||||
}
|
||||
|
||||
if (nif->getVersion() > NIFFile::VER_MW && nif->getVersion() < nif->generateVersion(10, 1, 0, 0))
|
||||
return;
|
||||
|
||||
if (nif->getBethVersion() >= NIFFile::BethVersion::BETHVER_FO4)
|
||||
return;
|
||||
|
||||
if (nif->getVersion() >= nif->generateVersion(10, 1, 0, 106))
|
||||
nif->read(mSwitchState);
|
||||
size_t numAffectedNodes = nif->get<uint32_t>();
|
||||
nif->skip(numAffectedNodes * 4);
|
||||
}
|
||||
|
||||
void NiLight::read(NIFStream* nif)
|
||||
{
|
||||
NiDynamicEffect::read(nif);
|
||||
|
||||
dimmer = nif->getFloat();
|
||||
ambient = nif->getVector3();
|
||||
diffuse = nif->getVector3();
|
||||
specular = nif->getVector3();
|
||||
mDimmer = nif->getFloat();
|
||||
mAmbient = nif->getVector3();
|
||||
mDiffuse = 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)
|
||||
{
|
||||
NiDynamicEffect::read(nif);
|
||||
|
||||
// Model Projection Matrix
|
||||
nif->skip(3 * 3 * sizeof(float));
|
||||
|
||||
// Model Projection Transform
|
||||
nif->skip(3 * sizeof(float));
|
||||
|
||||
// Texture Filtering
|
||||
nif->skip(4);
|
||||
|
||||
// Max anisotropy samples
|
||||
nif->read(mProjectionRotation);
|
||||
nif->read(mProjectionPosition);
|
||||
nif->read(mFilterMode);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20, 5, 0, 4))
|
||||
nif->skip(2);
|
||||
|
||||
clamp = nif->getUInt();
|
||||
|
||||
textureType = (TextureType)nif->getUInt();
|
||||
|
||||
coordGenType = (CoordGenType)nif->getUInt();
|
||||
|
||||
texture.read(nif);
|
||||
|
||||
nif->skip(1); // Use clipping plane
|
||||
nif->skip(16); // Clipping plane dimensions vector
|
||||
nif->read(mMaxAnisotropy);
|
||||
nif->read(mClampMode);
|
||||
mTextureType = static_cast<TextureType>(nif->get<uint32_t>());
|
||||
mCoordGenType = static_cast<CoordGenType>(nif->get<uint32_t>());
|
||||
mTexture.read(nif);
|
||||
nif->read(mEnableClipPlane);
|
||||
mClipPlane = osg::Plane(nif->get<osg::Vec4f>());
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(10, 2, 0, 0))
|
||||
nif->skip(4); // PS2-specific shorts
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4, 1, 0, 12))
|
||||
|
@ -67,24 +73,8 @@ namespace Nif
|
|||
void NiTextureEffect::post(Reader& nif)
|
||||
{
|
||||
NiDynamicEffect::post(nif);
|
||||
texture.post(nif);
|
||||
}
|
||||
|
||||
void NiPointLight::read(NIFStream* 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();
|
||||
mTexture.post(nif);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,67 +29,75 @@
|
|||
namespace Nif
|
||||
{
|
||||
|
||||
// Abstract
|
||||
struct NiDynamicEffect : public Node
|
||||
{
|
||||
bool mSwitchState{ true };
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
// Used as base for NiAmbientLight, NiDirectionalLight, NiPointLight and NiSpotLight.
|
||||
// Abstract light source
|
||||
struct NiLight : NiDynamicEffect
|
||||
{
|
||||
float dimmer;
|
||||
osg::Vec3f ambient;
|
||||
osg::Vec3f diffuse;
|
||||
osg::Vec3f specular;
|
||||
float mDimmer;
|
||||
osg::Vec3f mAmbient;
|
||||
osg::Vec3f mDiffuse;
|
||||
osg::Vec3f mSpecular;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiPointLight : public NiLight
|
||||
{
|
||||
float constantAttenuation;
|
||||
float linearAttenuation;
|
||||
float quadraticAttenuation;
|
||||
float mConstantAttenuation;
|
||||
float mLinearAttenuation;
|
||||
float mQuadraticAttenuation;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiSpotLight : public NiPointLight
|
||||
{
|
||||
float cutoff;
|
||||
float exponent;
|
||||
float mCutoff;
|
||||
float mExponent;
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct NiTextureEffect : NiDynamicEffect
|
||||
{
|
||||
NiSourceTexturePtr texture;
|
||||
unsigned int clamp;
|
||||
|
||||
enum TextureType
|
||||
enum class TextureType : uint32_t
|
||||
{
|
||||
Projected_Light = 0,
|
||||
Projected_Shadow = 1,
|
||||
Environment_Map = 2,
|
||||
Fog_Map = 3
|
||||
ProjectedLight = 0,
|
||||
ProjectedShadow = 1,
|
||||
EnvironmentMap = 2,
|
||||
FogMap = 3,
|
||||
};
|
||||
TextureType textureType;
|
||||
|
||||
enum CoordGenType
|
||||
enum class CoordGenType : uint32_t
|
||||
{
|
||||
World_Parallel = 0,
|
||||
World_Perspective,
|
||||
Sphere_Map,
|
||||
Specular_Cube_Map,
|
||||
Diffuse_Cube_Map
|
||||
WorldParallel = 0,
|
||||
WorldPerspective = 1,
|
||||
SphereMap = 2,
|
||||
SpecularCubeMap = 3,
|
||||
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 post(Reader& nif) override;
|
||||
|
||||
bool wrapT() const { return clamp & 1; }
|
||||
bool wrapS() const { return (clamp >> 1) & 1; }
|
||||
bool wrapT() const { return mClampMode & 1; }
|
||||
bool wrapS() const { return mClampMode & 2; }
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
|
|
|
@ -6,25 +6,21 @@ namespace Nif
|
|||
void NiExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
nif->readVector(data, recordSize);
|
||||
}
|
||||
|
||||
void NiStringExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
string = nif->getString();
|
||||
nif->readVector(mData, mRecordSize);
|
||||
}
|
||||
|
||||
void NiTextKeyExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
|
||||
int keynum = nif->getInt();
|
||||
list.resize(keynum);
|
||||
for (int i = 0; i < keynum; i++)
|
||||
uint32_t numKeys;
|
||||
nif->read(numKeys);
|
||||
mList.resize(numKeys);
|
||||
for (TextKey& key : mList)
|
||||
{
|
||||
list[i].time = nif->getFloat();
|
||||
list[i].text = nif->getString();
|
||||
nif->read(key.mTime);
|
||||
nif->read(key.mText);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,81 +28,39 @@ namespace Nif
|
|||
{
|
||||
Extra::read(nif);
|
||||
|
||||
nif->skip(nif->getUShort() * 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());
|
||||
nif->skip(nif->get<uint16_t>() * sizeof(float)); // vertex weights I guess
|
||||
}
|
||||
|
||||
void BSBound::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
center = nif->getVector3();
|
||||
halfExtents = nif->getVector3();
|
||||
|
||||
nif->read(mCenter);
|
||||
nif->read(mExtents);
|
||||
}
|
||||
|
||||
void BSFurnitureMarker::LegacyFurniturePosition::read(NIFStream* nif)
|
||||
{
|
||||
mOffset = nif->getVector3();
|
||||
mOrientation = nif->getUShort();
|
||||
mPositionRef = nif->getChar();
|
||||
nif->read(mOffset);
|
||||
nif->read(mOrientation);
|
||||
nif->read(mPositionRef);
|
||||
nif->skip(1); // Position ref 2
|
||||
}
|
||||
|
||||
void BSFurnitureMarker::FurniturePosition::read(NIFStream* nif)
|
||||
{
|
||||
mOffset = nif->getVector3();
|
||||
mHeading = nif->getFloat();
|
||||
mType = nif->getUShort();
|
||||
mEntryPoint = nif->getUShort();
|
||||
nif->read(mOffset);
|
||||
nif->read(mHeading);
|
||||
nif->read(mType);
|
||||
nif->read(mEntryPoint);
|
||||
}
|
||||
|
||||
void BSFurnitureMarker::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
unsigned int num = nif->getUInt();
|
||||
|
||||
uint32_t num;
|
||||
nif->read(num);
|
||||
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
||||
{
|
||||
mLegacyMarkers.resize(num);
|
||||
|
@ -124,19 +78,20 @@ namespace Nif
|
|||
void BSInvMarker::read(NIFStream* 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);
|
||||
nif->read(mScale);
|
||||
}
|
||||
|
||||
void BSBehaviorGraphExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
mFile = nif->getString();
|
||||
mControlsBaseSkeleton = nif->getBoolean();
|
||||
|
||||
nif->read(mFile);
|
||||
nif->read(mControlsBaseSkeleton);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
#define OPENMW_COMPONENTS_NIF_EXTRA_HPP
|
||||
|
||||
|
@ -29,9 +6,46 @@
|
|||
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
|
||||
{
|
||||
std::vector<char> data;
|
||||
std::vector<uint8_t> mData;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
@ -45,78 +59,17 @@ namespace Nif
|
|||
{
|
||||
struct TextKey
|
||||
{
|
||||
float time;
|
||||
std::string text;
|
||||
float mTime;
|
||||
std::string mText;
|
||||
};
|
||||
std::vector<TextKey> list;
|
||||
|
||||
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;
|
||||
std::vector<TextKey> mList;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct BSBound : public Extra
|
||||
{
|
||||
osg::Vec3f center, halfExtents;
|
||||
osg::Vec3f mCenter, mExtents;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
@ -149,7 +102,7 @@ namespace Nif
|
|||
struct BSInvMarker : public Extra
|
||||
{
|
||||
osg::Quat mRotation;
|
||||
float mScale = 1.0f;
|
||||
float mScale;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
@ -162,5 +115,5 @@ namespace Nif
|
|||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -10,15 +10,16 @@
|
|||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "controlled.hpp"
|
||||
#include "controller.hpp"
|
||||
#include "data.hpp"
|
||||
#include "effect.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "extra.hpp"
|
||||
#include "node.hpp"
|
||||
#include "particle.hpp"
|
||||
#include "physics.hpp"
|
||||
#include "property.hpp"
|
||||
#include "texture.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
|
95
components/nif/particle.cpp
Normal file
95
components/nif/particle.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
90
components/nif/particle.hpp
Normal file
90
components/nif/particle.hpp
Normal 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
|
|
@ -12,15 +12,15 @@ namespace Nif
|
|||
|
||||
void bhkWorldObjCInfoProperty::read(NIFStream* nif)
|
||||
{
|
||||
mData = nif->getUInt();
|
||||
mSize = nif->getUInt();
|
||||
mCapacityAndFlags = nif->getUInt();
|
||||
nif->read(mData);
|
||||
nif->read(mSize);
|
||||
nif->read(mCapacityAndFlags);
|
||||
}
|
||||
|
||||
void bhkWorldObjectCInfo::read(NIFStream* nif)
|
||||
{
|
||||
nif->skip(4); // Unused
|
||||
mPhaseType = static_cast<BroadPhaseType>(nif->getChar());
|
||||
mPhaseType = static_cast<BroadPhaseType>(nif->get<uint8_t>());
|
||||
nif->skip(3); // Unused
|
||||
mProperty.read(nif);
|
||||
}
|
||||
|
@ -29,47 +29,47 @@ namespace Nif
|
|||
{
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB_OLD)
|
||||
nif->skip(4); // Unknown
|
||||
mMaterial = nif->getUInt();
|
||||
nif->read(mMaterial);
|
||||
}
|
||||
|
||||
void HavokFilter::read(NIFStream* nif)
|
||||
{
|
||||
mLayer = nif->getChar();
|
||||
mFlags = nif->getChar();
|
||||
mGroup = nif->getUShort();
|
||||
nif->read(mLayer);
|
||||
nif->read(mFlags);
|
||||
nif->read(mGroup);
|
||||
}
|
||||
|
||||
void hkSubPartData::read(NIFStream* nif)
|
||||
{
|
||||
mHavokFilter.read(nif);
|
||||
mNumVertices = nif->getUInt();
|
||||
nif->read(mNumVertices);
|
||||
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)
|
||||
{
|
||||
mResponseType = static_cast<hkResponseType>(nif->getChar());
|
||||
mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
mTriangle[i] = nif->getUShort();
|
||||
mWeldingInfo = nif->getUShort();
|
||||
nif->readArray(mTriangle);
|
||||
nif->read(mWeldingInfo);
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
mNormal = nif->getVector3();
|
||||
nif->read(mNormal);
|
||||
}
|
||||
|
||||
void bhkMeshMaterial::read(NIFStream* nif)
|
||||
|
@ -80,28 +80,27 @@ namespace Nif
|
|||
|
||||
void bhkQsTransform::read(NIFStream* nif)
|
||||
{
|
||||
mTranslation = nif->getVector4();
|
||||
mRotation = nif->getQuaternion();
|
||||
nif->read(mTranslation);
|
||||
nif->read(mRotation);
|
||||
}
|
||||
|
||||
void bhkCMSBigTri::read(NIFStream* nif)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
mTriangle[i] = nif->getUShort();
|
||||
mMaterial = nif->getUInt();
|
||||
mWeldingInfo = nif->getUShort();
|
||||
nif->readArray(mTriangle);
|
||||
nif->read(mMaterial);
|
||||
nif->read(mWeldingInfo);
|
||||
}
|
||||
|
||||
void bhkCMSChunk::read(NIFStream* nif)
|
||||
{
|
||||
mTranslation = nif->getVector4();
|
||||
mMaterialIndex = nif->getUInt();
|
||||
mReference = nif->getUShort();
|
||||
mTransformIndex = nif->getUShort();
|
||||
nif->readVector(mVertices, nif->getUInt());
|
||||
nif->readVector(mIndices, nif->getUInt());
|
||||
nif->readVector(mStrips, nif->getUInt());
|
||||
nif->readVector(mWeldingInfos, nif->getUInt());
|
||||
nif->read(mTranslation);
|
||||
nif->read(mMaterialIndex);
|
||||
nif->read(mReference);
|
||||
nif->read(mTransformIndex);
|
||||
nif->readVector(mVertices, nif->get<uint32_t>());
|
||||
nif->readVector(mIndices, nif->get<uint32_t>());
|
||||
nif->readVector(mStrips, nif->get<uint32_t>());
|
||||
nif->readVector(mWeldingInfos, nif->get<uint32_t>());
|
||||
}
|
||||
|
||||
void bhkRigidBodyCInfo::read(NIFStream* nif)
|
||||
|
@ -115,64 +114,67 @@ namespace Nif
|
|||
{
|
||||
if (nif->getBethVersion() >= 83)
|
||||
nif->skip(4); // Unused
|
||||
mResponseType = static_cast<hkResponseType>(nif->getChar());
|
||||
mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
|
||||
nif->skip(1); // Unused
|
||||
mProcessContactDelay = nif->getUShort();
|
||||
nif->read(mProcessContactDelay);
|
||||
}
|
||||
}
|
||||
if (nif->getBethVersion() < 83)
|
||||
nif->skip(4); // Unused
|
||||
mTranslation = nif->getVector4();
|
||||
mRotation = nif->getQuaternion();
|
||||
mLinearVelocity = nif->getVector4();
|
||||
mAngularVelocity = nif->getVector4();
|
||||
nif->read(mTranslation);
|
||||
nif->read(mRotation);
|
||||
nif->read(mLinearVelocity);
|
||||
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 j = 0; j < 4; j++)
|
||||
mInertiaTensor[i][j] = nif->getFloat();
|
||||
mCenter = nif->getVector4();
|
||||
mMass = nif->getFloat();
|
||||
mLinearDamping = nif->getFloat();
|
||||
mAngularDamping = nif->getFloat();
|
||||
{
|
||||
nif->read(mInertiaTensor.mValues[i], 3);
|
||||
nif->skip(4); // Padding
|
||||
}
|
||||
nif->read(mCenter);
|
||||
nif->read(mMass);
|
||||
nif->read(mLinearDamping);
|
||||
nif->read(mAngularDamping);
|
||||
if (nif->getBethVersion() >= 83)
|
||||
{
|
||||
if (nif->getBethVersion() != NIFFile::BethVersion::BETHVER_FO4)
|
||||
mTimeFactor = nif->getFloat();
|
||||
mGravityFactor = nif->getFloat();
|
||||
nif->read(mTimeFactor);
|
||||
nif->read(mGravityFactor);
|
||||
}
|
||||
mFriction = nif->getFloat();
|
||||
nif->read(mFriction);
|
||||
if (nif->getBethVersion() >= 83)
|
||||
mRollingFrictionMult = nif->getFloat();
|
||||
mRestitution = nif->getFloat();
|
||||
nif->read(mRollingFrictionMult);
|
||||
nif->read(mRestitution);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
{
|
||||
mMaxLinearVelocity = nif->getFloat();
|
||||
mMaxAngularVelocity = nif->getFloat();
|
||||
nif->read(mMaxLinearVelocity);
|
||||
nif->read(mMaxAngularVelocity);
|
||||
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)
|
||||
mDeactivatorType = static_cast<hkDeactivatorType>(nif->getChar());
|
||||
mDeactivatorType = static_cast<hkDeactivatorType>(nif->get<uint8_t>());
|
||||
else
|
||||
mEnableDeactivation = nif->getBoolean();
|
||||
mSolverDeactivation = static_cast<hkSolverDeactivation>(nif->getChar());
|
||||
nif->read(mEnableDeactivation);
|
||||
mSolverDeactivation = static_cast<hkSolverDeactivation>(nif->get<uint8_t>());
|
||||
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4)
|
||||
{
|
||||
nif->skip(1);
|
||||
mPenetrationDepth = nif->getFloat();
|
||||
mTimeFactor = nif->getFloat();
|
||||
nif->read(mPenetrationDepth);
|
||||
nif->read(mTimeFactor);
|
||||
nif->skip(4);
|
||||
mResponseType = static_cast<hkResponseType>(nif->getChar());
|
||||
mResponseType = static_cast<hkResponseType>(nif->get<uint8_t>());
|
||||
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)
|
||||
{
|
||||
mAutoRemoveLevel = nif->getChar();
|
||||
mResponseModifierFlags = nif->getChar();
|
||||
mNumContactPointShapeKeys = nif->getChar();
|
||||
mForceCollidedOntoPPU = nif->getBoolean();
|
||||
nif->read(mAutoRemoveLevel);
|
||||
nif->read(mResponseModifierFlags);
|
||||
nif->read(mNumContactPointShapeKeys);
|
||||
nif->read(mForceCollidedOntoPPU);
|
||||
}
|
||||
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_FO4)
|
||||
nif->skip(3); // Unused
|
||||
|
@ -182,7 +184,7 @@ namespace 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);
|
||||
mEntityB.read(nif);
|
||||
|
||||
|
@ -203,7 +205,7 @@ namespace Nif
|
|||
nif->read(mDamping);
|
||||
nif->read(mProportionalRecoveryVelocity);
|
||||
nif->read(mConstantRecoveryVelocity);
|
||||
mEnabled = nif->getBoolean();
|
||||
nif->read(mEnabled);
|
||||
}
|
||||
|
||||
void bhkVelocityConstraintMotor::read(NIFStream* nif)
|
||||
|
@ -212,8 +214,8 @@ namespace Nif
|
|||
nif->read(mMaxForce);
|
||||
nif->read(mTau);
|
||||
nif->read(mTargetVelocity);
|
||||
mUseVelocityTarget = nif->getBoolean();
|
||||
mEnabled = nif->getBoolean();
|
||||
nif->read(mUseVelocityTarget);
|
||||
nif->read(mEnabled);
|
||||
}
|
||||
|
||||
void bhkSpringDamperConstraintMotor::read(NIFStream* nif)
|
||||
|
@ -222,7 +224,7 @@ namespace Nif
|
|||
nif->read(mMaxForce);
|
||||
nif->read(mSpringConstant);
|
||||
nif->read(mSpringDamping);
|
||||
mEnabled = nif->getBoolean();
|
||||
nif->read(mEnabled);
|
||||
}
|
||||
|
||||
void bhkConstraintMotorCInfo::read(NIFStream* nif)
|
||||
|
@ -335,7 +337,8 @@ namespace Nif
|
|||
void bhkCollisionObject::read(NIFStream* nif)
|
||||
{
|
||||
NiCollisionObject::read(nif);
|
||||
mFlags = nif->getUShort();
|
||||
|
||||
nif->read(mFlags);
|
||||
mBody.read(nif);
|
||||
}
|
||||
|
||||
|
@ -356,6 +359,7 @@ namespace Nif
|
|||
void bhkEntity::read(NIFStream* nif)
|
||||
{
|
||||
bhkWorldObject::read(nif);
|
||||
|
||||
mInfo.read(nif);
|
||||
}
|
||||
|
||||
|
@ -372,21 +376,26 @@ namespace Nif
|
|||
void bhkMoppBvTreeShape::read(NIFStream* nif)
|
||||
{
|
||||
bhkBvTreeShape::read(nif);
|
||||
|
||||
nif->skip(12); // Unused
|
||||
mScale = nif->getFloat();
|
||||
nif->read(mScale);
|
||||
mMopp.read(nif);
|
||||
}
|
||||
|
||||
void bhkNiTriStripsShape::read(NIFStream* nif)
|
||||
{
|
||||
mHavokMaterial.read(nif);
|
||||
mRadius = nif->getFloat();
|
||||
nif->read(mRadius);
|
||||
nif->skip(20); // Unused
|
||||
mGrowBy = nif->getUInt();
|
||||
nif->read(mGrowBy);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
mScale = nif->getVector4();
|
||||
nif->read(mScale);
|
||||
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)
|
||||
|
@ -398,15 +407,17 @@ namespace Nif
|
|||
{
|
||||
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
|
||||
{
|
||||
mSubshapes.resize(nif->getUShort());
|
||||
uint16_t numSubshapes;
|
||||
nif->read(numSubshapes);
|
||||
mSubshapes.resize(numSubshapes);
|
||||
for (hkSubPartData& subshape : mSubshapes)
|
||||
subshape.read(nif);
|
||||
}
|
||||
mUserData = nif->getUInt();
|
||||
nif->read(mUserData);
|
||||
nif->skip(4); // Unused
|
||||
mRadius = nif->getFloat();
|
||||
nif->read(mRadius);
|
||||
nif->skip(4); // Unused
|
||||
mScale = nif->getVector4();
|
||||
nif->read(mScale);
|
||||
nif->skip(20); // Duplicates of the two previous fields
|
||||
mData.read(nif);
|
||||
}
|
||||
|
@ -418,22 +429,26 @@ namespace Nif
|
|||
|
||||
void hkPackedNiTriStripsData::read(NIFStream* nif)
|
||||
{
|
||||
unsigned int numTriangles = nif->getUInt();
|
||||
uint32_t numTriangles;
|
||||
nif->read(numTriangles);
|
||||
mTriangles.resize(numTriangles);
|
||||
for (unsigned int i = 0; i < numTriangles; i++)
|
||||
for (uint32_t i = 0; i < numTriangles; i++)
|
||||
mTriangles[i].read(nif);
|
||||
|
||||
unsigned int numVertices = nif->getUInt();
|
||||
uint32_t numVertices;
|
||||
nif->read(numVertices);
|
||||
bool compressed = false;
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
compressed = nif->getBoolean();
|
||||
nif->read(compressed);
|
||||
if (!compressed)
|
||||
nif->readVector(mVertices, numVertices);
|
||||
else
|
||||
nif->skip(6 * numVertices); // Half-precision vectors are not currently supported
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
{
|
||||
mSubshapes.resize(nif->getUShort());
|
||||
uint16_t numSubshapes;
|
||||
nif->read(numSubshapes);
|
||||
mSubshapes.resize(numSubshapes);
|
||||
for (hkSubPartData& subshape : mSubshapes)
|
||||
subshape.read(nif);
|
||||
}
|
||||
|
@ -447,23 +462,25 @@ namespace Nif
|
|||
void bhkConvexShape::read(NIFStream* nif)
|
||||
{
|
||||
bhkSphereRepShape::read(nif);
|
||||
mRadius = nif->getFloat();
|
||||
|
||||
nif->read(mRadius);
|
||||
}
|
||||
|
||||
void bhkConvexVerticesShape::read(NIFStream* nif)
|
||||
{
|
||||
bhkConvexShape::read(nif);
|
||||
|
||||
mVerticesProperty.read(nif);
|
||||
mNormalsProperty.read(nif);
|
||||
nif->readVector(mVertices, nif->getUInt());
|
||||
nif->readVector(mNormals, nif->getUInt());
|
||||
nif->readVector(mVertices, nif->get<uint32_t>());
|
||||
nif->readVector(mNormals, nif->get<uint32_t>());
|
||||
}
|
||||
|
||||
void bhkConvexTransformShape::read(NIFStream* nif)
|
||||
{
|
||||
mShape.read(nif);
|
||||
mHavokMaterial.read(nif);
|
||||
mRadius = nif->getFloat();
|
||||
nif->read(mRadius);
|
||||
nif->skip(8); // Unused
|
||||
std::array<float, 16> mat;
|
||||
nif->readArray(mat);
|
||||
|
@ -478,19 +495,21 @@ namespace Nif
|
|||
void bhkBoxShape::read(NIFStream* nif)
|
||||
{
|
||||
bhkConvexShape::read(nif);
|
||||
|
||||
nif->skip(8); // Unused
|
||||
mExtents = nif->getVector3();
|
||||
nif->read(mExtents);
|
||||
nif->skip(4); // Unused
|
||||
}
|
||||
|
||||
void bhkCapsuleShape::read(NIFStream* nif)
|
||||
{
|
||||
bhkConvexShape::read(nif);
|
||||
|
||||
nif->skip(8); // Unused
|
||||
mPoint1 = nif->getVector3();
|
||||
mRadius1 = nif->getFloat();
|
||||
mPoint2 = nif->getVector3();
|
||||
mRadius2 = nif->getFloat();
|
||||
nif->read(mPoint1);
|
||||
nif->read(mRadius1);
|
||||
nif->read(mPoint2);
|
||||
nif->read(mRadius2);
|
||||
}
|
||||
|
||||
void bhkListShape::read(NIFStream* nif)
|
||||
|
@ -499,7 +518,8 @@ namespace Nif
|
|||
mHavokMaterial.read(nif);
|
||||
mChildShapeProperty.read(nif);
|
||||
mChildFilterProperty.read(nif);
|
||||
unsigned int numFilters = nif->getUInt();
|
||||
uint32_t numFilters;
|
||||
nif->read(numFilters);
|
||||
mHavokFilters.resize(numFilters);
|
||||
for (HavokFilter& filter : mHavokFilters)
|
||||
filter.read(nif);
|
||||
|
@ -508,12 +528,12 @@ namespace Nif
|
|||
void bhkCompressedMeshShape::read(NIFStream* nif)
|
||||
{
|
||||
mTarget.read(nif);
|
||||
mUserData = nif->getUInt();
|
||||
mRadius = nif->getFloat();
|
||||
nif->getFloat(); // Unknown
|
||||
mScale = nif->getVector4();
|
||||
nif->getFloat(); // Radius
|
||||
nif->getVector4(); // Scale
|
||||
nif->read(mUserData);
|
||||
nif->read(mRadius);
|
||||
nif->skip(4); // Unknown
|
||||
nif->read(mScale);
|
||||
nif->skip(4); // Radius
|
||||
nif->skip(16); // Scale
|
||||
mData.read(nif);
|
||||
}
|
||||
|
||||
|
@ -525,60 +545,66 @@ namespace Nif
|
|||
|
||||
void bhkCompressedMeshShapeData::read(NIFStream* nif)
|
||||
{
|
||||
mBitsPerIndex = nif->getUInt();
|
||||
mBitsPerWIndex = nif->getUInt();
|
||||
mMaskWIndex = nif->getUInt();
|
||||
mMaskIndex = nif->getUInt();
|
||||
mError = nif->getFloat();
|
||||
mAabbMin = nif->getVector4();
|
||||
mAabbMax = nif->getVector4();
|
||||
mWeldingType = nif->getChar();
|
||||
mMaterialType = nif->getChar();
|
||||
nif->skip(nif->getUInt() * 4); // Unused
|
||||
nif->skip(nif->getUInt() * 4); // Unused
|
||||
nif->skip(nif->getUInt() * 4); // Unused
|
||||
nif->read(mBitsPerIndex);
|
||||
nif->read(mBitsPerWIndex);
|
||||
nif->read(mMaskWIndex);
|
||||
nif->read(mMaskIndex);
|
||||
nif->read(mError);
|
||||
nif->read(mAabbMin);
|
||||
nif->read(mAabbMax);
|
||||
nif->read(mWeldingType);
|
||||
nif->read(mMaterialType);
|
||||
nif->skip(nif->get<uint32_t>() * 4); // Unused
|
||||
nif->skip(nif->get<uint32_t>() * 4); // Unused
|
||||
nif->skip(nif->get<uint32_t>() * 4); // Unused
|
||||
|
||||
size_t numMaterials = nif->getUInt();
|
||||
uint32_t numMaterials;
|
||||
nif->read(numMaterials);
|
||||
mMaterials.resize(numMaterials);
|
||||
for (bhkMeshMaterial& material : mMaterials)
|
||||
material.read(nif);
|
||||
|
||||
nif->getUInt(); // Unused
|
||||
size_t numTransforms = nif->getUInt();
|
||||
nif->skip(4); // Unused
|
||||
|
||||
uint32_t numTransforms;
|
||||
nif->read(numTransforms);
|
||||
mChunkTransforms.resize(numTransforms);
|
||||
for (bhkQsTransform& transform : mChunkTransforms)
|
||||
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);
|
||||
for (bhkCMSBigTri& tri : mBigTris)
|
||||
tri.read(nif);
|
||||
|
||||
size_t numChunks = nif->getUInt();
|
||||
uint32_t numChunks;
|
||||
nif->read(numChunks);
|
||||
mChunks.resize(numChunks);
|
||||
for (bhkCMSChunk& chunk : mChunks)
|
||||
chunk.read(nif);
|
||||
|
||||
nif->getUInt(); // Unused
|
||||
nif->skip(4); // Unused
|
||||
}
|
||||
|
||||
void bhkRigidBody::read(NIFStream* nif)
|
||||
{
|
||||
bhkEntity::read(nif);
|
||||
|
||||
mInfo.read(nif);
|
||||
readRecordList(nif, mConstraints);
|
||||
if (nif->getBethVersion() < 76)
|
||||
mBodyFlags = nif->getUInt();
|
||||
nif->read(mBodyFlags);
|
||||
else
|
||||
mBodyFlags = nif->getUShort();
|
||||
mBodyFlags = nif->get<uint16_t>();
|
||||
}
|
||||
|
||||
void bhkSimpleShapePhantom::read(NIFStream* nif)
|
||||
{
|
||||
bhkWorldObject::read(nif);
|
||||
|
||||
nif->skip(8); // Unused
|
||||
std::array<float, 16> mat;
|
||||
nif->readArray(mat);
|
||||
|
@ -598,18 +624,21 @@ namespace Nif
|
|||
void bhkRagdollConstraint::read(NIFStream* nif)
|
||||
{
|
||||
bhkConstraint::read(nif);
|
||||
|
||||
mConstraint.read(nif);
|
||||
}
|
||||
|
||||
void bhkHingeConstraint::read(NIFStream* nif)
|
||||
{
|
||||
bhkConstraint::read(nif);
|
||||
|
||||
mConstraint.read(nif);
|
||||
}
|
||||
|
||||
void bhkLimitedHingeConstraint::read(NIFStream* nif)
|
||||
{
|
||||
bhkConstraint::read(nif);
|
||||
|
||||
mConstraint.read(nif);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef OPENMW_COMPONENTS_NIF_PHYSICS_HPP
|
||||
#define OPENMW_COMPONENTS_NIF_PHYSICS_HPP
|
||||
|
||||
#include "niftypes.hpp"
|
||||
#include "record.hpp"
|
||||
#include "recordptr.hpp"
|
||||
|
||||
|
@ -23,9 +24,10 @@ namespace Nif
|
|||
|
||||
struct bhkWorldObjCInfoProperty
|
||||
{
|
||||
unsigned int mData;
|
||||
unsigned int mSize;
|
||||
unsigned int mCapacityAndFlags;
|
||||
uint32_t mData;
|
||||
uint32_t mSize;
|
||||
uint32_t mCapacityAndFlags;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -41,28 +43,32 @@ namespace Nif
|
|||
{
|
||||
BroadPhaseType mPhaseType;
|
||||
bhkWorldObjCInfoProperty mProperty;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct HavokMaterial
|
||||
{
|
||||
unsigned int mMaterial;
|
||||
uint32_t mMaterial;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct HavokFilter
|
||||
{
|
||||
unsigned char mLayer;
|
||||
unsigned char mFlags;
|
||||
unsigned short mGroup;
|
||||
uint8_t mLayer;
|
||||
uint8_t mFlags;
|
||||
uint16_t mGroup;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct hkSubPartData
|
||||
{
|
||||
HavokMaterial mHavokMaterial;
|
||||
unsigned int mNumVertices;
|
||||
uint32_t mNumVertices;
|
||||
HavokFilter mHavokFilter;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -77,22 +83,26 @@ namespace Nif
|
|||
struct bhkEntityCInfo
|
||||
{
|
||||
hkResponseType mResponseType;
|
||||
unsigned short mProcessContactDelay;
|
||||
uint16_t mProcessContactDelay;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct hkpMoppCode
|
||||
{
|
||||
osg::Vec4f mOffset;
|
||||
std::vector<char> mData;
|
||||
uint8_t mBuildType;
|
||||
std::vector<uint8_t> mData;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct TriangleData
|
||||
{
|
||||
unsigned short mTriangle[3];
|
||||
unsigned short mWeldingInfo;
|
||||
std::array<uint16_t, 3> mTriangle;
|
||||
uint16_t mWeldingInfo;
|
||||
osg::Vec3f mNormal;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -100,6 +110,7 @@ namespace Nif
|
|||
{
|
||||
HavokMaterial mHavokMaterial;
|
||||
HavokFilter mHavokFilter;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -107,27 +118,30 @@ namespace Nif
|
|||
{
|
||||
osg::Vec4f mTranslation;
|
||||
osg::Quat mRotation;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct bhkCMSBigTri
|
||||
{
|
||||
unsigned short mTriangle[3];
|
||||
unsigned int mMaterial;
|
||||
unsigned short mWeldingInfo;
|
||||
std::array<uint16_t, 3> mTriangle;
|
||||
uint32_t mMaterial;
|
||||
uint16_t mWeldingInfo;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
struct bhkCMSChunk
|
||||
{
|
||||
osg::Vec4f mTranslation;
|
||||
unsigned int mMaterialIndex;
|
||||
unsigned short mReference;
|
||||
unsigned short mTransformIndex;
|
||||
std::vector<unsigned short> mVertices;
|
||||
std::vector<unsigned short> mIndices;
|
||||
std::vector<unsigned short> mStrips;
|
||||
std::vector<unsigned short> mWeldingInfos;
|
||||
uint32_t mMaterialIndex;
|
||||
uint16_t mReference;
|
||||
uint16_t mTransformIndex;
|
||||
std::vector<uint16_t> mVertices;
|
||||
std::vector<uint16_t> mIndices;
|
||||
std::vector<uint16_t> mStrips;
|
||||
std::vector<uint16_t> mWeldingInfos;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -180,12 +194,12 @@ namespace Nif
|
|||
{
|
||||
HavokFilter mHavokFilter;
|
||||
hkResponseType mResponseType;
|
||||
unsigned short mProcessContactDelay;
|
||||
uint16_t mProcessContactDelay;
|
||||
osg::Vec4f mTranslation;
|
||||
osg::Quat mRotation;
|
||||
osg::Vec4f mLinearVelocity;
|
||||
osg::Vec4f mAngularVelocity;
|
||||
float mInertiaTensor[3][4];
|
||||
Matrix3 mInertiaTensor;
|
||||
osg::Vec4f mCenter;
|
||||
float mMass;
|
||||
float mLinearDamping;
|
||||
|
@ -203,10 +217,11 @@ namespace Nif
|
|||
bool mEnableDeactivation{ true };
|
||||
hkSolverDeactivation mSolverDeactivation;
|
||||
hkQualityType mQualityType;
|
||||
unsigned char mAutoRemoveLevel;
|
||||
unsigned char mResponseModifierFlags;
|
||||
unsigned char mNumContactPointShapeKeys;
|
||||
uint8_t mAutoRemoveLevel;
|
||||
uint8_t mResponseModifierFlags;
|
||||
uint8_t mNumContactPointShapeKeys;
|
||||
bool mForceCollidedOntoPPU;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -222,6 +237,7 @@ namespace Nif
|
|||
bhkEntityPtr mEntityA;
|
||||
bhkEntityPtr mEntityB;
|
||||
ConstraintPriority mPriority;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
void post(Reader& nif);
|
||||
};
|
||||
|
@ -242,6 +258,7 @@ namespace Nif
|
|||
float mProportionalRecoveryVelocity;
|
||||
float mConstantRecoveryVelocity;
|
||||
bool mEnabled;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -252,6 +269,7 @@ namespace Nif
|
|||
float mTargetVelocity;
|
||||
bool mUseVelocityTarget;
|
||||
bool mEnabled;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -261,6 +279,7 @@ namespace Nif
|
|||
float mSpringConstant;
|
||||
float mSpringDamping;
|
||||
bool mEnabled;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -270,6 +289,7 @@ namespace Nif
|
|||
bhkPositionConstraintMotor mPositionMotor;
|
||||
bhkVelocityConstraintMotor mVelocityMotor;
|
||||
bhkSpringDamperConstraintMotor mSpringDamperMotor;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -289,6 +309,7 @@ namespace Nif
|
|||
float mTwistMinAngle, mTwistMaxAngle;
|
||||
float mMaxFriction;
|
||||
bhkConstraintMotorCInfo mMotor;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -301,8 +322,10 @@ namespace Nif
|
|||
osg::Vec4f mPerpAxis1;
|
||||
osg::Vec4f mPerpAxis2;
|
||||
};
|
||||
|
||||
HingeData mDataA;
|
||||
HingeData mDataB;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -315,11 +338,13 @@ namespace Nif
|
|||
osg::Vec4f mPerpAxis1;
|
||||
osg::Vec4f mPerpAxis2;
|
||||
};
|
||||
|
||||
HingeData mDataA;
|
||||
HingeData mDataB;
|
||||
float mMinAngle, mMaxAngle;
|
||||
float mMaxFriction;
|
||||
bhkConstraintMotorCInfo mMotor;
|
||||
|
||||
void read(NIFStream* nif);
|
||||
};
|
||||
|
||||
|
@ -358,7 +383,7 @@ namespace Nif
|
|||
// Bethesda Havok-specific collision object
|
||||
struct bhkCollisionObject : public NiCollisionObject
|
||||
{
|
||||
unsigned short mFlags;
|
||||
uint16_t mFlags;
|
||||
bhkWorldObjectPtr mBody;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
|
@ -375,6 +400,7 @@ namespace Nif
|
|||
bhkShapePtr mShape;
|
||||
HavokFilter mHavokFilter;
|
||||
bhkWorldObjectCInfo mWorldObjectInfo;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
@ -383,6 +409,7 @@ namespace Nif
|
|||
struct bhkEntity : public bhkWorldObject
|
||||
{
|
||||
bhkEntityCInfo mInfo;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -391,6 +418,7 @@ namespace Nif
|
|||
struct bhkBvTreeShape : public bhkShape
|
||||
{
|
||||
bhkShapePtr mShape;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
@ -400,6 +428,7 @@ namespace Nif
|
|||
{
|
||||
float mScale;
|
||||
hkpMoppCode mMopp;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -408,10 +437,11 @@ namespace Nif
|
|||
{
|
||||
HavokMaterial mHavokMaterial;
|
||||
float mRadius;
|
||||
unsigned int mGrowBy;
|
||||
uint32_t mGrowBy;
|
||||
osg::Vec4f mScale{ 1.f, 1.f, 1.f, 0.f };
|
||||
NiTriStripsDataList mData;
|
||||
std::vector<unsigned int> mFilters;
|
||||
std::vector<HavokFilter> mHavokFilters;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
@ -420,7 +450,7 @@ namespace Nif
|
|||
struct bhkPackedNiTriStripsShape : public bhkShapeCollection
|
||||
{
|
||||
std::vector<hkSubPartData> mSubshapes;
|
||||
unsigned int mUserData;
|
||||
uint32_t mUserData;
|
||||
float mRadius;
|
||||
osg::Vec4f mScale;
|
||||
hkPackedNiTriStripsDataPtr mData;
|
||||
|
@ -435,6 +465,7 @@ namespace Nif
|
|||
std::vector<TriangleData> mTriangles;
|
||||
std::vector<osg::Vec3f> mVertices;
|
||||
std::vector<hkSubPartData> mSubshapes;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -442,6 +473,7 @@ namespace Nif
|
|||
struct bhkSphereRepShape : public bhkShape
|
||||
{
|
||||
HavokMaterial mHavokMaterial;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -449,6 +481,7 @@ namespace Nif
|
|||
struct bhkConvexShape : public bhkSphereRepShape
|
||||
{
|
||||
float mRadius;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -459,6 +492,7 @@ namespace Nif
|
|||
bhkWorldObjCInfoProperty mNormalsProperty;
|
||||
std::vector<osg::Vec4f> mVertices;
|
||||
std::vector<osg::Vec4f> mNormals;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -468,6 +502,7 @@ namespace Nif
|
|||
HavokMaterial mHavokMaterial;
|
||||
float mRadius;
|
||||
osg::Matrixf mTransform;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
@ -476,6 +511,7 @@ namespace Nif
|
|||
struct bhkBoxShape : public bhkConvexShape
|
||||
{
|
||||
osg::Vec3f mExtents;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -499,28 +535,30 @@ namespace Nif
|
|||
bhkWorldObjCInfoProperty mChildShapeProperty;
|
||||
bhkWorldObjCInfoProperty mChildFilterProperty;
|
||||
std::vector<HavokFilter> mHavokFilters;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct bhkCompressedMeshShape : public bhkShape
|
||||
{
|
||||
NodePtr mTarget;
|
||||
unsigned int mUserData;
|
||||
uint32_t mUserData;
|
||||
float mRadius;
|
||||
osg::Vec4f mScale;
|
||||
bhkCompressedMeshShapeDataPtr mData;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
||||
struct bhkCompressedMeshShapeData : public bhkRefObject
|
||||
{
|
||||
unsigned int mBitsPerIndex, mBitsPerWIndex;
|
||||
unsigned int mMaskWIndex, mMaskIndex;
|
||||
uint32_t mBitsPerIndex, mBitsPerWIndex;
|
||||
uint32_t mMaskWIndex, mMaskIndex;
|
||||
float mError;
|
||||
osg::Vec4f mAabbMin, mAabbMax;
|
||||
char mWeldingType;
|
||||
char mMaterialType;
|
||||
uint8_t mWeldingType;
|
||||
uint8_t mMaterialType;
|
||||
std::vector<bhkMeshMaterial> mMaterials;
|
||||
std::vector<bhkQsTransform> mChunkTransforms;
|
||||
std::vector<osg::Vec4f> mBigVerts;
|
||||
|
@ -534,7 +572,7 @@ namespace Nif
|
|||
{
|
||||
bhkRigidBodyCInfo mInfo;
|
||||
bhkSerializableList mConstraints;
|
||||
unsigned int mBodyFlags;
|
||||
uint32_t mBodyFlags;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
@ -542,6 +580,7 @@ namespace Nif
|
|||
struct bhkSimpleShapePhantom : public bhkWorldObject
|
||||
{
|
||||
osg::Matrixf mTransform;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
@ -549,6 +588,7 @@ namespace Nif
|
|||
struct bhkConstraint : public bhkSerializable
|
||||
{
|
||||
bhkConstraintCInfo mInfo;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
void post(Reader& nif) override;
|
||||
};
|
||||
|
@ -556,18 +596,21 @@ namespace Nif
|
|||
struct bhkRagdollConstraint : public bhkConstraint
|
||||
{
|
||||
bhkRagdollConstraintCInfo mConstraint;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct bhkHingeConstraint : public bhkConstraint
|
||||
{
|
||||
bhkHingeConstraintCInfo mConstraint;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
struct bhkLimitedHingeConstraint : public bhkConstraint
|
||||
{
|
||||
bhkLimitedHingeConstraintCInfo mConstraint;
|
||||
|
||||
void read(NIFStream* nif) override;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "property.hpp"
|
||||
|
||||
#include "controlled.hpp"
|
||||
#include "data.hpp"
|
||||
#include "texture.hpp"
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
|
47
components/nif/texture.cpp
Normal file
47
components/nif/texture.cpp
Normal 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>());
|
||||
}
|
||||
|
||||
}
|
81
components/nif/texture.hpp
Normal file
81
components/nif/texture.hpp
Normal 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
|
|
@ -305,7 +305,7 @@ namespace NifBullet
|
|||
|
||||
// Check for extra data
|
||||
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);
|
||||
for (const auto& extraNode : node.extralist)
|
||||
if (!extraNode.empty())
|
||||
|
@ -316,29 +316,30 @@ namespace NifBullet
|
|||
{
|
||||
// String markers may contain important information
|
||||
// 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
|
||||
// uppercase
|
||||
if (sd->string.length() > 2 && sd->string[2] == 'C')
|
||||
if (sd->mData.length() > 2 && sd->mData[2] == 'C')
|
||||
// Collide only with camera.
|
||||
visualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
else
|
||||
// No collision.
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (e->recType == Nif::RC_BSXFlags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,13 +42,14 @@
|
|||
#include <osg/TexEnvCombine>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
#include <components/nif/controlled.hpp>
|
||||
#include <components/nif/effect.hpp>
|
||||
#include <components/nif/exception.hpp>
|
||||
#include <components/nif/extra.hpp>
|
||||
#include <components/nif/niffile.hpp>
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/nif/particle.hpp>
|
||||
#include <components/nif/property.hpp>
|
||||
#include <components/nif/texture.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
|
@ -174,16 +175,16 @@ namespace
|
|||
|
||||
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;
|
||||
Misc::StringUtils::split(tk->list[i].text, results, "\r\n");
|
||||
Misc::StringUtils::split(key.mText, results, "\r\n");
|
||||
for (std::string& result : results)
|
||||
{
|
||||
Misc::StringUtils::trim(result);
|
||||
Misc::StringUtils::lowerCaseInPlace(result);
|
||||
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);
|
||||
|
||||
extra = extra->next;
|
||||
extra = extra->mNext;
|
||||
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)
|
||||
{
|
||||
|
@ -315,8 +316,8 @@ namespace NifOsg
|
|||
osg::ref_ptr<SceneUtil::KeyframeController> callback = new NifOsg::KeyframeController(key);
|
||||
setupController(key, callback, /*animflags*/ 0);
|
||||
|
||||
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)
|
||||
Log(Debug::Verbose) << "Controller " << strdata->string << " present more than once in "
|
||||
if (!target.mKeyframeControllers.emplace(strdata->mData, callback).second)
|
||||
Log(Debug::Verbose) << "Controller " << strdata->mData << " present more than once in "
|
||||
<< nif.getFilename() << ", ignoring later version";
|
||||
}
|
||||
}
|
||||
|
@ -509,15 +510,15 @@ namespace NifOsg
|
|||
return nullptr;
|
||||
|
||||
osg::ref_ptr<osg::Image> image;
|
||||
if (!st->external && !st->data.empty())
|
||||
if (st->mExternal)
|
||||
{
|
||||
image = handleInternalTexture(st->data.getPtr());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS());
|
||||
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, imageManager->getVFS());
|
||||
image = imageManager->getImage(filename);
|
||||
}
|
||||
else if (!st->mData.empty())
|
||||
{
|
||||
image = handleInternalTexture(st->mData.getPtr());
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
|
@ -536,38 +537,41 @@ namespace NifOsg
|
|||
}
|
||||
|
||||
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 "
|
||||
<< mFilename;
|
||||
Log(Debug::Info) << "Unhandled NiTextureEffect type "
|
||||
<< static_cast<uint32_t>(textureEffect->mTextureType) << " in " << mFilename;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (textureEffect->texture.empty())
|
||||
if (textureEffect->mTexture.empty())
|
||||
{
|
||||
Log(Debug::Info) << "NiTextureEffect missing source texture in " << mFilename;
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
case Nif::NiTextureEffect::World_Perspective:
|
||||
case Nif::NiTextureEffect::CoordGenType::WorldPerspective:
|
||||
texGen->setMode(osg::TexGen::EYE_LINEAR);
|
||||
break;
|
||||
case Nif::NiTextureEffect::Sphere_Map:
|
||||
case Nif::NiTextureEffect::CoordGenType::SphereMap:
|
||||
texGen->setMode(osg::TexGen::SPHERE_MAP);
|
||||
break;
|
||||
default:
|
||||
Log(Debug::Info) << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType
|
||||
<< " in " << mFilename;
|
||||
Log(Debug::Info) << "Unhandled NiTextureEffect CoordGenType "
|
||||
<< static_cast<uint32_t>(textureEffect->mCoordGenType) << " in " << mFilename;
|
||||
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));
|
||||
if (image)
|
||||
texture2d->setTextureSize(image->s(), image->t());
|
||||
|
@ -644,7 +648,7 @@ namespace NifOsg
|
|||
|
||||
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);
|
||||
|
||||
for (const auto& extraNode : nifNode->extralist)
|
||||
|
@ -666,25 +670,25 @@ namespace NifOsg
|
|||
|
||||
// String markers may contain important information
|
||||
// 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.
|
||||
args.mHasMarkers = true;
|
||||
}
|
||||
else if (sd->string == "BONE")
|
||||
else if (sd->mData == "BONE")
|
||||
{
|
||||
node->getOrCreateUserDataContainer()->addDescription("CustomBone");
|
||||
}
|
||||
else if (sd->string.rfind(extraDataIdentifer, 0) == 0)
|
||||
else if (sd->mData.rfind(extraDataIdentifer, 0) == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -895,7 +899,7 @@ namespace NifOsg
|
|||
if (!key->mInterpolator.empty() && key->mInterpolator->recType != Nif::RC_NiTransformInterpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiKeyframeController " << key->recIndex
|
||||
<< " in " << mFilename;
|
||||
<< " in " << mFilename << ": " << key->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<KeyframeController> callback = new KeyframeController(key);
|
||||
|
@ -922,7 +926,7 @@ namespace NifOsg
|
|||
&& visctrl->mInterpolator->recType != Nif::RC_NiBoolInterpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiVisController " << visctrl->recIndex
|
||||
<< " in " << mFilename;
|
||||
<< " in " << mFilename << ": " << visctrl->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<VisController> callback(new VisController(visctrl, Loader::getHiddenNodeMask()));
|
||||
|
@ -938,7 +942,7 @@ namespace NifOsg
|
|||
&& rollctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiRollController " << rollctrl->recIndex
|
||||
<< " in " << mFilename;
|
||||
<< " in " << mFilename << ": " << rollctrl->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<RollController> callback = new RollController(rollctrl);
|
||||
|
@ -973,8 +977,9 @@ namespace NifOsg
|
|||
if (!alphactrl->mInterpolator.empty()
|
||||
&& alphactrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiAlphaController "
|
||||
<< alphactrl->recIndex << " in " << mFilename;
|
||||
Log(Debug::Error)
|
||||
<< "Unsupported interpolator type for NiAlphaController " << alphactrl->recIndex << " in "
|
||||
<< mFilename << ": " << alphactrl->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<AlphaController> osgctrl = new AlphaController(alphactrl, baseMaterial);
|
||||
|
@ -994,8 +999,9 @@ namespace NifOsg
|
|||
if (!matctrl->mInterpolator.empty()
|
||||
&& matctrl->mInterpolator->recType != Nif::RC_NiPoint3Interpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiMaterialColorController "
|
||||
<< matctrl->recIndex << " in " << mFilename;
|
||||
Log(Debug::Error)
|
||||
<< "Unsupported interpolator type for NiMaterialColorController " << matctrl->recIndex
|
||||
<< " in " << mFilename << ": " << matctrl->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
osg::ref_ptr<MaterialColorController> osgctrl = new MaterialColorController(matctrl, baseMaterial);
|
||||
|
@ -1021,7 +1027,7 @@ namespace NifOsg
|
|||
&& flipctrl->mInterpolator->recType != Nif::RC_NiFloatInterpolator)
|
||||
{
|
||||
Log(Debug::Error) << "Unsupported interpolator type for NiFlipController " << flipctrl->recIndex
|
||||
<< " in " << mFilename;
|
||||
<< " in " << mFilename << ": " << flipctrl->mInterpolator->recName;
|
||||
continue;
|
||||
}
|
||||
std::vector<osg::ref_ptr<osg::Texture2D>> textures;
|
||||
|
@ -1067,12 +1073,12 @@ namespace NifOsg
|
|||
attachTo->addChild(program);
|
||||
program->setParticleSystem(partsys);
|
||||
program->setReferenceFrame(rf);
|
||||
for (; !affectors.empty(); affectors = affectors->next)
|
||||
for (; !affectors.empty(); affectors = affectors->mNext)
|
||||
{
|
||||
if (affectors->recType == Nif::RC_NiParticleGrowFade)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -1083,9 +1089,9 @@ namespace NifOsg
|
|||
{
|
||||
const Nif::NiParticleColorModifier* cl
|
||||
= static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr());
|
||||
if (cl->data.empty())
|
||||
if (cl->mData.empty())
|
||||
continue;
|
||||
const Nif::NiColorData* clrdata = cl->data.getPtr();
|
||||
const Nif::NiColorData* clrdata = cl->mData.getPtr();
|
||||
program->addOperator(new ParticleColorAffector(clrdata));
|
||||
}
|
||||
else if (affectors->recType == Nif::RC_NiParticleRotation)
|
||||
|
@ -1095,7 +1101,7 @@ namespace NifOsg
|
|||
else
|
||||
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)
|
||||
{
|
||||
|
@ -2008,15 +2014,15 @@ namespace NifOsg
|
|||
|
||||
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;
|
||||
switch (i)
|
||||
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
|
||||
{
|
||||
case Nif::BSShaderTextureSet::TextureType_Base:
|
||||
case Nif::BSShaderTextureSet::TextureType_Normal:
|
||||
case Nif::BSShaderTextureSet::TextureType_Glow:
|
||||
case Nif::BSShaderTextureSet::TextureType::Base:
|
||||
case Nif::BSShaderTextureSet::TextureType::Normal:
|
||||
case Nif::BSShaderTextureSet::TextureType::Glow:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
|
@ -2026,7 +2032,7 @@ namespace NifOsg
|
|||
}
|
||||
}
|
||||
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::Texture2D> texture2d = new osg::Texture2D(image);
|
||||
if (image)
|
||||
|
@ -2035,17 +2041,19 @@ namespace NifOsg
|
|||
unsigned int texUnit = boundTextures.size();
|
||||
stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON);
|
||||
// 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");
|
||||
break;
|
||||
case Nif::BSShaderTextureSet::TextureType_Normal:
|
||||
case Nif::BSShaderTextureSet::TextureType::Normal:
|
||||
texture2d->setName("normalMap");
|
||||
break;
|
||||
case Nif::BSShaderTextureSet::TextureType_Glow:
|
||||
case Nif::BSShaderTextureSet::TextureType::Glow:
|
||||
texture2d->setName("emissiveMap");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
boundTextures.emplace_back(uvSet);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/nif/controlled.hpp>
|
||||
#include <components/nif/data.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
|
@ -281,20 +280,13 @@ namespace NifOsg
|
|||
|
||||
GravityAffector::GravityAffector(const Nif::NiGravity* gravity)
|
||||
: mForce(gravity->mForce)
|
||||
, mType(static_cast<ForceType>(gravity->mType))
|
||||
, mType(gravity->mType)
|
||||
, mPosition(gravity->mPosition)
|
||||
, mDirection(gravity->mDirection)
|
||||
, mDecay(gravity->mDecay)
|
||||
{
|
||||
}
|
||||
|
||||
GravityAffector::GravityAffector()
|
||||
: mForce(0)
|
||||
, mType(Type_Wind)
|
||||
, mDecay(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
GravityAffector::GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop)
|
||||
: osgParticle::Operator(copy, copyop)
|
||||
{
|
||||
|
@ -311,8 +303,8 @@ namespace NifOsg
|
|||
{
|
||||
bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);
|
||||
|
||||
if (mType == Type_Point
|
||||
|| mDecay != 0.f) // we don't need the position for Wind gravity, except if decay is being applied
|
||||
// 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;
|
||||
|
||||
mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
|
||||
|
@ -324,7 +316,7 @@ namespace NifOsg
|
|||
const float magic = 1.6f;
|
||||
switch (mType)
|
||||
{
|
||||
case Type_Wind:
|
||||
case Nif::NiGravity::ForceType::Wind:
|
||||
{
|
||||
float decayFactor = 1.f;
|
||||
if (mDecay != 0.f)
|
||||
|
@ -338,7 +330,7 @@ namespace NifOsg
|
|||
|
||||
break;
|
||||
}
|
||||
case Type_Point:
|
||||
case Nif::NiGravity::ForceType::Point:
|
||||
{
|
||||
osg::Vec3f diff = mCachedWorldPosition - particle->getPosition();
|
||||
|
||||
|
|
|
@ -10,15 +10,14 @@
|
|||
#include <osgParticle/Placer>
|
||||
#include <osgParticle/Shooter>
|
||||
|
||||
#include <components/nif/particle.hpp> // NiGravity::ForceType
|
||||
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
|
||||
#include "controller.hpp" // ValueInterpolator
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
struct NiGravity;
|
||||
struct NiPlanarCollider;
|
||||
struct NiSphericalCollider;
|
||||
struct NiColorData;
|
||||
}
|
||||
|
||||
|
@ -180,7 +179,7 @@ namespace NifOsg
|
|||
{
|
||||
public:
|
||||
GravityAffector(const Nif::NiGravity* gravity);
|
||||
GravityAffector();
|
||||
GravityAffector() = default;
|
||||
GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
GravityAffector& operator=(const GravityAffector&) = delete;
|
||||
|
@ -191,16 +190,11 @@ namespace NifOsg
|
|||
void beginOperate(osgParticle::Program*) override;
|
||||
|
||||
private:
|
||||
float mForce;
|
||||
enum ForceType
|
||||
{
|
||||
Type_Wind,
|
||||
Type_Point
|
||||
};
|
||||
ForceType mType;
|
||||
float mForce{ 0.f };
|
||||
Nif::NiGravity::ForceType mType{ Nif::NiGravity::ForceType::Wind };
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mDirection;
|
||||
float mDecay;
|
||||
float mDecay{ 0.f };
|
||||
osg::Vec3f mCachedWorldPosition;
|
||||
osg::Vec3f mCachedWorldDirection;
|
||||
};
|
||||
|
|
|
@ -32,7 +32,9 @@ namespace Resource
|
|||
};
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -62,9 +62,9 @@ namespace Resource
|
|||
{
|
||||
// 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 ((itr->second.first != nullptr && itr->second.first->referenceCount() > 1)
|
||||
|| itr->second.second == 0.0)
|
||||
itr->second.second = referenceTime;
|
||||
if ((itr->second.mValue != nullptr && itr->second.mValue->referenceCount() > 1)
|
||||
|| itr->second.mLastUsage == 0.0)
|
||||
itr->second.mLastUsage = referenceTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,10 +81,10 @@ namespace Resource
|
|||
typename ObjectCacheMap::iterator oitr = _objectCache.begin();
|
||||
while (oitr != _objectCache.end())
|
||||
{
|
||||
if (oitr->second.second <= expiryTime)
|
||||
if (oitr->second.mLastUsage <= expiryTime)
|
||||
{
|
||||
if (oitr->second.first != nullptr)
|
||||
objectsToRemove.push_back(oitr->second.first);
|
||||
if (oitr->second.mValue != nullptr)
|
||||
objectsToRemove.push_back(std::move(oitr->second.mValue));
|
||||
_objectCache.erase(oitr++);
|
||||
}
|
||||
else
|
||||
|
@ -106,7 +106,7 @@ namespace Resource
|
|||
void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
_objectCache[key] = ObjectTimeStampPair(object, timestamp);
|
||||
_objectCache[key] = Item{ object, timestamp };
|
||||
}
|
||||
|
||||
/** Remove Object from cache.*/
|
||||
|
@ -124,7 +124,7 @@ namespace Resource
|
|||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||
if (itr != _objectCache.end())
|
||||
return itr->second.first;
|
||||
return itr->second.mValue;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ namespace Resource
|
|||
const auto it = _objectCache.find(key);
|
||||
if (it == _objectCache.end())
|
||||
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. */
|
||||
|
@ -145,7 +145,7 @@ namespace Resource
|
|||
typename ObjectCacheMap::iterator itr = _objectCache.find(key);
|
||||
if (itr != _objectCache.end())
|
||||
{
|
||||
itr->second.second = timeStamp;
|
||||
itr->second.mLastUsage = timeStamp;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -158,7 +158,7 @@ namespace Resource
|
|||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +169,7 @@ namespace Resource
|
|||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
for (typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr)
|
||||
{
|
||||
osg::Object* object = itr->second.first.get();
|
||||
if (object)
|
||||
if (osg::Object* object = itr->second.mValue.get())
|
||||
{
|
||||
osg::Node* node = dynamic_cast<osg::Node*>(object);
|
||||
if (node)
|
||||
|
@ -185,7 +184,7 @@ namespace Resource
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(_objectCacheMutex);
|
||||
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. */
|
||||
|
@ -195,11 +194,26 @@ namespace Resource
|
|||
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:
|
||||
struct Item
|
||||
{
|
||||
osg::ref_ptr<osg::Object> mValue;
|
||||
double mLastUsage;
|
||||
};
|
||||
|
||||
virtual ~GenericObjectCache() {}
|
||||
|
||||
typedef std::pair<osg::ref_ptr<osg::Object>, double> ObjectTimeStampPair;
|
||||
typedef std::map<KeyType, ObjectTimeStampPair> ObjectCacheMap;
|
||||
using ObjectCacheMap = std::map<KeyType, Item, std::less<>>;
|
||||
|
||||
ObjectCacheMap _objectCache;
|
||||
mutable std::mutex _objectCacheMutex;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <components/settings/values.hpp>
|
||||
|
||||
#include "objectcache.hpp"
|
||||
|
||||
namespace VFS
|
||||
|
@ -23,11 +25,11 @@ namespace Resource
|
|||
{
|
||||
public:
|
||||
virtual ~BaseResourceManager() = default;
|
||||
virtual void updateCache(double referenceTime) {}
|
||||
virtual void clearCache() {}
|
||||
virtual void setExpiryDelay(double expiryDelay) {}
|
||||
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {}
|
||||
virtual void releaseGLObjects(osg::State* state) {}
|
||||
virtual void updateCache(double referenceTime) = 0;
|
||||
virtual void clearCache() = 0;
|
||||
virtual void setExpiryDelay(double expiryDelay) = 0;
|
||||
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const = 0;
|
||||
virtual void releaseGLObjects(osg::State* state) = 0;
|
||||
};
|
||||
|
||||
/// @brief Base class for managers that require a virtual file system and object cache.
|
||||
|
@ -39,10 +41,11 @@ namespace Resource
|
|||
public:
|
||||
typedef GenericObjectCache<KeyType> CacheType;
|
||||
|
||||
GenericResourceManager(const VFS::Manager* vfs)
|
||||
explicit GenericResourceManager(
|
||||
const VFS::Manager* vfs, double expiryDelay = Settings::cells().mCacheExpiryDelay)
|
||||
: mVFS(vfs)
|
||||
, mCache(new CacheType)
|
||||
, mExpiryDelay(0.0)
|
||||
, mExpiryDelay(expiryDelay)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -59,7 +62,7 @@ namespace Resource
|
|||
void clearCache() override { mCache->clear(); }
|
||||
|
||||
/// 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; }
|
||||
|
||||
const VFS::Manager* getVFS() const { return mVFS; }
|
||||
|
@ -77,10 +80,15 @@ namespace Resource
|
|||
class ResourceManager : public GenericResourceManager<std::string>
|
||||
{
|
||||
public:
|
||||
ResourceManager(const VFS::Manager* vfs)
|
||||
explicit ResourceManager(const VFS::Manager* vfs)
|
||||
: GenericResourceManager<std::string>(vfs)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ResourceManager(const VFS::Manager* vfs, double expiryDelay)
|
||||
: GenericResourceManager<std::string>(vfs, expiryDelay)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Terrain
|
|||
|
||||
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
||||
CompositeMapRenderer* renderer, ESM::RefId worldspace)
|
||||
: GenericResourceManager<ChunkId>(nullptr)
|
||||
: GenericResourceManager<ChunkKey>(nullptr)
|
||||
, QuadTreeWorld::ChunkManager(worldspace)
|
||||
, mStorage(storage)
|
||||
, mSceneManager(sceneMgr)
|
||||
|
@ -39,38 +39,26 @@ namespace Terrain
|
|||
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,
|
||||
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
|
||||
{
|
||||
// Override lod with the vertexLodMod adjusted value.
|
||||
// TODO: maybe we can refactor this code by moving all vertexLodMod code into this class.
|
||||
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);
|
||||
if (obj)
|
||||
|
||||
const ChunkKey key{ .mCenter = center, .mLod = lod, .mLodFlags = lodFlags };
|
||||
if (osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(key))
|
||||
return static_cast<osg::Node*>(obj.get());
|
||||
else
|
||||
{
|
||||
FindChunkTemplate find;
|
||||
find.mId = id;
|
||||
mCache->call(find);
|
||||
TerrainDrawable* templateGeometry
|
||||
= find.mFoundTemplate ? static_cast<TerrainDrawable*>(find.mFoundTemplate.get()) : nullptr;
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry);
|
||||
mCache->addEntryToObjectCache(id, node.get());
|
||||
return node;
|
||||
}
|
||||
|
||||
const TerrainDrawable* templateGeometry = nullptr;
|
||||
const TemplateKey templateKey{ .mCenter = center, .mLod = lod };
|
||||
const auto pair = mCache->lowerBound(templateKey);
|
||||
if (pair.has_value() && templateKey == TemplateKey{ .mCenter = pair->first.mCenter, .mLod = pair->first.mLod })
|
||||
templateGeometry = static_cast<const TerrainDrawable*>(pair->second.get());
|
||||
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry);
|
||||
mCache->addEntryToObjectCache(key, node.get());
|
||||
return node;
|
||||
}
|
||||
|
||||
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
|
||||
|
@ -80,14 +68,14 @@ namespace Terrain
|
|||
|
||||
void ChunkManager::clearCache()
|
||||
{
|
||||
GenericResourceManager<ChunkId>::clearCache();
|
||||
GenericResourceManager<ChunkKey>::clearCache();
|
||||
|
||||
mBufferCache.clearCache();
|
||||
}
|
||||
|
||||
void ChunkManager::releaseGLObjects(osg::State* state)
|
||||
{
|
||||
GenericResourceManager<ChunkId>::releaseGLObjects(state);
|
||||
GenericResourceManager<ChunkKey>::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,
|
||||
unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry)
|
||||
unsigned int lodFlags, bool compile, const TerrainDrawable* templateGeometry)
|
||||
{
|
||||
osg::ref_ptr<TerrainDrawable> geometry(new TerrainDrawable);
|
||||
|
||||
|
|
|
@ -28,10 +28,51 @@ namespace Terrain
|
|||
class CompositeMap;
|
||||
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
|
||||
class ChunkManager : public Resource::GenericResourceManager<ChunkId>, public QuadTreeWorld::ChunkManager
|
||||
class ChunkManager : public Resource::GenericResourceManager<ChunkKey>, public QuadTreeWorld::ChunkManager
|
||||
{
|
||||
public:
|
||||
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
||||
|
@ -55,7 +96,7 @@ namespace Terrain
|
|||
|
||||
private:
|
||||
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();
|
||||
|
||||
|
|
|
@ -544,37 +544,16 @@ namespace Terrain
|
|||
vd->setViewPoint(viewPoint);
|
||||
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();
|
||||
|
||||
float distanceModifier = 0.f;
|
||||
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
|
||||
}
|
||||
ViewDataEntry& entry = vd->getEntry(i);
|
||||
loadRenderingNode(entry, vd, cellWorldSize, grid, true);
|
||||
reporter.addProgress(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace Terrain
|
|||
const osg::BoundingBox& getWaterBoundingBox() const { return mWaterBoundingBox; }
|
||||
|
||||
void setCompositeMap(CompositeMap* map) { mCompositeMap = map; }
|
||||
CompositeMap* getCompositeMap() { return mCompositeMap; }
|
||||
CompositeMap* getCompositeMap() const { return mCompositeMap; }
|
||||
void setCompositeMapRenderer(CompositeMapRenderer* renderer) { mCompositeMapRenderer = renderer; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -17,6 +17,7 @@ Lua API reference
|
|||
openmw_core
|
||||
openmw_types
|
||||
openmw_async
|
||||
openmw_vfs
|
||||
openmw_world
|
||||
openmw_self
|
||||
openmw_nearby
|
||||
|
|
7
docs/source/reference/lua-scripting/openmw_vfs.rst
Normal file
7
docs/source/reference/lua-scripting/openmw_vfs.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
Package openmw.vfs
|
||||
==================
|
||||
|
||||
.. include:: version.rst
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/openmw_vfs.html
|
|
@ -6,7 +6,7 @@ Overview of Lua scripting
|
|||
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.
|
||||
|
||||
.. note::
|
||||
|
@ -40,6 +40,10 @@ Supported Lua 5.2 features:
|
|||
- ``__pairs`` and ``__ipairs`` metamethods;
|
||||
- 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:
|
||||
|
||||
1. If `library_name` is one of the standard libraries, then return the library.
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|: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.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
||||
|
|
|
@ -22,6 +22,7 @@ local env = {
|
|||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
vfs = require('openmw.vfs'),
|
||||
async = require('openmw.async'),
|
||||
world = require('openmw.world'),
|
||||
aux_util = require('openmw_aux.util'),
|
||||
|
|
|
@ -24,6 +24,7 @@ local env = {
|
|||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
vfs = require('openmw.vfs'),
|
||||
async = require('openmw.async'),
|
||||
nearby = require('openmw.nearby'),
|
||||
self = require('openmw.self'),
|
||||
|
|
|
@ -71,6 +71,8 @@ local env = {
|
|||
storage = require('openmw.storage'),
|
||||
core = require('openmw.core'),
|
||||
types = require('openmw.types'),
|
||||
vfs = require('openmw.vfs'),
|
||||
ambient = require('openmw.ambient'),
|
||||
async = require('openmw.async'),
|
||||
nearby = require('openmw.nearby'),
|
||||
self = require('openmw.self'),
|
||||
|
|
|
@ -12,14 +12,15 @@ set(LUA_API_FILES
|
|||
openmw/ambient.lua
|
||||
openmw/async.lua
|
||||
openmw/core.lua
|
||||
openmw/debug.lua
|
||||
openmw/nearby.lua
|
||||
openmw/postprocessing.lua
|
||||
openmw/self.lua
|
||||
openmw/types.lua
|
||||
openmw/ui.lua
|
||||
openmw/util.lua
|
||||
openmw/vfs.lua
|
||||
openmw/world.lua
|
||||
openmw/types.lua
|
||||
openmw/postprocessing.lua
|
||||
openmw/debug.lua
|
||||
)
|
||||
|
||||
foreach (f ${LUA_API_FILES})
|
||||
|
|
159
files/lua_api/openmw/vfs.lua
Normal file
159
files/lua_api/openmw/vfs.lua
Normal 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
77
files/lua_api/utf8.doclua
Normal 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
|
Loading…
Reference in a new issue