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)
Finbar Crago(finbar-crago)
Florian Weber (Florianjw)
Gaëtan Dezeiraud (Brouilles)
Gašper Sedej
Gijsbert ter Horst (Ghostbird)
Gohan1989

@ -176,6 +176,9 @@
Bug #5209: Spellcasting ignores race height
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 #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 #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls

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

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

@ -1,6 +1,7 @@
#include <iostream>
#include <QApplication>
#include <QTranslator>
#include <QTextCodec>
#include <QDir>
#include <QDebug>
@ -26,6 +27,13 @@ int main(int argc, char *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
QDir dir(QCoreApplication::applicationDirPath());

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

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

@ -266,5 +266,5 @@ endif (MSVC)
if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT Bundle)
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
#define GAME_MWBASE_WORLD_H
#include "rotationflags.hpp"
#include <vector>
#include <map>
#include <set>
@ -408,7 +410,8 @@ namespace MWBase
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;
///< Place an object. Makes a copy of the Ptr.

@ -631,7 +631,8 @@ namespace MWGui
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;
@ -680,7 +681,8 @@ namespace MWGui
int i=0;
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;
mSelected = &mKey[i];

@ -1917,23 +1917,25 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
break;
case UpperCharState_StartToMinAttack:
case UpperCharState_MaxAttackToMinHit:
{
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
// 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)
if (mUpperBodyState == UpperCharState_StartToMinAttack)
{
start = mAttackType+" min attack";
stop = mAttackType+" max attack";
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
break;
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
// 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";
stop = mAttackType+" max attack";
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
break;
}
playSwishSound(0.0f);
}
playSwishSound(0.0f);
}
// Fall-through
case UpperCharState_MaxAttackToMinHit:
if(mAttackType == "shoot")
{
start = mAttackType+" min hit";
@ -1946,6 +1948,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
}
mUpperBodyState = UpperCharState_MinHitToHit;
break;
}
case UpperCharState_MinHitToHit:
if(mAttackType == "shoot")
{

@ -127,54 +127,39 @@ namespace MWMechanics
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
const float distSameSpot = mDistSameSpot * duration;
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
mPrev = pos;
switch(mWalkState)
if (mWalkState != State_Evade)
{
case State_Norm:
if(!samePosition)
{
if(!samePosition)
break;
else
mWalkState = State_CheckStuck;
mWalkState = State_Norm;
mStuckDuration = 0;
mEvadeDuration = 0;
return;
}
/* FALL THROUGH */
case State_CheckStuck:
{
if(!samePosition)
{
mWalkState = State_Norm;
mStuckDuration = 0;
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:
mWalkState = State_CheckStuck;
mStuckDuration += duration;
// consider stuck only if position unchanges for a period
if(mStuckDuration < DURATION_SAME_SPOT)
return; // still checking, note duration added to timer
else
{
mEvadeDuration += duration;
if(mEvadeDuration >= DURATION_TO_EVADE)
{
// tried to evade, assume all is ok and start again
mWalkState = State_Norm;
mEvadeDuration = 0;
}
mStuckDuration = 0;
mWalkState = State_Evade;
chooseEvasionDirection();
}
/* 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);
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);
}
@ -470,9 +472,9 @@ namespace MWScript
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;
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);
}
@ -513,7 +515,7 @@ namespace MWScript
std::string targetID = runtime.getStringLiteral (runtime[0].mInteger);
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
@ -521,12 +523,13 @@ namespace MWScript
Track whether this actor is already in combat with its target, to ensure we don't
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
*/
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);
if (!target.isEmpty())
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);
/*
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
forwarding them to the cell authority
*/
if (target && !alreadyInCombatWithTarget)
if (!target.isEmpty() && !alreadyInCombatWithTarget)
{
mwmp::ActorList *actorList = mwmp::Main::get().getNetworking()->getActorList();
actorList->reset();

@ -269,7 +269,7 @@ namespace MWScript
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;
}
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);
cast.playSpellCastingEffects(spell->mId, false);

@ -496,16 +496,6 @@ namespace MWScript
{
// Apply looping particles immediately for constant effects
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))
{
// 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);
if (ptr == MWMechanics::getPlayer())

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

@ -51,6 +51,8 @@
namespace
{
using MWWorld::RotationOrder;
osg::Quat makeActorOsgQuat(const ESM::Position& position)
{
return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1));
@ -78,7 +80,7 @@ namespace
* 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())
return;
@ -86,7 +88,7 @@ namespace
rendering.rotateObject(ptr,
ptr.getClass().isActor()
? makeActorOsgQuat(ptr.getRefData().getPosition())
: (inverseRotationOrder
: (order == RotationOrder::inverse
? makeInversedOrderObjectOsgQuat(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
ptr.getClass().insertObjectRendering(ptr, model, rendering);
setNodeRotation(ptr, rendering, false);
setNodeRotation(ptr, rendering, RotationOrder::direct);
ptr.getClass().insertObject (ptr, model, physics);
@ -190,9 +192,9 @@ namespace
}
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);
}
@ -289,9 +291,9 @@ namespace
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)

@ -51,6 +51,12 @@ namespace MWWorld
class CellStore;
class CellPreloader;
enum class RotationOrder
{
direct,
inverse
};
class Scene
{
public:
@ -137,7 +143,7 @@ namespace MWWorld
void removeObjectFromScene (const Ptr& ptr);
///< 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);
bool isCellActive(const CellStore &cell);

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

@ -1550,13 +1550,13 @@ namespace MWWorld
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);
ESM::Position pos = ptr.getRefData().getPosition();
float *objRot = pos.rot;
if(adjust)
if (flags & MWBase::RotationFlag_adjust)
{
objRot[0] += rot.x();
objRot[1] += rot.y();
@ -1588,7 +1588,9 @@ namespace MWWorld
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))
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)
@ -1868,7 +1870,7 @@ namespace MWWorld
float diff = duration * osg::DegreesToRadians(90.f);
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;
@ -1912,11 +1914,9 @@ namespace MWWorld
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;
}
@ -2771,7 +2771,7 @@ namespace MWWorld
player.getClass().getInventoryStore(player).setContListener(anim);
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()->watchActor(getPlayerPtr());

@ -126,7 +126,7 @@ namespace MWWorld
void updateWeather(float duration, bool paused = false);
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);
///< @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
/// could be considered a bug, but is needed for MW compatibility.
/// \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;
///< Place an object. Makes a copy of the Ptr.

@ -361,9 +361,9 @@ namespace
);
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
0.707107067108154296875, 0, -3.535533905029296875,
-0.70710659027099609375, 0, -3.535533905029296875,
2.384185791015625e-07, 0, -4.24264049530029296875,
1.41421353816986083984375, 0, 1.1920928955078125e-07,
-1.41421353816986083984375, 0, -1.1920928955078125e-07,
1.1920928955078125e-07, 0, -1.41421353816986083984375,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
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* 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> cerrsb;
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
#endif
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)
// Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<Debug::DebugOutput> sb;
sb.open(Debug::DebugOutput());
std::cout.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
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName));
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
coutsb.open (Debug::Tee(logfile, oldcout));
cerrsb.open (Debug::Tee(logfile, oldcerr));

@ -135,7 +135,7 @@ namespace DetourNavigator
}
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";

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

@ -76,7 +76,14 @@ ParticleShooter::ParticleShooter()
ParticleShooter::ParticleShooter(const ParticleShooter &copy, const osg::CopyOp &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
@ -112,7 +119,9 @@ GrowFadeAffector::GrowFadeAffector()
GrowFadeAffector::GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop)
: osgParticle::Operator(copy, copyop)
{
*this = copy;
mGrowTime = copy.mGrowTime;
mFadeTime = copy.mFadeTime;
mCachedDefaultSize = copy.mCachedDefaultSize;
}
void GrowFadeAffector::beginOperate(osgParticle::Program *program)
@ -143,7 +152,7 @@ ParticleColorAffector::ParticleColorAffector()
ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy, const osg::CopyOp &copyop)
: osgParticle::Operator(copy, copyop)
{
*this = copy;
mData = copy.mData;
}
void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
@ -172,7 +181,13 @@ GravityAffector::GravityAffector()
GravityAffector::GravityAffector(const GravityAffector &copy, const osg::CopyOp &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)

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

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

@ -176,12 +176,12 @@ recast scale factor
:Type: floating point
:Range: > 0.0
:Default: 0.023529411764705882
:Default: 0.029411764705882353
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
"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.
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"));
ICS_LOG("Applying Bezier filter to channel [number="
+ ToString<int>(ch) + ", startX="
+ ToString<size_t>(ch) + ", startX="
+ ToString<float>(startX) + ", startY="
+ ToString<float>(startY) + ", midX="
+ 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.
# 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:
# sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels)
recast scale factor = 0.023529411764705882
# sStepSizeUp * "recast scale factor" / "cell size" = 5 (max climb height should be equal to 4 voxels)
recast scale factor = 0.029411764705882353
# The z-axis cell size to use for fields. (value > 0.0)
# 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
# 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)
border size = 16

Loading…
Cancel
Save