mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-17 01:10:10 +00:00
Raycasting in Lua
This commit is contained in:
parent
fd251dfe55
commit
0bd1c22e24
@ -58,7 +58,7 @@ add_openmw_dir (mwscript
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp actions object worldview userdataserializer eventqueue query
|
||||
luabindings localscripts objectbindings cellbindings asyncbindings settingsbindings
|
||||
camerabindings uibindings inputbindings
|
||||
camerabindings uibindings inputbindings nearbybindings
|
||||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
|
@ -25,7 +25,7 @@ namespace MWLua
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 5;
|
||||
api["API_REVISION"] = 6;
|
||||
api["quit"] = [lua]()
|
||||
{
|
||||
std::string traceback = lua->sol()["debug"]["traceback"]().get<std::string>();
|
||||
@ -110,36 +110,6 @@ namespace MWLua
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
||||
sol::table initNearbyPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
WorldView* worldView = context.mWorldView;
|
||||
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
||||
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||
api["selectObjects"] = [context](const Queries::Query& query)
|
||||
{
|
||||
ObjectIdList list;
|
||||
WorldView* worldView = context.mWorldView;
|
||||
if (query.mQueryType == "activators")
|
||||
list = worldView->getActivatorsInScene();
|
||||
else if (query.mQueryType == "actors")
|
||||
list = worldView->getActorsInScene();
|
||||
else if (query.mQueryType == "containers")
|
||||
list = worldView->getContainersInScene();
|
||||
else if (query.mQueryType == "doors")
|
||||
list = worldView->getDoorsInScene();
|
||||
else if (query.mQueryType == "items")
|
||||
list = worldView->getItemsInScene();
|
||||
return LObjectList{selectObjectsFromList(query, list, context)};
|
||||
// TODO: Maybe use sqlite
|
||||
// return LObjectList{worldView->selectObjects(query, true)};
|
||||
};
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
||||
sol::table initQueryPackage(const Context& context)
|
||||
{
|
||||
Queries::registerQueryBindings(context.mLua->sol());
|
||||
|
@ -21,11 +21,13 @@ namespace MWLua
|
||||
|
||||
sol::table initCorePackage(const Context&);
|
||||
sol::table initWorldPackage(const Context&);
|
||||
sol::table initNearbyPackage(const Context&);
|
||||
sol::table initQueryPackage(const Context&);
|
||||
|
||||
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
|
||||
|
||||
// Implemented in nearbybindings.cpp
|
||||
sol::table initNearbyPackage(const Context&);
|
||||
|
||||
// Implemented in objectbindings.cpp
|
||||
void initObjectBindingsForLocalScripts(const Context&);
|
||||
void initObjectBindingsForGlobalScripts(const Context&);
|
||||
|
108
apps/openmw/mwlua/nearbybindings.cpp
Normal file
108
apps/openmw/mwlua/nearbybindings.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/queries/luabindings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwphysics/raycasting.hpp"
|
||||
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWPhysics::RayCastingResult> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initNearbyPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
WorldView* worldView = context.mWorldView;
|
||||
|
||||
sol::usertype<MWPhysics::RayCastingResult> rayResult =
|
||||
context.mLua->sol().new_usertype<MWPhysics::RayCastingResult>("RayCastingResult");
|
||||
rayResult["hit"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) { return r.mHit; });
|
||||
rayResult["hitPos"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional<osg::Vec3f>
|
||||
{
|
||||
if (r.mHit)
|
||||
return r.mHitPos;
|
||||
else
|
||||
return sol::nullopt;
|
||||
});
|
||||
rayResult["hitNormal"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional<osg::Vec3f>
|
||||
{
|
||||
if (r.mHit)
|
||||
return r.mHitNormal;
|
||||
else
|
||||
return sol::nullopt;
|
||||
});
|
||||
rayResult["hitObject"] = sol::readonly_property([worldView](const MWPhysics::RayCastingResult& r) -> sol::optional<LObject>
|
||||
{
|
||||
if (r.mHitObject.isEmpty())
|
||||
return sol::nullopt;
|
||||
else
|
||||
return LObject(getId(r.mHitObject), worldView->getObjectRegistry());
|
||||
});
|
||||
|
||||
constexpr int defaultCollisionType = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap |
|
||||
MWPhysics::CollisionType_Actor | MWPhysics::CollisionType_Door;
|
||||
api["COLLISION_TYPE"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"World", MWPhysics::CollisionType_World,
|
||||
"Door", MWPhysics::CollisionType_Door,
|
||||
"Actor", MWPhysics::CollisionType_Actor,
|
||||
"HeightMap", MWPhysics::CollisionType_HeightMap,
|
||||
"Projectile", MWPhysics::CollisionType_Projectile,
|
||||
"Water", MWPhysics::CollisionType_Water,
|
||||
"Default", defaultCollisionType));
|
||||
|
||||
api["castRay"] = [defaultCollisionType](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options)
|
||||
{
|
||||
MWWorld::Ptr ignore;
|
||||
int collisionType = defaultCollisionType;
|
||||
float radius = 0;
|
||||
if (options)
|
||||
{
|
||||
sol::optional<LObject> ignoreObj = options->get<sol::optional<LObject>>("ignore");
|
||||
if (ignoreObj) ignore = ignoreObj->ptr();
|
||||
collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType);
|
||||
radius = options->get<sol::optional<float>>("radius").value_or(0);
|
||||
}
|
||||
const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
|
||||
if (radius <= 0)
|
||||
return rayCasting->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
|
||||
else
|
||||
{
|
||||
if (!ignore.isEmpty()) throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
|
||||
return rayCasting->castSphere(from, to, radius, collisionType);
|
||||
}
|
||||
};
|
||||
|
||||
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
||||
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||
api["selectObjects"] = [context](const Queries::Query& query)
|
||||
{
|
||||
ObjectIdList list;
|
||||
WorldView* worldView = context.mWorldView;
|
||||
if (query.mQueryType == "activators")
|
||||
list = worldView->getActivatorsInScene();
|
||||
else if (query.mQueryType == "actors")
|
||||
list = worldView->getActorsInScene();
|
||||
else if (query.mQueryType == "containers")
|
||||
list = worldView->getContainersInScene();
|
||||
else if (query.mQueryType == "doors")
|
||||
list = worldView->getDoorsInScene();
|
||||
else if (query.mQueryType == "items")
|
||||
list = worldView->getItemsInScene();
|
||||
return LObjectList{selectObjectsFromList(query, list, context)};
|
||||
// TODO: Maybe use sqlite
|
||||
// return LObjectList{worldView->selectObjects(query, true)};
|
||||
};
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
@ -32,5 +32,47 @@
|
||||
-- @param openmw.query#Query query
|
||||
-- @return openmw.core#ObjectList
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- @type COLLISION_TYPE
|
||||
-- @field [parent=#COLLISION_TYPE] #number World
|
||||
-- @field [parent=#COLLISION_TYPE] #number Door
|
||||
-- @field [parent=#COLLISION_TYPE] #number Actor
|
||||
-- @field [parent=#COLLISION_TYPE] #number HeightMap
|
||||
-- @field [parent=#COLLISION_TYPE] #number Projectile
|
||||
-- @field [parent=#COLLISION_TYPE] #number Water
|
||||
-- @field [parent=#COLLISION_TYPE] #number Default Used by deafult: World+Door+Actor+HeightMap
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Collision types that are used in `castRay`.
|
||||
-- Several types can be combined with '+'.
|
||||
-- @field [parent=#nearby] #COLLISION_TYPE COLLISION_TYPE
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Result of raycasing
|
||||
-- @type RayCastingResult
|
||||
-- @field [parent=#RayCastingResult] #boolean hit Is there a collision? (true/false)
|
||||
-- @field [parent=#RayCastingResult] openmw.util#Vector3 hitPos Position of the collision point (nil if no collision)
|
||||
-- @field [parent=#RayCastingResult] openmw.util#Vector3 hitNormal Normal to the surface in the collision point (nil if no collision)
|
||||
-- @field [parent=#RayCastingResult] openmw.core#GameObject hitObject The object the ray has collided with (can be nil)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Cast ray from one point to another and return the first collision.
|
||||
-- @function [parent=#nearby] castRay
|
||||
-- @param openmw.util#Vector3 from Start point of the ray.
|
||||
-- @param openmw.util#Vector3 to End point of the ray.
|
||||
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||
-- `ignore` - an object to ignore (specify here the source of the ray);
|
||||
-- `collisionType` - object types to work with (see @{openmw.nearby#COLLISION_TYPE}), several types can be combined with '+';
|
||||
-- `radius` - the radius of the ray (zero by default). If not zero then castRay actually casts a sphere with given radius.
|
||||
-- NOTE: currently `ignore` is not supported if `radius>0`.
|
||||
-- @return #RayCastingResult
|
||||
-- @usage if nearby.castRay(pointA, pointB).hit then print('obstacle between A and B') end
|
||||
-- @usage local res = nearby.castRay(self.position, enemy.position, {ignore=self})
|
||||
-- if res.hitObject and res.hitObject ~= enemy then obstacle = res.hitObject end
|
||||
-- @usage local res = nearby.castRay(self.position, targetPos, {
|
||||
-- collisionType=nearby.COLLISION_TYPE.HeightMap + nearby.COLLISION_TYPE.Water,
|
||||
-- radius = 10,
|
||||
-- })
|
||||
|
||||
return nil
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user