1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 09:09:42 +00:00

add ignore list to raycasts

This commit is contained in:
Cody Glassman 2024-02-12 07:52:47 -08:00
parent c889026b71
commit 56b31ceaf5
16 changed files with 237 additions and 79 deletions

View file

@ -72,7 +72,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 53) set(OPENMW_LUA_API_REVISION 54)
set(OPENMW_POSTPROCESSING_API_REVISION 1) set(OPENMW_POSTPROCESSING_API_REVISION 1)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")

View file

@ -304,7 +304,7 @@ namespace MWBase
virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0; virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;
virtual bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to, virtual bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
bool ignorePlayer, bool ignoreActors) bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList = {})
= 0; = 0;
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0; virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;

View file

@ -16,6 +16,31 @@
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "objectlists.hpp" #include "objectlists.hpp"
namespace
{
template <class T = MWWorld::Ptr>
std::vector<T> parseIgnoreList(const sol::table& options)
{
std::vector<T> ignore;
if (const auto& ignoreObj = options.get<sol::optional<MWLua::LObject>>("ignore"))
{
ignore.push_back(ignoreObj->ptr());
}
else if (const auto& ignoreTable = options.get<sol::optional<sol::table>>("ignore"))
{
ignoreTable->for_each([&](const auto& _, const sol::object& value) {
if (value.is<MWLua::LObject>())
{
ignore.push_back(value.as<MWLua::LObject>().ptr());
}
});
}
return ignore;
}
}
namespace sol namespace sol
{ {
template <> template <>
@ -71,24 +96,27 @@ namespace MWLua
})); }));
api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options) { api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options) {
MWWorld::Ptr ignore; std::vector<MWWorld::ConstPtr> ignore;
int collisionType = MWPhysics::CollisionType_Default; int collisionType = MWPhysics::CollisionType_Default;
float radius = 0; float radius = 0;
if (options) if (options)
{ {
sol::optional<LObject> ignoreObj = options->get<sol::optional<LObject>>("ignore"); ignore = parseIgnoreList<MWWorld::ConstPtr>(*options);
if (ignoreObj)
ignore = ignoreObj->ptr();
collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType); collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType);
radius = options->get<sol::optional<float>>("radius").value_or(0); radius = options->get<sol::optional<float>>("radius").value_or(0);
} }
const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting(); const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
if (radius <= 0) if (radius <= 0)
return rayCasting->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType); {
return rayCasting->castRay(from, to, ignore, {}, collisionType);
}
else else
{ {
if (!ignore.isEmpty()) for (const auto& ptr : ignore)
throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0"); {
if (!ptr.isEmpty())
throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
}
return rayCasting->castSphere(from, to, radius, collisionType); return rayCasting->castSphere(from, to, radius, collisionType);
} }
}; };
@ -108,22 +136,37 @@ namespace MWLua
// and use this callback from the main thread at the beginning of the next frame processing. // and use this callback from the main thread at the beginning of the next frame processing.
rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType); rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
};*/ };*/
api["castRenderingRay"] = [manager = context.mLuaManager](const osg::Vec3f& from, const osg::Vec3f& to) { api["castRenderingRay"] = [manager = context.mLuaManager](const osg::Vec3f& from, const osg::Vec3f& to,
const sol::optional<sol::table>& options) {
if (!manager->isProcessingInputEvents()) if (!manager->isProcessingInputEvents())
{ {
throw std::logic_error( throw std::logic_error(
"castRenderingRay can be used only in player scripts during processing of input events; " "castRenderingRay can be used only in player scripts during processing of input events; "
"use asyncCastRenderingRay instead."); "use asyncCastRenderingRay instead.");
} }
std::vector<MWWorld::Ptr> ignore;
if (options.has_value())
{
ignore = parseIgnoreList(*options);
}
MWPhysics::RayCastingResult res; MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
return res; return res;
}; };
api["asyncCastRenderingRay"] = [context]( api["asyncCastRenderingRay"] = [context](const sol::table& callback, const osg::Vec3f& from,
const sol::table& callback, const osg::Vec3f& from, const osg::Vec3f& to) { const osg::Vec3f& to, const sol::optional<sol::table>& options) {
context.mLuaManager->addAction([context, callback = LuaUtil::Callback::fromLua(callback), from, to] { std::vector<MWWorld::Ptr> ignore;
if (options.has_value())
{
ignore = parseIgnoreList(*options);
}
context.mLuaManager->addAction([context, ignore, callback = LuaUtil::Callback::fromLua(callback), from,
to] {
MWPhysics::RayCastingResult res; MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res)); context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res));
}); });
}; };

View file

@ -86,7 +86,7 @@ namespace MWMechanics
return MWBase::Environment::get() return MWBase::Environment::get()
.getWorld() .getWorld()
->getRayCasting() ->getRayCasting()
->castRay(position, visibleDestination, actor, {}, mask) ->castRay(position, visibleDestination, { actor }, {}, mask)
.mHit; .mHit;
} }

View file

@ -12,7 +12,7 @@ namespace MWPhysics
btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
{ {
const auto* hitObject = rayResult.m_collisionObject; const auto* hitObject = rayResult.m_collisionObject;
if (hitObject == mMe) if (std::find(mIgnoreList.begin(), mIgnoreList.end(), hitObject) != mIgnoreList.end())
return 1.f; return 1.f;
if (hitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor && !mTargets.empty()) if (hitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor && !mTargets.empty())

View file

@ -14,10 +14,10 @@ namespace MWPhysics
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{ {
public: public:
explicit ClosestNotMeRayResultCallback(const btCollisionObject* me, std::span<const btCollisionObject*> targets, explicit ClosestNotMeRayResultCallback(std::span<const btCollisionObject*> ignore,
const btVector3& from, const btVector3& to) std::span<const btCollisionObject*> targets, const btVector3& from, const btVector3& to)
: btCollisionWorld::ClosestRayResultCallback(from, to) : btCollisionWorld::ClosestRayResultCallback(from, to)
, mMe(me) , mIgnoreList(ignore)
, mTargets(targets) , mTargets(targets)
{ {
} }
@ -25,7 +25,7 @@ namespace MWPhysics
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override; btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
private: private:
const btCollisionObject* mMe; const std::span<const btCollisionObject*> mIgnoreList;
const std::span<const btCollisionObject*> mTargets; const std::span<const btCollisionObject*> mTargets;
}; };
} }

View file

@ -192,7 +192,8 @@ namespace MWPhysics
} }
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f& from, const osg::Vec3f& to, RayCastingResult PhysicsSystem::castRay(const osg::Vec3f& from, const osg::Vec3f& to,
const MWWorld::ConstPtr& ignore, const std::vector<MWWorld::Ptr>& targets, int mask, int group) const const std::vector<MWWorld::ConstPtr>& ignore, const std::vector<MWWorld::Ptr>& targets, int mask,
int group) const
{ {
if (from == to) if (from == to)
{ {
@ -203,19 +204,22 @@ namespace MWPhysics
btVector3 btFrom = Misc::Convert::toBullet(from); btVector3 btFrom = Misc::Convert::toBullet(from);
btVector3 btTo = Misc::Convert::toBullet(to); btVector3 btTo = Misc::Convert::toBullet(to);
const btCollisionObject* me = nullptr; std::vector<const btCollisionObject*> ignoreList;
std::vector<const btCollisionObject*> targetCollisionObjects; std::vector<const btCollisionObject*> targetCollisionObjects;
if (!ignore.isEmpty()) for (const auto& ptr : ignore)
{ {
const Actor* actor = getActor(ignore); if (!ptr.isEmpty())
if (actor)
me = actor->getCollisionObject();
else
{ {
const Object* object = getObject(ignore); const Actor* actor = getActor(ptr);
if (object) if (actor)
me = object->getCollisionObject(); ignoreList.push_back(actor->getCollisionObject());
else
{
const Object* object = getObject(ptr);
if (object)
ignoreList.push_back(object->getCollisionObject());
}
} }
} }
@ -229,7 +233,7 @@ namespace MWPhysics
} }
} }
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo); ClosestNotMeRayResultCallback resultCallback(ignoreList, targetCollisionObjects, btFrom, btTo);
resultCallback.m_collisionFilterGroup = group; resultCallback.m_collisionFilterGroup = group;
resultCallback.m_collisionFilterMask = mask; resultCallback.m_collisionFilterMask = mask;

View file

@ -209,12 +209,11 @@ namespace MWPhysics
const MWWorld::ConstPtr& ptr, int collisionGroup, int collisionMask) const; const MWWorld::ConstPtr& ptr, int collisionGroup, int collisionMask) const;
osg::Vec3f traceDown(const MWWorld::Ptr& ptr, const osg::Vec3f& position, float maxHeight); osg::Vec3f traceDown(const MWWorld::Ptr& ptr, const osg::Vec3f& position, float maxHeight);
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all /// @param ignore Optional, a list of Ptr to ignore in the list of results. targets are actors to filter for,
/// other actors. /// ignoring all other actors.
RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to, RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to,
const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), const std::vector<MWWorld::ConstPtr>& ignore = {}, const std::vector<MWWorld::Ptr>& targets = {},
const std::vector<MWWorld::Ptr>& targets = std::vector<MWWorld::Ptr>(), int mask = CollisionType_Default, int mask = CollisionType_Default, int group = 0xff) const override;
int group = 0xff) const override;
using RayCastingInterface::castRay; using RayCastingInterface::castRay;
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius, RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius,

View file

@ -23,16 +23,15 @@ namespace MWPhysics
public: public:
virtual ~RayCastingInterface() = default; virtual ~RayCastingInterface() = default;
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all /// @param ignore Optional, a list of Ptr to ignore in the list of results. targets are actors to filter for,
/// other actors. /// ignoring all other actors.
virtual RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to, virtual RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to,
const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), const std::vector<MWWorld::ConstPtr>& ignore = {}, const std::vector<MWWorld::Ptr>& targets = {},
const std::vector<MWWorld::Ptr>& targets = std::vector<MWWorld::Ptr>(), int mask = CollisionType_Default, int mask = CollisionType_Default, int group = 0xff) const = 0;
int group = 0xff) const = 0;
RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask) const RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask) const
{ {
return castRay(from, to, MWWorld::ConstPtr(), std::vector<MWWorld::Ptr>(), mask); return castRay(from, to, {}, {}, mask);
} }
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius, virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius,

View file

@ -59,6 +59,7 @@
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/groundcoverstore.hpp" #include "../mwworld/groundcoverstore.hpp"
#include "../mwworld/scene.hpp"
#include "../mwgui/postprocessorhud.hpp" #include "../mwgui/postprocessorhud.hpp"
@ -1014,20 +1015,17 @@ namespace MWRender
return osg::Vec4f(min_x, min_y, max_x, max_y); return osg::Vec4f(min_x, min_y, max_x, max_y);
} }
RenderingManager::RayResult getIntersectionResult(osgUtil::LineSegmentIntersector* intersector) RenderingManager::RayResult getIntersectionResult(osgUtil::LineSegmentIntersector* intersector,
const osg::ref_ptr<osgUtil::IntersectionVisitor>& visitor, std::span<const MWWorld::Ptr> ignoreList = {})
{ {
RenderingManager::RayResult result; RenderingManager::RayResult result;
result.mHit = false; result.mHit = false;
result.mRatio = 0; result.mRatio = 0;
if (intersector->containsIntersections())
{
result.mHit = true;
osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();
result.mHitPointWorld = intersection.getWorldIntersectPoint(); if (!intersector->containsIntersections())
result.mHitNormalWorld = intersection.getWorldIntersectNormal(); return result;
result.mRatio = intersection.ratio;
auto test = [&](const osgUtil::LineSegmentIntersector::Intersection& intersection) {
PtrHolder* ptrHolder = nullptr; PtrHolder* ptrHolder = nullptr;
std::vector<RefnumMarker*> refnumMarkers; std::vector<RefnumMarker*> refnumMarkers;
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end();
@ -1039,9 +1037,16 @@ namespace MWRender
for (unsigned int i = 0; i < userDataContainer->getNumUserObjects(); ++i) for (unsigned int i = 0; i < userDataContainer->getNumUserObjects(); ++i)
{ {
if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i))) if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
ptrHolder = p; {
if (std::find(ignoreList.begin(), ignoreList.end(), p->mPtr) == ignoreList.end())
{
ptrHolder = p;
}
}
if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i))) if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))
{
refnumMarkers.push_back(r); refnumMarkers.push_back(r);
}
} }
} }
@ -1056,21 +1061,113 @@ namespace MWRender
|| (intersectionIndex >= vertexCounter || (intersectionIndex >= vertexCounter
&& intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices)) && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices))
{ {
result.mHitRefnum = refnumMarkers[i]->mRefnum; auto it = std::find_if(
ignoreList.begin(), ignoreList.end(), [target = refnumMarkers[i]->mRefnum](const auto& ptr) {
return target == ptr.getCellRef().getRefNum();
});
if (it == ignoreList.end())
{
result.mHitRefnum = refnumMarkers[i]->mRefnum;
}
break; break;
} }
vertexCounter += refnumMarkers[i]->mNumVertices; vertexCounter += refnumMarkers[i]->mNumVertices;
} }
if (!result.mHitObject.isEmpty() || result.mHitRefnum.isSet())
{
result.mHit = true;
result.mHitPointWorld = intersection.getWorldIntersectPoint();
result.mHitNormalWorld = intersection.getWorldIntersectNormal();
result.mRatio = intersection.ratio;
}
};
if (ignoreList.empty() || intersector->getIntersectionLimit() != osgUtil::LineSegmentIntersector::NO_LIMIT)
{
test(intersector->getFirstIntersection());
}
else
{
for (const auto& intersection : intersector->getIntersections())
{
test(intersection);
if (result.mHit)
{
break;
}
}
} }
return result; return result;
} }
class IntersectionVisitorWithIgnoreList : public osgUtil::IntersectionVisitor
{
public:
bool skipTransform(osg::Transform& transform)
{
if (mContainsPagedRefs)
return false;
osg::UserDataContainer* userDataContainer = transform.getUserDataContainer();
if (!userDataContainer)
return false;
for (unsigned int i = 0; i < userDataContainer->getNumUserObjects(); ++i)
{
if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
{
if (std::find(mIgnoreList.begin(), mIgnoreList.end(), p->mPtr) != mIgnoreList.end())
{
return true;
}
}
}
return false;
}
void apply(osg::Transform& transform) override
{
if (skipTransform(transform))
{
return;
}
osgUtil::IntersectionVisitor::apply(transform);
}
void setIgnoreList(std::span<const MWWorld::Ptr> ignoreList) { mIgnoreList = ignoreList; }
void setContainsPagedRefs(bool contains) { mContainsPagedRefs = contains; }
private:
std::span<const MWWorld::Ptr> mIgnoreList;
bool mContainsPagedRefs = false;
};
osg::ref_ptr<osgUtil::IntersectionVisitor> RenderingManager::getIntersectionVisitor( osg::ref_ptr<osgUtil::IntersectionVisitor> RenderingManager::getIntersectionVisitor(
osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors) osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors,
std::span<const MWWorld::Ptr> ignoreList)
{ {
if (!mIntersectionVisitor) if (!mIntersectionVisitor)
mIntersectionVisitor = new osgUtil::IntersectionVisitor; mIntersectionVisitor = new IntersectionVisitorWithIgnoreList;
mIntersectionVisitor->setIgnoreList(ignoreList);
mIntersectionVisitor->setContainsPagedRefs(false);
MWWorld::Scene* worldScene = MWBase::Environment::get().getWorldScene();
for (const auto& ptr : ignoreList)
{
if (worldScene->isPagedRef(ptr))
{
mIntersectionVisitor->setContainsPagedRefs(true);
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
break;
}
}
mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());
mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp()); mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
@ -1088,16 +1185,16 @@ namespace MWRender
return mIntersectionVisitor; return mIntersectionVisitor;
} }
RenderingManager::RayResult RenderingManager::castRay( RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest,
const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors) bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList)
{ {
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector( osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL, origin, dest)); new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL, origin, dest));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST);
mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors, ignoreList));
return getIntersectionResult(intersector); return getIntersectionResult(intersector, mIntersectionVisitor, ignoreList);
} }
RenderingManager::RayResult RenderingManager::castCameraToViewportRay( RenderingManager::RayResult RenderingManager::castCameraToViewportRay(
@ -1117,7 +1214,7 @@ namespace MWRender
mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors));
return getIntersectionResult(intersector); return getIntersectionResult(intersector, mIntersectionVisitor);
} }
void RenderingManager::updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated) void RenderingManager::updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated)

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_MWRENDER_RENDERINGMANAGER_H #ifndef OPENMW_MWRENDER_RENDERINGMANAGER_H
#define OPENMW_MWRENDER_RENDERINGMANAGER_H #define OPENMW_MWRENDER_RENDERINGMANAGER_H
#include <span>
#include <osg/Camera> #include <osg/Camera>
#include <osg/Light> #include <osg/Light>
#include <osg/ref_ptr> #include <osg/ref_ptr>
@ -87,6 +89,7 @@ namespace MWRender
class StateUpdater; class StateUpdater;
class SharedUniformStateUpdater; class SharedUniformStateUpdater;
class PerViewUniformStateUpdater; class PerViewUniformStateUpdater;
class IntersectionVisitorWithIgnoreList;
class EffectManager; class EffectManager;
class ScreenshotManager; class ScreenshotManager;
@ -177,8 +180,8 @@ namespace MWRender
float mRatio; float mRatio;
}; };
RayResult castRay( RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer,
const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors = false); bool ignoreActors = false, std::span<const MWWorld::Ptr> ignoreList = {});
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen /// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen
/// coordinates, where (0,0) is the top left corner. /// coordinates, where (0,0) is the top left corner.
@ -299,10 +302,10 @@ namespace MWRender
const bool mSkyBlending; const bool mSkyBlending;
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor( osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector,
osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors); bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList = {});
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor; osg::ref_ptr<IntersectionVisitorWithIgnoreList> mIntersectionVisitor;
osg::ref_ptr<osgViewer::Viewer> mViewer; osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Group> mRootNode; osg::ref_ptr<osg::Group> mRootNode;

View file

@ -99,6 +99,10 @@ namespace
return ptr.getClass().getCorrectedModel(ptr); return ptr.getClass().getCorrectedModel(ptr);
} }
// Null node meant to distinguish objects that aren't in the scene from paged objects
// TODO: find a more clever way to make paging exclusion more reliable?
static osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pagedNode = new SceneUtil::PositionAttitudeTransform;
void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const std::vector<ESM::RefNum>& pagedRefs, void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const std::vector<ESM::RefNum>& pagedRefs,
MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering)
{ {
@ -111,11 +115,6 @@ namespace
std::string model = getModel(ptr); std::string model = getModel(ptr);
const auto rotation = makeDirectNodeRotation(ptr); const auto rotation = makeDirectNodeRotation(ptr);
// Null node meant to distinguish objects that aren't in the scene from paged objects
// TODO: find a more clever way to make paging exclusion more reliable?
static const osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pagedNode(
new SceneUtil::PositionAttitudeTransform);
ESM::RefNum refnum = ptr.getCellRef().getRefNum(); ESM::RefNum refnum = ptr.getCellRef().getRefNum();
if (!refnum.hasContentFile() || !std::binary_search(pagedRefs.begin(), pagedRefs.end(), refnum)) if (!refnum.hasContentFile() || !std::binary_search(pagedRefs.begin(), pagedRefs.end(), refnum))
ptr.getClass().insertObjectRendering(ptr, model, rendering); ptr.getClass().insertObjectRendering(ptr, model, rendering);
@ -164,13 +163,13 @@ namespace
Misc::Convert::makeBulletQuaternion(ptr.getCellRef().getPosition()), transform.getOrigin()); Misc::Convert::makeBulletQuaternion(ptr.getCellRef().getPosition()), transform.getOrigin());
const auto start = Misc::Convert::toOsg(closedDoorTransform(center + toPoint)); const auto start = Misc::Convert::toOsg(closedDoorTransform(center + toPoint));
const auto startPoint = physics.castRay(start, start - osg::Vec3f(0, 0, 1000), ptr, {}, const auto startPoint = physics.castRay(start, start - osg::Vec3f(0, 0, 1000), { ptr }, {},
MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap
| MWPhysics::CollisionType_Water); | MWPhysics::CollisionType_Water);
const auto connectionStart = startPoint.mHit ? startPoint.mHitPos : start; const auto connectionStart = startPoint.mHit ? startPoint.mHitPos : start;
const auto end = Misc::Convert::toOsg(closedDoorTransform(center - toPoint)); const auto end = Misc::Convert::toOsg(closedDoorTransform(center - toPoint));
const auto endPoint = physics.castRay(end, end - osg::Vec3f(0, 0, 1000), ptr, {}, const auto endPoint = physics.castRay(end, end - osg::Vec3f(0, 0, 1000), { ptr }, {},
MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap
| MWPhysics::CollisionType_Water); | MWPhysics::CollisionType_Water);
const auto connectionEnd = endPoint.mHit ? endPoint.mHitPos : end; const auto connectionEnd = endPoint.mHit ? endPoint.mHitPos : end;
@ -274,7 +273,6 @@ namespace
namespace MWWorld namespace MWWorld
{ {
void Scene::removeFromPagedRefs(const Ptr& ptr) void Scene::removeFromPagedRefs(const Ptr& ptr)
{ {
ESM::RefNum refnum = ptr.getCellRef().getRefNum(); ESM::RefNum refnum = ptr.getCellRef().getRefNum();
@ -288,6 +286,11 @@ namespace MWWorld
} }
} }
bool Scene::isPagedRef(const Ptr& ptr) const
{
return ptr.getRefData().getBaseNode() == pagedNode.get();
}
void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order) void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order)
{ {
const auto rot = makeNodeRotation(ptr, order); const auto rot = makeNodeRotation(ptr, order);

View file

@ -190,6 +190,8 @@ namespace MWWorld
void removeFromPagedRefs(const Ptr& ptr); void removeFromPagedRefs(const Ptr& ptr);
bool isPagedRef(const Ptr& ptr) const;
void updateObjectRotation(const Ptr& ptr, RotationOrder order); void updateObjectRotation(const Ptr& ptr, RotationOrder order);
void updateObjectScale(const Ptr& ptr); void updateObjectScale(const Ptr& ptr);

View file

@ -1817,9 +1817,10 @@ namespace MWWorld
} }
bool World::castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to, bool World::castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
bool ignorePlayer, bool ignoreActors) bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList)
{ {
MWRender::RenderingManager::RayResult rayRes = mRendering->castRay(from, to, ignorePlayer, ignoreActors); MWRender::RenderingManager::RayResult rayRes
= mRendering->castRay(from, to, ignorePlayer, ignoreActors, ignoreList);
res.mHit = rayRes.mHit; res.mHit = rayRes.mHit;
res.mHitPos = rayRes.mHitPointWorld; res.mHitPos = rayRes.mHitPointWorld;
res.mHitNormal = rayRes.mHitNormalWorld; res.mHitNormal = rayRes.mHitNormalWorld;
@ -2598,7 +2599,7 @@ namespace MWWorld
collisionTypes |= MWPhysics::CollisionType_Water; collisionTypes |= MWPhysics::CollisionType_Water;
} }
MWPhysics::RayCastingResult result MWPhysics::RayCastingResult result
= mPhysics->castRay(from, to, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), collisionTypes); = mPhysics->castRay(from, to, { MWWorld::Ptr() }, std::vector<MWWorld::Ptr>(), collisionTypes);
if (!result.mHit) if (!result.mHit)
return maxDist; return maxDist;
@ -3064,8 +3065,8 @@ namespace MWWorld
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
// Check for impact, if yes, handle hit, if not, launch projectile // Check for impact, if yes, handle hit, if not, launch projectile
MWPhysics::RayCastingResult result MWPhysics::RayCastingResult result = mPhysics->castRay(
= mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile); sourcePos, worldPos, { actor }, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
if (result.mHit) if (result.mHit)
MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength); MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength);
else else

View file

@ -392,7 +392,7 @@ namespace MWWorld
const MWPhysics::RayCastingInterface* getRayCasting() const override; const MWPhysics::RayCastingInterface* getRayCasting() const override;
bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to, bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
bool ignorePlayer, bool ignoreActors) override; bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList) override;
void setActorCollisionMode(const Ptr& ptr, bool internal, bool external) override; void setActorCollisionMode(const Ptr& ptr, bool internal, bool external) override;
bool isActorCollisionEnabled(const Ptr& ptr) override; bool isActorCollisionEnabled(const Ptr& ptr) override;

View file

@ -89,6 +89,11 @@
-- radius = 10, -- radius = 10,
-- }) -- })
---
-- A table of parameters for @{#nearby.castRenderingRay} and @{#nearby.asyncCastRenderingRay}
-- @type CastRenderingRayOptions
-- @field #table ignore A list of @{openmw.core#GameObject} to ignore while doing the ray cast
--- ---
-- Cast ray from one point to another and find the first visual intersection with anything in the scene. -- Cast ray from one point to another and find the first visual intersection with anything in the scene.
-- As opposite to `castRay` can find an intersection with an object without collisions. -- As opposite to `castRay` can find an intersection with an object without collisions.
@ -97,6 +102,7 @@
-- @function [parent=#nearby] castRenderingRay -- @function [parent=#nearby] castRenderingRay
-- @param openmw.util#Vector3 from Start point of the ray. -- @param openmw.util#Vector3 from Start point of the ray.
-- @param openmw.util#Vector3 to End point of the ray. -- @param openmw.util#Vector3 to End point of the ray.
-- @param #CastRenderingRayOptions
-- @return #RayCastingResult -- @return #RayCastingResult
--- ---
@ -105,6 +111,7 @@
-- @param openmw.async#Callback callback The callback to pass the result to (should accept a single argument @{openmw.nearby#RayCastingResult}). -- @param openmw.async#Callback callback The callback to pass the result to (should accept a single argument @{openmw.nearby#RayCastingResult}).
-- @param openmw.util#Vector3 from Start point of the ray. -- @param openmw.util#Vector3 from Start point of the ray.
-- @param openmw.util#Vector3 to End point of the ray. -- @param openmw.util#Vector3 to End point of the ray.
-- @param #CastRenderingRayOptions
--- ---
-- @type NAVIGATOR_FLAGS -- @type NAVIGATOR_FLAGS