mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 14:59:54 +00:00
Merge branch 'ncc' into 'master'
#6496 Handle NCC flag in Nif files See merge request OpenMW/openmw!1516
This commit is contained in:
commit
c14796fc57
16 changed files with 114 additions and 38 deletions
|
@ -143,6 +143,7 @@
|
|||
Feature #6380: Commas are treated as whitespace in vanilla
|
||||
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
|
||||
Feature #6443: Support NiStencilProperty
|
||||
Feature #6496: NCC flag isn't handled properly
|
||||
Feature #6534: Shader-based object texture blending
|
||||
Feature #6541: Gloss-mapping
|
||||
Feature #6592: Missing support for NiTriShape particle emitters
|
||||
|
|
|
@ -54,7 +54,10 @@ namespace MWLua
|
|||
{"HeightMap", MWPhysics::CollisionType_HeightMap},
|
||||
{"Projectile", MWPhysics::CollisionType_Projectile},
|
||||
{"Water", MWPhysics::CollisionType_Water},
|
||||
{"Default", MWPhysics::CollisionType_Default}
|
||||
{"Default", MWPhysics::CollisionType_Default},
|
||||
{"AnyPhysical", MWPhysics::CollisionType_AnyPhysical},
|
||||
{"Camera", MWPhysics::CollisionType_CameraOnly},
|
||||
{"VisualOnly", MWPhysics::CollisionType_VisualOnly},
|
||||
}));
|
||||
|
||||
api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options)
|
||||
|
|
|
@ -11,7 +11,10 @@ enum CollisionType {
|
|||
CollisionType_HeightMap = 1<<3,
|
||||
CollisionType_Projectile = 1<<4,
|
||||
CollisionType_Water = 1<<5,
|
||||
CollisionType_Default = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door
|
||||
CollisionType_Default = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door,
|
||||
CollisionType_AnyPhysical = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door|CollisionType_Projectile|CollisionType_Water,
|
||||
CollisionType_CameraOnly = 1<<6,
|
||||
CollisionType_VisualOnly = 1<<7
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace MWPhysics
|
|||
btVector3 to = from - btVector3(0,0,maxHeight);
|
||||
|
||||
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
|
||||
resultCallback1.m_collisionFilterGroup = 0xff;
|
||||
resultCallback1.m_collisionFilterGroup = CollisionType_AnyPhysical;
|
||||
resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap;
|
||||
|
||||
collisionWorld->rayTest(from, to, resultCallback1);
|
||||
|
@ -426,7 +426,7 @@ namespace MWPhysics
|
|||
return;
|
||||
|
||||
ProjectileConvexCallback resultCallback(projectile.mCaster, projectile.mCollisionObject, btFrom, btTo, projectile.mProjectile);
|
||||
resultCallback.m_collisionFilterMask = 0xff;
|
||||
resultCallback.m_collisionFilterMask = CollisionType_AnyPhysical;
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||
|
||||
const btQuaternion btrot = btQuaternion::getIdentity();
|
||||
|
|
|
@ -661,7 +661,7 @@ namespace MWPhysics
|
|||
btVector3 pos2 = Misc::Convert::toBullet(actor2->getCollisionObjectPosition() + osg::Vec3f(0,0,actor2->getHalfExtents().z() * 0.9));
|
||||
|
||||
btCollisionWorld::ClosestRayResultCallback resultCallback(pos1, pos2);
|
||||
resultCallback.m_collisionFilterGroup = 0xFF;
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_AnyPhysical;
|
||||
resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;
|
||||
|
||||
MaybeLock lockColWorld(mCollisionWorldMutex, mNumThreads);
|
||||
|
|
|
@ -490,6 +490,17 @@ namespace MWPhysics
|
|||
|
||||
assert(!getObject(ptr));
|
||||
|
||||
// Override collision type based on shape content.
|
||||
switch (shapeInstance->mCollisionType)
|
||||
{
|
||||
case Resource::BulletShape::CollisionType::Camera:
|
||||
collisionType = CollisionType_CameraOnly;
|
||||
break;
|
||||
case Resource::BulletShape::CollisionType::None:
|
||||
collisionType = CollisionType_VisualOnly;
|
||||
break;
|
||||
}
|
||||
|
||||
auto obj = std::make_shared<Object>(ptr, shapeInstance, rotation, collisionType, mTaskScheduler.get());
|
||||
mObjects.emplace(ptr.mRef, obj);
|
||||
|
||||
|
@ -905,7 +916,7 @@ namespace MWPhysics
|
|||
const auto aabbMin = bulletPosition - btVector3(radius, radius, radius);
|
||||
const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);
|
||||
const int mask = MWPhysics::CollisionType_Actor;
|
||||
const int group = 0xff;
|
||||
const int group = MWPhysics::CollisionType_AnyPhysical;
|
||||
if (occupyingActors == nullptr)
|
||||
{
|
||||
HasSphereCollisionCallback callback(bulletPosition, radius, mask, group, ignoreFilter,
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace MWRender
|
|||
|
||||
Camera::Camera (osg::Camera* camera)
|
||||
: mHeightScale(1.f),
|
||||
mCollisionType(MWPhysics::CollisionType::CollisionType_Default & ~MWPhysics::CollisionType::CollisionType_Actor),
|
||||
mCollisionType((MWPhysics::CollisionType::CollisionType_Default & ~MWPhysics::CollisionType::CollisionType_Actor) | MWPhysics::CollisionType_CameraOnly),
|
||||
mCamera(camera),
|
||||
mAnimation(nullptr),
|
||||
mFirstPersonView(true),
|
||||
|
|
|
@ -1009,9 +1009,9 @@ namespace
|
|||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
||||
{
|
||||
mNiStringExtraData.string = "NC___";
|
||||
mNiStringExtraData.string = "NCC__";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
|
@ -1022,12 +1022,16 @@ namespace
|
|||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
expected.mCollisionType = Resource::BulletShape::CollisionType::Camera;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
|
||||
{
|
||||
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.string = "NC___";
|
||||
|
@ -1041,7 +1045,57 @@ namespace
|
|||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
expected.mCollisionType = Resource::BulletShape::CollisionType::Camera;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
||||
{
|
||||
mNiStringExtraData.string = "NC___";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
expected.mCollisionType = Resource::BulletShape::CollisionType::None;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
|
||||
{
|
||||
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
|
||||
mNiStringExtraData2.string = "NC___";
|
||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
expected.mCollisionType = Resource::BulletShape::CollisionType::None;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
|
|
@ -55,9 +55,10 @@ struct NiTextKeyExtraData : public Extra
|
|||
|
||||
struct NiStringExtraData : public Extra
|
||||
{
|
||||
/* Two known meanings:
|
||||
/* Known meanings:
|
||||
"MRK" - marker, only visible in the editor, not rendered in-game
|
||||
"NCO" - no collision
|
||||
"NCC" - no collision except with the camera
|
||||
Anything else starting with "NC" - no collision
|
||||
*/
|
||||
std::string string;
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <components/nif/extra.hpp>
|
||||
#include <components/nif/parent.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
@ -215,7 +217,7 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
for (const Nif::Node* node : roots)
|
||||
{
|
||||
bool autogenerated = hasAutoGeneratedCollision(*node);
|
||||
handleNode(filename, *node, nullptr, 0, autogenerated, isAnimated, autogenerated);
|
||||
handleNode(filename, *node, nullptr, 0, autogenerated, isAnimated, autogenerated, false, mShape->mCollisionType);
|
||||
}
|
||||
|
||||
if (mCompoundShape)
|
||||
|
@ -307,7 +309,7 @@ bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node& rootNode)
|
|||
}
|
||||
|
||||
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent,
|
||||
int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid)
|
||||
int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, unsigned int& collisionType)
|
||||
{
|
||||
// TODO: allow on-the fly collision switching via toggling this flag
|
||||
if (node.recType == Nif::RC_NiCollisionSwitch && !(node.flags & Nif::NiNode::Flag_ActiveCollision))
|
||||
|
@ -341,8 +343,13 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
|
|||
|
||||
if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0)
|
||||
{
|
||||
// No collision. Use an internal flag setting to mark this.
|
||||
flags |= 0x800;
|
||||
// NCC flag in vanilla is partly case sensitive: prefix NC is case insensitive but second C needs be uppercase
|
||||
if (sd->string.length() > 2 && sd->string[2] == 'C')
|
||||
// Collide only with camera.
|
||||
collisionType = Resource::BulletShape::CollisionType::Camera;
|
||||
else
|
||||
// No collision.
|
||||
collisionType = Resource::BulletShape::CollisionType::None;
|
||||
}
|
||||
else if (sd->string == "MRK" && autogenerated)
|
||||
{
|
||||
|
@ -362,7 +369,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
|
|||
|| node.recType == Nif::RC_NiTriStrips
|
||||
|| node.recType == Nif::RC_BSLODTriShape))
|
||||
{
|
||||
handleNiTriShape(node, parent, flags, getWorldTransform(node, parent), isAnimated, avoid);
|
||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, getWorldTransform(node, parent), isAnimated, avoid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,22 +384,11 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
|
|||
continue;
|
||||
|
||||
assert(std::find(list[i]->parents.begin(), list[i]->parents.end(), ninode) != list[i]->parents.end());
|
||||
handleNode(fileName, list[i].get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated, avoid);
|
||||
handleNode(fileName, list[i].get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated, avoid, collisionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNiTriShape(const Nif::Node& nifNode, const Nif::Parent* parent, int flags,
|
||||
const osg::Matrixf &transform, bool isAnimated, bool avoid)
|
||||
{
|
||||
// If the object was marked "NCO" earlier, it shouldn't collide with
|
||||
// anything. So don't do anything.
|
||||
if ((flags & 0x800))
|
||||
return;
|
||||
|
||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(nifNode), parent, transform, isAnimated, avoid);
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNiTriShape(const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent,
|
||||
const osg::Matrixf &transform, bool isAnimated, bool avoid)
|
||||
{
|
||||
|
|
|
@ -56,14 +56,11 @@ public:
|
|||
private:
|
||||
bool findBoundingBox(const Nif::Node& node, const std::string& filename);
|
||||
|
||||
void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, int flags,
|
||||
bool isCollisionNode, bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
||||
void handleNode(const std::string& fileName, const Nif::Node& node,const Nif::Parent* parent, int flags,
|
||||
bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, unsigned int& cameraOnlyCollision);
|
||||
|
||||
bool hasAutoGeneratedCollision(const Nif::Node& rootNode);
|
||||
|
||||
void handleNiTriShape(const Nif::Node& nifNode, const Nif::Parent* parent, int flags, const osg::Matrixf& transform,
|
||||
bool isAnimated, bool avoid);
|
||||
|
||||
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, const osg::Matrixf& transform,
|
||||
bool isAnimated, bool avoid);
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<const BulletShape> source)
|
|||
{
|
||||
mCollisionBox = mSource->mCollisionBox;
|
||||
mAnimatedShapes = mSource->mAnimatedShapes;
|
||||
mCollisionType = mSource->mCollisionType;
|
||||
mCollisionShape = duplicateCollisionShape(mSource->mCollisionShape.get());
|
||||
mAvoidCollisionShape = duplicateCollisionShape(mSource->mAvoidCollisionShape.get());
|
||||
}
|
||||
|
|
|
@ -59,6 +59,13 @@ namespace Resource
|
|||
void setLocalScaling(const btVector3& scale);
|
||||
|
||||
bool isAnimated() const { return !mAnimatedShapes.empty(); }
|
||||
|
||||
unsigned int mCollisionType = 0;
|
||||
enum CollisionType
|
||||
{
|
||||
None = 0x1,
|
||||
Camera = 0x2
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ local noHeadBobbing = 0
|
|||
local noZoom = 0
|
||||
|
||||
local function init()
|
||||
camera.setCollisionType(util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor)))
|
||||
camera.setCollisionType(util.bitOr(util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor)), nearby.COLLISION_TYPE.Camera))
|
||||
camera.setFieldOfView(camera.getBaseFieldOfView())
|
||||
camera.allowCharacterDeferredRotation(settings._getBoolFromSettingsCfg('Camera', 'deferred preview rotation'))
|
||||
if camera.getMode() == MODE.FirstPerson then
|
||||
|
|
|
@ -28,10 +28,9 @@ local combatOffset = util.vector2(0, 15)
|
|||
|
||||
local state = defaultShoulder
|
||||
|
||||
local rayOptions = {collisionType = nearby.COLLISION_TYPE.Default - nearby.COLLISION_TYPE.Actor}
|
||||
local function ray(from, angle, limit)
|
||||
local to = from + util.transform.rotateZ(angle) * util.vector3(0, limit, 0)
|
||||
local res = nearby.castRay(from, to, rayOptions)
|
||||
local res = nearby.castRay(from, to, {collisionType = camera.getCollisionType()})
|
||||
if res.hit then
|
||||
return (res.hitPos - from):length()
|
||||
else
|
||||
|
|
|
@ -34,7 +34,10 @@
|
|||
-- @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
|
||||
-- @field [parent=#COLLISION_TYPE] #number Default Used by default: World+Door+Actor+HeightMap
|
||||
-- @field [parent=#COLLISION_TYPE] #number AnyPhysical : World+Door+Actor+HeightMap+Projectile+Water
|
||||
-- @field [parent=#COLLISION_TYPE] #number Camera Objects that should collide only with camera
|
||||
-- @field [parent=#COLLISION_TYPE] #number VisualOnly Objects that were not intended to be part of the physics world
|
||||
|
||||
---
|
||||
-- Collision types that are used in `castRay`.
|
||||
|
|
Loading…
Reference in a new issue