Add OpenMW commits up to 1 Dec 2019

# Conflicts:
#	CMakeLists.txt
#	apps/openmw/mwscript/aiextensions.cpp
#	apps/openmw/mwscript/statsextensions.cpp
pull/556/head
David Cernat 5 years ago
commit 9d6f3fdd09

@ -74,6 +74,7 @@ Programmers
Fil Krynicki (filkry) Fil Krynicki (filkry)
Finbar Crago(finbar-crago) Finbar Crago(finbar-crago)
Florian Weber (Florianjw) Florian Weber (Florianjw)
Gaëtan Dezeiraud (Brouilles)
Gašper Sedej Gašper Sedej
Gijsbert ter Horst (Ghostbird) Gijsbert ter Horst (Ghostbird)
Gohan1989 Gohan1989

@ -176,6 +176,9 @@
Bug #5209: Spellcasting ignores race height Bug #5209: Spellcasting ignores race height
Bug #5210: AiActivate allows actors to open dialogue and inventory windows Bug #5210: AiActivate allows actors to open dialogue and inventory windows
Bug #5211: Screen fades in if the first loaded save is in interior cell Bug #5211: Screen fades in if the first loaded save is in interior cell
Bug #5213: SameFaction script function is broken
Bug #5218: Crash when disabling ToggleBorders
Bug #5220: GetLOS crashes when actor isn't loaded
Feature #1774: Handle AvoidNode Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls

@ -2,6 +2,7 @@
brew update brew update
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install cmake
brew install qt brew install qt
brew install ccache brew install ccache

@ -332,6 +332,8 @@ IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
endif() endif()
set(Boost_NO_BOOST_CMAKE ON)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
include_directories("." include_directories("."

@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include <QApplication> #include <QApplication>
#include <QTranslator>
#include <QTextCodec> #include <QTextCodec>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
@ -26,6 +27,13 @@ int main(int argc, char *argv[])
QApplication app(argc, argv); QApplication app(argc, argv);
// Internationalization
QString locale = QLocale::system().name().section('_', 0, 0);
QTranslator appTranslator;
appTranslator.load(":/translations/" + locale + ".qm");
app.installTranslator(&appTranslator);
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());

@ -55,6 +55,7 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
iconWidget->setFlow(QListView::LeftToRight); iconWidget->setFlow(QListView::LeftToRight);
QPushButton *playButton = new QPushButton(tr("Play")); QPushButton *playButton = new QPushButton(tr("Play"));
buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));

@ -27,13 +27,13 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
setupUi(this); setupUi(this);
QStringList languages; QStringList languages;
languages << QLatin1String("English") languages << tr("English")
<< QLatin1String("French") << tr("French")
<< QLatin1String("German") << tr("German")
<< QLatin1String("Italian") << tr("Italian")
<< QLatin1String("Polish") << tr("Polish")
<< QLatin1String("Russian") << tr("Russian")
<< QLatin1String("Spanish"); << tr("Spanish");
languageComboBox->addItems(languages); languageComboBox->addItems(languages);

@ -266,5 +266,5 @@ endif (MSVC)
if(APPLE) if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT Bundle)
endif() endif()

@ -0,0 +1,16 @@
#ifndef GAME_MWBASE_ROTATIONFLAGS_H
#define GAME_MWBASE_ROTATIONFLAGS_H
namespace MWBase
{
using RotationFlags = unsigned short;
enum RotationFlag : RotationFlags
{
RotationFlag_none = 0,
RotationFlag_adjust = 1,
RotationFlag_inverseOrder = 1 << 1,
};
}
#endif

@ -1,6 +1,8 @@
#ifndef GAME_MWBASE_WORLD_H #ifndef GAME_MWBASE_WORLD_H
#define GAME_MWBASE_WORLD_H #define GAME_MWBASE_WORLD_H
#include "rotationflags.hpp"
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
@ -408,7 +410,8 @@ namespace MWBase
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr, float x, float y, float z,
RotationFlags flags = RotationFlag_inverseOrder) = 0;
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
///< Place an object. Makes a copy of the Ptr. ///< Place an object. Makes a copy of the Ptr.

@ -631,7 +631,8 @@ namespace MWGui
ESM::QuickKeys keys; ESM::QuickKeys keys;
for (int i=0; i<10; ++i) // NB: The quick key with index 9 always has Hand-to-Hand type and must not be saved
for (int i=0; i<9; ++i)
{ {
ItemWidget* button = mKey[i].button; ItemWidget* button = mKey[i].button;
@ -680,7 +681,8 @@ namespace MWGui
int i=0; int i=0;
for (ESM::QuickKeys::QuickKey& quickKey : keys.mKeys) for (ESM::QuickKeys::QuickKey& quickKey : keys.mKeys)
{ {
if (i >= 10) // NB: The quick key with index 9 always has Hand-to-Hand type and must not be loaded
if (i >= 9)
return; return;
mSelected = &mKey[i]; mSelected = &mKey[i];

@ -1917,23 +1917,25 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
break; break;
case UpperCharState_StartToMinAttack: case UpperCharState_StartToMinAttack:
case UpperCharState_MaxAttackToMinHit:
{ {
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part. if (mUpperBodyState == UpperCharState_StartToMinAttack)
// Happens if the player did not hold the attack button.
// Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be random.
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (mAttackingOrSpell || minAttackTime == maxAttackTime)
{ {
start = mAttackType+" min attack"; // If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
stop = mAttackType+" max attack"; // Happens if the player did not hold the attack button.
mUpperBodyState = UpperCharState_MinAttackToMaxAttack; // Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be random.
break; float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (mAttackingOrSpell || minAttackTime == maxAttackTime)
{
start = mAttackType+" min attack";
stop = mAttackType+" max attack";
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
break;
}
playSwishSound(0.0f);
} }
playSwishSound(0.0f);
}
// Fall-through
case UpperCharState_MaxAttackToMinHit:
if(mAttackType == "shoot") if(mAttackType == "shoot")
{ {
start = mAttackType+" min hit"; start = mAttackType+" min hit";
@ -1946,6 +1948,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
} }
mUpperBodyState = UpperCharState_MinHitToHit; mUpperBodyState = UpperCharState_MinHitToHit;
break; break;
}
case UpperCharState_MinHitToHit: case UpperCharState_MinHitToHit:
if(mAttackType == "shoot") if(mAttackType == "shoot")
{ {

@ -127,54 +127,39 @@ namespace MWMechanics
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor); mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
const float distSameSpot = mDistSameSpot * duration; const float distSameSpot = mDistSameSpot * duration;
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot; const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
mPrev = pos; mPrev = pos;
switch(mWalkState) if (mWalkState != State_Evade)
{ {
case State_Norm: if(!samePosition)
{ {
if(!samePosition) mWalkState = State_Norm;
break; mStuckDuration = 0;
else mEvadeDuration = 0;
mWalkState = State_CheckStuck; return;
} }
/* FALL THROUGH */
case State_CheckStuck: mWalkState = State_CheckStuck;
{ mStuckDuration += duration;
if(!samePosition) // consider stuck only if position unchanges for a period
{ if(mStuckDuration < DURATION_SAME_SPOT)
mWalkState = State_Norm; return; // still checking, note duration added to timer
mStuckDuration = 0; else
break;
}
else
{
mStuckDuration += duration;
// consider stuck only if position unchanges for a period
if(mStuckDuration < DURATION_SAME_SPOT)
break; // still checking, note duration added to timer
else
{
mStuckDuration = 0;
mWalkState = State_Evade;
chooseEvasionDirection();
}
}
}
/* FALL THROUGH */
case State_Evade:
{ {
mEvadeDuration += duration; mStuckDuration = 0;
if(mEvadeDuration >= DURATION_TO_EVADE) mWalkState = State_Evade;
{ chooseEvasionDirection();
// tried to evade, assume all is ok and start again
mWalkState = State_Norm;
mEvadeDuration = 0;
}
} }
/* NO DEFAULT CASE */ }
mEvadeDuration += duration;
if(mEvadeDuration >= DURATION_TO_EVADE)
{
// tried to evade, assume all is ok and start again
mWalkState = State_Norm;
mEvadeDuration = 0;
} }
} }

@ -448,9 +448,11 @@ namespace MWScript
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->searchPtr(actorID, true, false);
Interpreter::Type_Integer value = MWBase::Environment::get().getMechanicsManager()->isActorDetected(actor, observer); Interpreter::Type_Integer value = 0;
if (!actor.isEmpty())
value = MWBase::Environment::get().getMechanicsManager()->isActorDetected(actor, observer);
runtime.push (value); runtime.push (value);
} }
@ -470,9 +472,9 @@ namespace MWScript
runtime.pop(); runtime.pop();
MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true); MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->searchPtr(actorID, true, false);
bool value = false; bool value = false;
if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor()) if (!dest.isEmpty() && source.getClass().isActor() && dest.getClass().isActor())
{ {
value = MWBase::Environment::get().getWorld()->getLOS(source,dest); value = MWBase::Environment::get().getWorld()->getLOS(source,dest);
} }
@ -513,7 +515,7 @@ namespace MWScript
std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); std::string targetID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(targetID, true); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetID, true, false);
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -521,12 +523,13 @@ namespace MWScript
Track whether this actor is already in combat with its target, to ensure we don't Track whether this actor is already in combat with its target, to ensure we don't
send repetitive packets to the server send repetitive packets to the server
*/ */
bool alreadyInCombatWithTarget = actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(target); bool alreadyInCombatWithTarget = !target.isEmpty() ? actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(target) : false;
/* /*
End of tes3mp addition End of tes3mp addition
*/ */
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target); if (!target.isEmpty())
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -535,7 +538,7 @@ namespace MWScript
cell authority or not; the server can decide if it wants to comply with them by cell authority or not; the server can decide if it wants to comply with them by
forwarding them to the cell authority forwarding them to the cell authority
*/ */
if (target && !alreadyInCombatWithTarget) if (!target.isEmpty() && !alreadyInCombatWithTarget)
{ {
mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList(); mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();
actorList->reset(); actorList->reset();

@ -269,7 +269,7 @@ namespace MWScript
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr)); runtime.push(player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr)));
} }
}; };

@ -1248,7 +1248,9 @@ namespace MWScript
return; return;
} }
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetId, false, false);
if (target.isEmpty())
return;
MWMechanics::CastSpell cast(ptr, target, false, true); MWMechanics::CastSpell cast(ptr, target, false, true);
cast.playSpellCastingEffects(spell->mId, false); cast.playSpellCastingEffects(spell->mId, false);

@ -496,16 +496,6 @@ namespace MWScript
{ {
// Apply looping particles immediately for constant effects // Apply looping particles immediately for constant effects
MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);
// The spell may have an instant effect which must be handled immediately.
for (const auto& effect : creatureStats.getSpells().getMagicEffects())
{
if (effect.second.getMagnitude() <= 0)
continue;
MWMechanics::CastSpell cast(ptr, ptr);
if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))
creatureStats.getSpells().purgeEffect(effect.first.mId);
}
} }
} }
}; };
@ -533,6 +523,16 @@ namespace MWScript
if (spells.hasSpell(id)) if (spells.hasSpell(id))
{ {
// The spell may have an instant effect which must be handled before the spell's removal.
for (const auto& effect : creatureStats.getSpells().getMagicEffects())
{
if (effect.second.getMagnitude() <= 0)
continue;
MWMechanics::CastSpell cast(ptr, ptr);
if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))
creatureStats.getSpells().purgeEffect(effect.first.mId);
}
ptr.getClass().getCreatureStats(ptr).getSpells().remove(id); ptr.getClass().getCreatureStats(ptr).getSpells().remove(id);
if (ptr == MWMechanics::getPlayer()) if (ptr == MWMechanics::getPlayer())

@ -344,6 +344,7 @@ namespace MWWorld
ContainerStoreIteratorBase& operator++ (); ContainerStoreIteratorBase& operator++ ();
ContainerStoreIteratorBase operator++ (int); ContainerStoreIteratorBase operator++ (int);
ContainerStoreIteratorBase& operator= (const ContainerStoreIteratorBase& rhs); ContainerStoreIteratorBase& operator= (const ContainerStoreIteratorBase& rhs);
ContainerStoreIteratorBase (const ContainerStoreIteratorBase& rhs) = default;
int getType() const; int getType() const;
const ContainerStore *getContainerStore() const; const ContainerStore *getContainerStore() const;

@ -51,6 +51,8 @@
namespace namespace
{ {
using MWWorld::RotationOrder;
osg::Quat makeActorOsgQuat(const ESM::Position& position) osg::Quat makeActorOsgQuat(const ESM::Position& position)
{ {
return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1)); return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1));
@ -78,7 +80,7 @@ namespace
* osg::Quat(xr, osg::Vec3(-1, 0, 0)); * osg::Quat(xr, osg::Vec3(-1, 0, 0));
} }
void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, const bool inverseRotationOrder) void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, RotationOrder order)
{ {
if (!ptr.getRefData().getBaseNode()) if (!ptr.getRefData().getBaseNode())
return; return;
@ -86,7 +88,7 @@ namespace
rendering.rotateObject(ptr, rendering.rotateObject(ptr,
ptr.getClass().isActor() ptr.getClass().isActor()
? makeActorOsgQuat(ptr.getRefData().getPosition()) ? makeActorOsgQuat(ptr.getRefData().getPosition())
: (inverseRotationOrder : (order == RotationOrder::inverse
? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition()) ? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition())
: makeObjectOsgQuat(ptr.getRefData().getPosition())) : makeObjectOsgQuat(ptr.getRefData().getPosition()))
); );
@ -111,7 +113,7 @@ namespace
model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
ptr.getClass().insertObjectRendering(ptr, model, rendering); ptr.getClass().insertObjectRendering(ptr, model, rendering);
setNodeRotation(ptr, rendering, false); setNodeRotation(ptr, rendering, RotationOrder::direct);
ptr.getClass().insertObject (ptr, model, physics); ptr.getClass().insertObject (ptr, model, physics);
@ -190,9 +192,9 @@ namespace
} }
void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering, bool inverseRotationOrder) MWRender::RenderingManager& rendering, RotationOrder order)
{ {
setNodeRotation(ptr, rendering, inverseRotationOrder); setNodeRotation(ptr, rendering, order);
physics.updateRotation(ptr); physics.updateRotation(ptr);
} }
@ -289,9 +291,9 @@ namespace
namespace MWWorld namespace MWWorld
{ {
void Scene::updateObjectRotation (const Ptr& ptr, bool inverseRotationOrder) void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order)
{ {
::updateObjectRotation(ptr, *mPhysics, mRendering, inverseRotationOrder); ::updateObjectRotation(ptr, *mPhysics, mRendering, order);
} }
void Scene::updateObjectScale(const Ptr &ptr) void Scene::updateObjectScale(const Ptr &ptr)

@ -51,6 +51,12 @@ namespace MWWorld
class CellStore; class CellStore;
class CellPreloader; class CellPreloader;
enum class RotationOrder
{
direct,
inverse
};
class Scene class Scene
{ {
public: public:
@ -137,7 +143,7 @@ namespace MWWorld
void removeObjectFromScene (const Ptr& ptr); void removeObjectFromScene (const Ptr& ptr);
///< Remove an object from the scene, but not from the world model. ///< Remove an object from the scene, but not from the world model.
void updateObjectRotation (const Ptr& ptr, bool inverseRotationOrder); void updateObjectRotation(const Ptr& ptr, RotationOrder order);
void updateObjectScale(const Ptr& ptr); void updateObjectScale(const Ptr& ptr);
bool isCellActive(const CellStore &cell); bool isCellActive(const CellStore &cell);

@ -92,6 +92,8 @@ namespace MWWorld
: mIter(iter) : mIter(iter)
{} {}
SharedIterator& operator=(const SharedIterator&) = default;
SharedIterator &operator++() { SharedIterator &operator++() {
++mIter; ++mIter;
return *this; return *this;

@ -1550,13 +1550,13 @@ namespace MWWorld
mShouldUpdateNavigator = updateNavigatorObject(object) || mShouldUpdateNavigator; mShouldUpdateNavigator = updateNavigatorObject(object) || mShouldUpdateNavigator;
} }
void World::rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust) void World::rotateObjectImp(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)
{ {
const float pi = static_cast<float>(osg::PI); const float pi = static_cast<float>(osg::PI);
ESM::Position pos = ptr.getRefData().getPosition(); ESM::Position pos = ptr.getRefData().getPosition();
float *objRot = pos.rot; float *objRot = pos.rot;
if(adjust) if (flags & MWBase::RotationFlag_adjust)
{ {
objRot[0] += rot.x(); objRot[0] += rot.x();
objRot[1] += rot.y(); objRot[1] += rot.y();
@ -1588,7 +1588,9 @@ namespace MWWorld
if(ptr.getRefData().getBaseNode() != 0) if(ptr.getRefData().getBaseNode() != 0)
{ {
mWorldScene->updateObjectRotation(ptr, true); const auto order = flags & MWBase::RotationFlag_inverseOrder
? RotationOrder::inverse : RotationOrder::direct;
mWorldScene->updateObjectRotation(ptr, order);
if (const auto object = mPhysics->getObject(ptr)) if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(object); updateNavigatorObject(object);
@ -1660,9 +1662,9 @@ namespace MWWorld
} }
} }
void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) void World::rotateObject (const Ptr& ptr, float x, float y, float z, MWBase::RotationFlags flags)
{ {
rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust); rotateObjectImp(ptr, osg::Vec3f(x, y, z), flags);
} }
void World::rotateWorldObject (const Ptr& ptr, osg::Quat rotate) void World::rotateWorldObject (const Ptr& ptr, osg::Quat rotate)
@ -1868,7 +1870,7 @@ namespace MWWorld
float diff = duration * osg::DegreesToRadians(90.f); float diff = duration * osg::DegreesToRadians(90.f);
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot); float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot);
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot); rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none);
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot; bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
@ -1912,11 +1914,9 @@ namespace MWWorld
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound); MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
} }
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot); rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot, MWBase::RotationFlag_none);
} }
// the rotation order we want to use
mWorldScene->updateObjectRotation(door, false);
return reached; return reached;
} }
@ -2771,7 +2771,7 @@ namespace MWWorld
player.getClass().getInventoryStore(player).setContListener(anim); player.getClass().getInventoryStore(player).setContListener(anim);
scaleObject(player, player.getCellRef().getScale()); // apply race height scaleObject(player, player.getCellRef().getScale()); // apply race height
rotateObject(player, 0.f, 0.f, 0.f, true); rotateObject(player, 0.f, 0.f, 0.f, MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust);
MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr()); MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr());
MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr()); MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr());

@ -126,7 +126,7 @@ namespace MWWorld
void updateWeather(float duration, bool paused = false); void updateWeather(float duration, bool paused = false);
int getDaysPerMonth (int month) const; int getDaysPerMonth (int month) const;
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust); void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false); Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false);
///< @return an updated Ptr in case the Ptr's cell changes ///< @return an updated Ptr in case the Ptr's cell changes
@ -507,7 +507,8 @@ namespace MWWorld
/// @note Rotations via this method use a different rotation order than the initial rotations in the CS. This /// @note Rotations via this method use a different rotation order than the initial rotations in the CS. This
/// could be considered a bug, but is needed for MW compatibility. /// could be considered a bug, but is needed for MW compatibility.
/// \param adjust indicates rotation should be set or adjusted /// \param adjust indicates rotation should be set or adjusted
void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false) override; void rotateObject (const Ptr& ptr, float x, float y, float z,
MWBase::RotationFlags flags = MWBase::RotationFlag_inverseOrder) override;
MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) override; MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) override;
///< Place an object. Makes a copy of the Ptr. ///< Place an object. Makes a copy of the Ptr.

@ -361,9 +361,9 @@ namespace
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
0.707107067108154296875, 0, -3.535533905029296875, 1.41421353816986083984375, 0, 1.1920928955078125e-07,
-0.70710659027099609375, 0, -3.535533905029296875, -1.41421353816986083984375, 0, -1.1920928955078125e-07,
2.384185791015625e-07, 0, -4.24264049530029296875, 1.1920928955078125e-07, 0, -1.41421353816986083984375,
})); }));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground}));

@ -0,0 +1,38 @@
#ifndef OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H
#define OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H
#include <LinearMath/btVector3.h>
#include <LinearMath/btTransform.h>
#include <algorithm>
namespace BulletHelpers
{
inline btVector3 min(const btVector3& a, const btVector3& b)
{
return btVector3(std::min(a.x(), b.x()), std::min(a.y(), b.y()), std::min(a.z(), b.z()));
}
inline btVector3 max(const btVector3& a, const btVector3& b)
{
return btVector3(std::max(a.x(), b.x()), std::max(a.y(), b.y()), std::max(a.z(), b.z()));
}
// http://dev.theomader.com/transform-bounding-boxes/
inline void transformBoundingBox(const btTransform& transform, btVector3& aabbMin, btVector3& aabbMax)
{
const btVector3 xa(transform.getBasis().getColumn(0) * aabbMin.x());
const btVector3 xb(transform.getBasis().getColumn(0) * aabbMax.x());
const btVector3 ya(transform.getBasis().getColumn(1) * aabbMin.y());
const btVector3 yb(transform.getBasis().getColumn(1) * aabbMax.y());
const btVector3 za(transform.getBasis().getColumn(2) * aabbMin.z());
const btVector3 zb(transform.getBasis().getColumn(2) * aabbMax.z());
aabbMin = min(xa, xb) + min(ya, yb) + min(za, zb) + transform.getOrigin();
aabbMax = max(xa, xb) + max(ya, yb) + max(za, zb) + transform.getOrigin();
}
}
#endif

@ -97,9 +97,13 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cout_rdbuf = std::cout.rdbuf ();
std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
#if !(defined(_WIN32) && defined(_DEBUG)) #if defined(_WIN32) && defined(_DEBUG)
boost::iostreams::stream_buffer<Debug::DebugOutput> sb;
#else
boost::iostreams::stream_buffer<Debug::Tee> coutsb; boost::iostreams::stream_buffer<Debug::Tee> coutsb;
boost::iostreams::stream_buffer<Debug::Tee> cerrsb; boost::iostreams::stream_buffer<Debug::Tee> cerrsb;
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
#endif #endif
const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log";
@ -113,7 +117,6 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
#if defined(_WIN32) && defined(_DEBUG) #if defined(_WIN32) && defined(_DEBUG)
// Redirect cout and cerr to VS debug output when running in debug mode // Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<Debug::DebugOutput> sb;
sb.open(Debug::DebugOutput()); sb.open(Debug::DebugOutput());
std::cout.rdbuf (&sb); std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb); std::cerr.rdbuf (&sb);
@ -121,8 +124,6 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c
// Redirect cout and cerr to the log file // Redirect cout and cerr to the log file
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
coutsb.open (Debug::Tee(logfile, oldcout)); coutsb.open (Debug::Tee(logfile, oldcout));
cerrsb.open (Debug::Tee(logfile, oldcerr)); cerrsb.open (Debug::Tee(logfile, oldcerr));

@ -135,7 +135,7 @@ namespace DetourNavigator
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: ", e.what(); Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what();
} }
} }
Log(Debug::Debug) << "Stop navigator jobs processing"; Log(Debug::Debug) << "Stop navigator jobs processing";

@ -5,6 +5,7 @@
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "exceptions.hpp" #include "exceptions.hpp"
#include <components/bullethelpers/transformboundingbox.hpp>
#include <components/bullethelpers/processtrianglecallback.hpp> #include <components/bullethelpers/processtrianglecallback.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
@ -13,6 +14,7 @@
#include <BulletCollision/CollisionShapes/btConcaveShape.h> #include <BulletCollision/CollisionShapes/btConcaveShape.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <LinearMath/btTransform.h> #include <LinearMath/btTransform.h>
#include <LinearMath/btAabbUtil2.h>
#include <algorithm> #include <algorithm>
@ -57,7 +59,7 @@ namespace DetourNavigator
return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)
{ {
for (std::size_t i = 3; i > 0; --i) for (std::size_t i = 3; i > 0; --i)
addTriangleVertex(transform(triangle[i - 1])); addTriangleVertex(triangle[i - 1]);
mAreaTypes.push_back(areaType); mAreaTypes.push_back(areaType);
})); }));
} }
@ -68,7 +70,7 @@ namespace DetourNavigator
return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)
{ {
for (std::size_t i = 0; i < 3; ++i) for (std::size_t i = 0; i < 3; ++i)
addTriangleVertex(transform(triangle[i])); addTriangleVertex(triangle[i]);
mAreaTypes.push_back(areaType); mAreaTypes.push_back(areaType);
})); }));
} }
@ -131,8 +133,34 @@ namespace DetourNavigator
shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
aabbMin = transform(aabbMin); const btVector3 boundsMin(mBounds.mMin.x(), mBounds.mMin.y(),
aabbMax = transform(aabbMax); -std::numeric_limits<btScalar>::max() * std::numeric_limits<btScalar>::epsilon());
const btVector3 boundsMax(mBounds.mMax.x(), mBounds.mMax.y(),
std::numeric_limits<btScalar>::max() * std::numeric_limits<btScalar>::epsilon());
auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex)
{
std::array<btVector3, 3> transformed;
for (std::size_t i = 0; i < 3; ++i)
transformed[i] = transform(triangle[i]);
if (TestTriangleAgainstAabb2(transformed.data(), boundsMin, boundsMax))
callback.processTriangle(transformed.data(), partId, triangleIndex);
});
shape.processAllTriangles(&wrapper, aabbMin, aabbMax);
}
void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform,
btTriangleCallback&& callback)
{
using BulletHelpers::transformBoundingBox;
btVector3 aabbMin;
btVector3 aabbMax;
shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
transformBoundingBox(transform, aabbMin, aabbMax);
aabbMin.setX(std::max(mBounds.mMin.x(), aabbMin.x())); aabbMin.setX(std::max(mBounds.mMin.x(), aabbMin.x()));
aabbMin.setX(std::min(mBounds.mMax.x(), aabbMin.x())); aabbMin.setX(std::min(mBounds.mMax.x(), aabbMin.x()));
@ -144,20 +172,17 @@ namespace DetourNavigator
aabbMax.setY(std::max(mBounds.mMin.y(), aabbMax.y())); aabbMax.setY(std::max(mBounds.mMin.y(), aabbMax.y()));
aabbMax.setY(std::min(mBounds.mMax.y(), aabbMax.y())); aabbMax.setY(std::min(mBounds.mMax.y(), aabbMax.y()));
const auto inversedTransform = transform.inverse(); transformBoundingBox(transform.inverse(), aabbMin, aabbMax);
aabbMin = inversedTransform(aabbMin); auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex)
aabbMax = inversedTransform(aabbMax); {
std::array<btVector3, 3> transformed;
aabbMin.setX(std::min(aabbMin.x(), aabbMax.x())); for (std::size_t i = 0; i < 3; ++i)
aabbMin.setY(std::min(aabbMin.y(), aabbMax.y())); transformed[i] = transform(triangle[i]);
aabbMin.setZ(std::min(aabbMin.z(), aabbMax.z())); callback.processTriangle(transformed.data(), partId, triangleIndex);
});
aabbMax.setX(std::max(aabbMin.x(), aabbMax.x()));
aabbMax.setY(std::max(aabbMin.y(), aabbMax.y()));
aabbMax.setZ(std::max(aabbMin.z(), aabbMax.z()));
shape.processAllTriangles(&callback, aabbMin, aabbMax); shape.processAllTriangles(&wrapper, aabbMin, aabbMax);
} }
void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition) void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition)

@ -48,6 +48,8 @@ namespace DetourNavigator
void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback); void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback);
void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback);
void addTriangleVertex(const btVector3& worldPosition); void addTriangleVertex(const btVector3& worldPosition);
void addVertex(const btVector3& worldPosition); void addVertex(const btVector3& worldPosition);

@ -76,7 +76,14 @@ ParticleShooter::ParticleShooter()
ParticleShooter::ParticleShooter(const ParticleShooter &copy, const osg::CopyOp &copyop) ParticleShooter::ParticleShooter(const ParticleShooter &copy, const osg::CopyOp &copyop)
: osgParticle::Shooter(copy, copyop) : osgParticle::Shooter(copy, copyop)
{ {
*this = copy; mMinSpeed = copy.mMinSpeed;
mMaxSpeed = copy.mMaxSpeed;
mHorizontalDir = copy.mHorizontalDir;
mHorizontalAngle = copy.mHorizontalAngle;
mVerticalDir = copy.mVerticalDir;
mVerticalAngle = copy.mVerticalAngle;
mLifetime = copy.mLifetime;
mLifetimeRandom = copy.mLifetimeRandom;
} }
void ParticleShooter::shoot(osgParticle::Particle *particle) const void ParticleShooter::shoot(osgParticle::Particle *particle) const
@ -112,7 +119,9 @@ GrowFadeAffector::GrowFadeAffector()
GrowFadeAffector::GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop) GrowFadeAffector::GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop)
: osgParticle::Operator(copy, copyop) : osgParticle::Operator(copy, copyop)
{ {
*this = copy; mGrowTime = copy.mGrowTime;
mFadeTime = copy.mFadeTime;
mCachedDefaultSize = copy.mCachedDefaultSize;
} }
void GrowFadeAffector::beginOperate(osgParticle::Program *program) void GrowFadeAffector::beginOperate(osgParticle::Program *program)
@ -143,7 +152,7 @@ ParticleColorAffector::ParticleColorAffector()
ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy, const osg::CopyOp &copyop) ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy, const osg::CopyOp &copyop)
: osgParticle::Operator(copy, copyop) : osgParticle::Operator(copy, copyop)
{ {
*this = copy; mData = copy.mData;
} }
void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */) void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
@ -172,7 +181,13 @@ GravityAffector::GravityAffector()
GravityAffector::GravityAffector(const GravityAffector &copy, const osg::CopyOp &copyop) GravityAffector::GravityAffector(const GravityAffector &copy, const osg::CopyOp &copyop)
: osgParticle::Operator(copy, copyop) : osgParticle::Operator(copy, copyop)
{ {
*this = copy; mForce = copy.mForce;
mType = copy.mType;
mPosition = copy.mPosition;
mDirection = copy.mDirection;
mDecay = copy.mDecay;
mCachedWorldPosition = copy.mCachedWorldPosition;
mCachedWorldDirection = copy.mCachedWorldDirection;
} }
void GravityAffector::beginOperate(osgParticle::Program* program) void GravityAffector::beginOperate(osgParticle::Program* program)

@ -78,6 +78,8 @@ namespace NifOsg
ParticleShooter(); ParticleShooter();
ParticleShooter(const ParticleShooter& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); ParticleShooter(const ParticleShooter& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
ParticleShooter& operator=(const ParticleShooter&) = delete;
META_Object(NifOsg, ParticleShooter) META_Object(NifOsg, ParticleShooter)
virtual void shoot(osgParticle::Particle* particle) const; virtual void shoot(osgParticle::Particle* particle) const;
@ -135,6 +137,8 @@ namespace NifOsg
GrowFadeAffector(); GrowFadeAffector();
GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
GrowFadeAffector& operator=(const GrowFadeAffector&) = delete;
META_Object(NifOsg, GrowFadeAffector) META_Object(NifOsg, GrowFadeAffector)
virtual void beginOperate(osgParticle::Program* program); virtual void beginOperate(osgParticle::Program* program);
@ -155,6 +159,8 @@ namespace NifOsg
ParticleColorAffector(); ParticleColorAffector();
ParticleColorAffector(const ParticleColorAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); ParticleColorAffector(const ParticleColorAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
ParticleColorAffector& operator=(const ParticleColorAffector&) = delete;
META_Object(NifOsg, ParticleColorAffector) META_Object(NifOsg, ParticleColorAffector)
virtual void operate(osgParticle::Particle* particle, double dt); virtual void operate(osgParticle::Particle* particle, double dt);
@ -170,6 +176,8 @@ namespace NifOsg
GravityAffector(); GravityAffector();
GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
GravityAffector& operator=(const GravityAffector&) = delete;
META_Object(NifOsg, GravityAffector) META_Object(NifOsg, GravityAffector)
virtual void operate(osgParticle::Particle* particle, double dt); virtual void operate(osgParticle::Particle* particle, double dt);

@ -91,8 +91,9 @@ void CellBorder::destroyCellBorderGeometry(int x, int y)
void CellBorder::destroyCellBorderGeometry() void CellBorder::destroyCellBorderGeometry()
{ {
for (CellGrid::iterator it = mCellBorderNodes.begin(); it != mCellBorderNodes.end(); ++it) for (const auto& v : mCellBorderNodes)
destroyCellBorderGeometry(it->first.first,it->first.second); mRoot->removeChild(v.second);
mCellBorderNodes.clear();
} }
} }

@ -176,12 +176,12 @@ recast scale factor
:Type: floating point :Type: floating point
:Range: > 0.0 :Range: > 0.0
:Default: 0.023529411764705882 :Default: 0.029411764705882353
Scale of nav mesh coordinates to world coordinates. Recastnavigation builds voxels for world geometry. Scale of nav mesh coordinates to world coordinates. Recastnavigation builds voxels for world geometry.
Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size
"recast scale factor" / "cell size". Default value calculates by this equation: "recast scale factor" / "cell size". Default value calculates by this equation:
sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels). sStepSizeUp * "recast scale factor" / "cell size" = 5 (max climb height should be equal to 4 voxels).
Changing this value will change generated nav mesh. Some locations may become unavailable for NPC and creatures. Changing this value will change generated nav mesh. Some locations may become unavailable for NPC and creatures.
Pay attention to slopes and roofs when change it. Increasing this value will reduce nav mesh update latency. Pay attention to slopes and roofs when change it. Increasing this value will reduce nav mesh update latency.

@ -148,7 +148,7 @@ namespace ICS
float step = FromString<float>(xmlInterval->Attribute("step")); float step = FromString<float>(xmlInterval->Attribute("step"));
ICS_LOG("Applying Bezier filter to channel [number=" ICS_LOG("Applying Bezier filter to channel [number="
+ ToString<int>(ch) + ", startX=" + ToString<size_t>(ch) + ", startX="
+ ToString<float>(startX) + ", startY=" + ToString<float>(startX) + ", startY="
+ ToString<float>(startY) + ", midX=" + ToString<float>(startY) + ", midX="
+ ToString<float>(midX) + ", midY=" + ToString<float>(midX) + ", midY="

@ -642,8 +642,8 @@ enable = true
# Scale of NavMesh coordinates to world coordinates (value > 0.0). Recastnavigation builds voxels for world geometry. # Scale of NavMesh coordinates to world coordinates (value > 0.0). Recastnavigation builds voxels for world geometry.
# Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size # Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size
# "recast scale factor" / "cell size". Default value calculates by this equation: # "recast scale factor" / "cell size". Default value calculates by this equation:
# sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels) # sStepSizeUp * "recast scale factor" / "cell size" = 5 (max climb height should be equal to 4 voxels)
recast scale factor = 0.023529411764705882 recast scale factor = 0.029411764705882353
# The z-axis cell size to use for fields. (value > 0.0) # The z-axis cell size to use for fields. (value > 0.0)
# Defines voxel/grid/cell size. So their values have significant # Defines voxel/grid/cell size. So their values have significant
@ -671,7 +671,7 @@ detail sample max error = 1.0
max simplification error = 1.3 max simplification error = 1.3
# The width and height of each tile. (value > 0) # The width and height of each tile. (value > 0)
tile size = 64 tile size = 128
# The size of the non-navigable border around the heightfield. (value >= 0) # The size of the non-navigable border around the heightfield. (value >= 0)
border size = 16 border size = 16

Loading…
Cancel
Save