From ccd95419e579b0e707a67274b3efdfa93f95e661 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 1 Jun 2015 01:57:15 +0200 Subject: [PATCH] Restore various raycasting --- apps/openmw/mwmechanics/character.cpp | 8 +- apps/openmw/mwphysics/physicssystem.cpp | 73 ++++++++--- apps/openmw/mwphysics/physicssystem.hpp | 19 ++- apps/openmw/mwrender/animation.cpp | 2 - apps/openmw/mwrender/effectmanager.cpp | 2 - apps/openmw/mwrender/renderingmanager.cpp | 15 +++ apps/openmw/mwrender/renderingmanager.hpp | 3 + apps/openmw/mwworld/weather.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 147 ++++++++++------------ 9 files changed, 158 insertions(+), 112 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 354ca998c..3843df132 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -838,14 +838,12 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) void CharacterController::updateIdleStormState() { bool inStormDirection = false; - /* if (MWBase::Environment::get().getWorld()->isInStorm()) { - Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); - Ogre::Vector3 characterDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); - inStormDirection = stormDirection.angleBetween(characterDirection) > Ogre::Degree(120); + osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + osg::Vec3f characterDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); + inStormDirection = std::acos(stormDirection * characterDirection) > osg::DegreesToRadians(120.f); } - */ if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) { float complete = 0; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index d36aded24..c942fecb1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -728,35 +728,74 @@ namespace MWPhysics return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); } - - bool PhysicsSystem::castRay(const Ogre::Vector3& from, const Ogre::Vector3& to, bool ignoreHeightMap) + class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { - return false; - /* - btVector3 _from, _to; - _from = btVector3(from.x, from.y, from.z); - _to = btVector3(to.x, to.y, to.z); - - std::pair result = mEngine->rayTest(_from, _to,ignoreHeightMap); - return !(result.first == ""); - */ - } + public: + ClosestNotMeRayResultCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to) + : btCollisionWorld::ClosestRayResultCallback(from, to) + , mMe(me) + { + } - std::pair PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to) + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) + { + if (rayResult.m_collisionObject == mMe) + return 1.f; + return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); + } + private: + const btCollisionObject* mMe; + }; + + PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::Ptr ignore, int mask) { btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); - btCollisionWorld::ClosestRayResultCallback resultCallback(btFrom, btTo); + const btCollisionObject* me = NULL; + if (!ignore.isEmpty()) + { + Actor* actor = getActor(ignore); + if (actor) + me = actor->getCollisionObject(); + } + + ClosestNotMeRayResultCallback resultCallback(me, btFrom, btTo); resultCallback.m_collisionFilterGroup = 0xff; - resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap; + resultCallback.m_collisionFilterMask = mask; mCollisionWorld->rayTest(btFrom, btTo, resultCallback); + + RayResult result; + result.mHit = resultCallback.hasHit(); if (resultCallback.hasHit()) { - return std::make_pair(true, toOsg(resultCallback.m_hitPointWorld)); + result.mHitPos = toOsg(resultCallback.m_hitPointWorld); + result.mHitNormal = toOsg(resultCallback.m_hitNormalWorld); + if (PtrHolder* ptrHolder = static_cast(resultCallback.m_collisionObject->getUserPointer())) + result.mHitObject = ptrHolder->getPtr(); } - return std::make_pair(false, osg::Vec3f()); + return result; + } + + bool PhysicsSystem::getLineOfSight(const MWWorld::Ptr &actor1, const MWWorld::Ptr &actor2) + { + Actor* physactor1 = getActor(actor1); + Actor* physactor2 = getActor(actor2); + + if (!physactor1 || !physactor2) + return false; + + osg::Vec3f halfExt1 = physactor1->getHalfExtents(); + osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); + pos1.z() += halfExt1.z()*2*0.9f; // eye level + osg::Vec3f halfExt2 = physactor2->getHalfExtents(); + osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); + pos2.z() += halfExt2.z()*2*0.9f; + + RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap); + + return !result.mHit; } class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 2240a5119..7920347ad 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -11,6 +11,8 @@ #include +#include "collisiontype.hpp" + namespace osg { class Group; @@ -87,11 +89,20 @@ namespace MWPhysics const osg::Quat &orientation, float queryDistance); - // cast ray, return true if it hit something. - bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to,bool ignoreHeightMap = false); + struct RayResult + { + bool mHit; + osg::Vec3f mHitPos; + osg::Vec3f mHitNormal; + MWWorld::Ptr mHitObject; + }; + + /// @param me Optional, a Ptr to ignore in the list of results + RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::Ptr ignore = MWWorld::Ptr(), int mask = + CollisionType_World|CollisionType_HeightMap|CollisionType_Actor); - /// @return - std::pair castRay(const osg::Vec3f &from, const osg::Vec3f &to); + /// Return true if actor1 can see actor2. + bool getLineOfSight(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b23981f81..2279375c2 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -20,8 +20,6 @@ #include -#include - #include #include #include diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 642909cda..42a63fc68 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e1f1e13d1..a067422d4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -298,6 +298,21 @@ namespace MWRender mObjects->removeObject(ptr); } + void RenderingManager::getCameraToViewportRay(float nX, float nY, osg::Vec3f &origin, osg::Vec3f &dest) + { + osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); + osg::Matrix invViewProj = viewProj.inverse(viewProj); + + nX = nX * 2 - 1; + nY = nY * -2 + 1; + + osg::Vec3f start (nX, nY, -1.f); + osg::Vec3f end (nX, nY, 1.f); + + origin = invViewProj.preMult(start); + dest = invViewProj.preMult(end); + } + osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) { if (!ptr.getRefData().getBaseNode()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 011ceee09..e2524b360 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -80,6 +80,9 @@ namespace MWRender /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); + /// Get a camera to viewport ray for normalized screen coordinates nX and nY, with the top left corner being at (0,0) + void getCameraToViewportRay(float nX, float nY, osg::Vec3f& origin, osg::Vec3f& dest); + void setSkyEnabled(bool enabled); bool toggleRenderMode(RenderMode mode); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c9e69b0b4..81497aa6c 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -419,6 +419,7 @@ void WeatherManager::update(float duration, bool paused) mStormDirection = (playerPos - redMountainPos); mStormDirection.z() = 0; + mStormDirection.normalize(); mRendering->getSkyManager()->setStormDirection(mStormDirection); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 926e453b8..f4f960d97 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -7,7 +7,6 @@ #else #include #endif -#include #include #include @@ -21,8 +20,6 @@ #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -1411,9 +1408,10 @@ namespace MWWorld bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) { - Ogre::Vector3 a(x1,y1,z1); - Ogre::Vector3 b(x2,y2,z2); - return 0;//mPhysics->castRay(a,b,false,true); + osg::Vec3f a(x1,y1,z1); + osg::Vec3f b(x2,y2,z2); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World); + return result.mHit; } void World::processDoors(float duration) @@ -1793,17 +1791,26 @@ namespace MWWorld MWWorld::Ptr World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) { - std::pair result;// = mPhysics->castRay(cursorX, cursorY); + osg::Vec3f origin, dest; + mRendering->getCameraToViewportRay(cursorX, cursorY, origin, dest); + + const float maxDist = 200.f; + osg::Vec3f dir = (dest - origin); + dir.normalize(); + dest = origin + dir * maxDist; - if (!result.first) - return MWWorld::Ptr(); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(origin, dest, MWWorld::Ptr(), + MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap); CellStore* cell = getPlayerPtr().getCell(); ESM::Position pos = getPlayerPtr().getRefData().getPosition(); - pos.pos[0] = result.second[0]; - pos.pos[1] = result.second[1]; - pos.pos[2] = result.second[2]; + if (result.mHit) + { + pos.pos[0] = result.mHitPos.x(); + pos.pos[1] = result.mHitPos.y(); + pos.pos[2] = result.mHitPos.z(); + } // We want only the Z part of the player's rotation pos.rot[0] = 0; pos.rot[1] = 0; @@ -1822,21 +1829,23 @@ namespace MWWorld bool World::canPlaceObject(float cursorX, float cursorY) { - Ogre::Vector3 normal(0,0,0); - std::string handle; - std::pair result;// = mPhysics->castRay(cursorX, cursorY, &normal, &handle); + osg::Vec3f origin, dest; + mRendering->getCameraToViewportRay(cursorX, cursorY, origin, dest); - if (result.first) + const float maxDist = 200.f; + osg::Vec3f dir = (dest - origin); + dir.normalize(); + dest = origin + dir * maxDist; + + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(origin, dest, MWWorld::Ptr(), + MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap); + + if (result.mHit) { // check if the wanted position is on a flat surface, and not e.g. against a vertical wall - if (normal.angleBetween(Ogre::Vector3(0.f,0.f,1.f)).valueDegrees() >= 30) + if (std::acos(result.mHitNormal * osg::Vec3f(0,0,1)) >= osg::DegreesToRadians(30.f)) return false; - /* - MWWorld::Ptr hitObject = searchPtrViaHandle(handle); - if (!hitObject.isEmpty() && hitObject.getClass().isActor()) - return false; - */ return true; } else @@ -1917,9 +1926,10 @@ namespace MWWorld float len = 100.0; - std::pair hit = mPhysics->castRay(orig, dir*len); - if (hit.first) - pos.pos[2] = hit.second.z(); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(orig, orig+dir*len, MWWorld::Ptr(), + MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap); + if (result.mHit) + pos.pos[2] = result.mHitPos.z(); // copy the object and set its count int origCount = object.getRefData().getCount(); @@ -2346,50 +2356,30 @@ namespace MWWorld } } - bool World::getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) + bool World::getLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor) { if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) return false; // not in active cell - // TODO: move to PhysicsSystem - /* - OEngine::Physic::PhysicActor* actor1 = mPhysEngine->getCharacter(actor.getRefData().getHandle()); - OEngine::Physic::PhysicActor* actor2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle()); - - if (!actor1 || !actor2) - return false; - - Ogre::Vector3 halfExt1 = actor1->getHalfExtents(); - const float* pos1 = actor.getRefData().getPosition().pos; - Ogre::Vector3 halfExt2 = actor2->getHalfExtents(); - const float* pos2 = targetActor.getRefData().getPosition().pos; - - btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9f); // eye level - btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z*2*0.9f); - - std::pair result = mPhysEngine->rayTest(from, to,false); - if(result.first == "") return true; - */ - - return true; + return mPhysics->getLineOfSight(actor, targetActor); } float World::getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) { - return 0; - /* - btVector3 btFrom(from.x, from.y, from.z); - btVector3 btTo = btVector3(dir.x, dir.y, dir.z); - btTo.normalize(); - btTo = btFrom + btTo * maxDist; + osg::Vec3f from_ (from.x, from.y, from.z); + osg::Vec3f to_ (dir.x, dir.y, dir.z); + to_.normalize(); + to_ = from_ + (to_ * maxDist); - std::pair result = mPhysEngine->rayTest(btFrom, btTo, false); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from_, to_, MWWorld::Ptr(), + MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap); - if(result.second == -1) return maxDist; - else return result.second*(btTo-btFrom).length(); - */ + if (!result.mHit) + return maxDist; + else + return (result.mHitPos - from_).length(); } void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) @@ -2697,37 +2687,30 @@ namespace MWWorld else { // For NPCs use facing direction from Head node - Ogre::Vector3 origin(actor.getRefData().getPosition().pos); -#if 0 - MWRender::Animation *anim = mRendering->getAnimation(actor); - if(anim != NULL) + osg::Vec3f origin(actor.getRefData().getPosition().asVec3()); + + MWRender::Animation* anim = mRendering->getAnimation(actor); + if (anim != NULL) { - Ogre::Node *node = anim->getNode("Head"); + const osg::Node* node = anim->getNode("Head"); if (node == NULL) node = anim->getNode("Bip01 Head"); - if(node != NULL) - origin += node->_getDerivedPosition(); - } -#endif - /* - - Ogre::Quaternion orient; - orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); - Ogre::Vector3 direction = orient.yAxis(); - Ogre::Vector3 dest = origin + direction * distance; - - std::vector > collisions = mPhysEngine->rayTest2(btVector3(origin.x, origin.y, origin.z), btVector3(dest.x, dest.y, dest.z)); - for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end(); ++cIt) - { - MWWorld::Ptr collided = getPtrViaHandle(cIt->second); - if (collided != actor) + if (node != NULL) { - target = collided; - break; + osg::MatrixList mats = node->getWorldMatrices(); + if (mats.size()) + origin = mats[0].getTrans(); } } - */ + + osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) + * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); + + osg::Vec3f direction = orient * osg::Vec3f(0,1,0); + osg::Vec3f dest = origin + direction * distance; + + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(origin, dest, actor); + target = result.mHitObject; } std::string selectedSpell = stats.getSpells().getSelectedSpell();