// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "PxScene.h" #include "PxQueryReport.h" #include "PxBatchQueryDesc.h" #include "extensions/PxJoint.h" #include "PxRigidDynamic.h" #include "extensions/PxDistanceJoint.h" #include "extensions/PxSphericalJoint.h" #include "PxArticulationLink.h" #include "PxShape.h" #include "Picking.h" #include "RendererMemoryMacros.h" #if PX_UNIX_FAMILY #include #endif using namespace physx; // PT: please DO NOT indent the whole file Picking::Picking(PhysXSample& frame) : mSelectedActor (NULL), mMouseJoint (NULL), mMouseActor (NULL), mMouseActorToDelete (NULL), mDistanceToPicked (0.0f), mMouseScreenX (0), mMouseScreenY (0), mFrame (frame) { } Picking::~Picking() {} bool Picking::isPicked() const { return mMouseJoint!=0; } void Picking::moveCursor(PxI32 x, PxI32 y) { mMouseScreenX = x; mMouseScreenY = y; } /*void Picking::moveCursor(PxReal deltaDepth) { const PxReal range[2] = { 0.0f, 1.0f }; const PxReal r = (range[1] - range[0]); const PxReal d = (mMouseDepth - range[0])/r; const PxReal delta = deltaDepth*0.02f*(1.0f - d); mMouseDepth = PxClamp(mMouseDepth + delta, range[0], range[1]); }*/ void Picking::tick() { if(mMouseJoint) moveActor(mMouseScreenX,mMouseScreenY); // PT: delete mouse actor one frame later to avoid crashes SAFE_RELEASE(mMouseActorToDelete); } void Picking::computeCameraRay(PxVec3& orig, PxVec3& dir, PxI32 x, PxI32 y) const { const PxVec3& camPos = mFrame.getCamera().getPos(); // compute picking ray // const PxVec3 rayOrig = unProject(x, y, 0.0f); // PT: what the frell is that? const PxVec3 rayOrig = camPos; const PxVec3 rayDir = (unProject(x, y, 1.0f) - rayOrig).getNormalized(); orig = rayOrig; dir = rayDir; } bool Picking::pick(int x, int y) { PxScene& scene = mFrame.getActiveScene(); PxVec3 rayOrig, rayDir; computeCameraRay(rayOrig, rayDir, x, y); // raycast rigid bodies in scene PxRaycastHit hit; hit.shape = NULL; PxRaycastBuffer hit1; scene.raycast(rayOrig, rayDir, PX_MAX_F32, hit1, PxHitFlag::ePOSITION); hit = hit1.block; if(hit.shape) { const char* shapeName = hit.shape->getName(); if(shapeName) shdfnd::printFormatted("Picked shape name: %s\n", shapeName); PxRigidActor* actor = hit.actor; PX_ASSERT(actor); mSelectedActor = static_cast(actor->is()); if(!mSelectedActor) mSelectedActor = static_cast(actor->is()); //ML::this is very useful to debug some collision problem PxTransform t = actor->getGlobalPose(); PX_UNUSED(t); shdfnd::printFormatted("id = %i\n PxTransform transform(PxVec3(%f, %f, %f), PxQuat(%f, %f, %f, %f))\n", reinterpret_cast(actor->userData), t.p.x, t.p.y, t.p.z, t.q.x, t.q.y, t.q.z, t.q.w); } else mSelectedActor = 0; if(mSelectedActor) { shdfnd::printFormatted("Actor '%s' picked! (userData: %p)\n", mSelectedActor->getName(), mSelectedActor->userData); //if its a dynamic rigid body, joint it for dragging purposes: grabActor(hit.position, rayOrig); } #ifdef VISUALIZE_PICKING_RAYS Ray ray; ray.origin = rayOrig; ray.dir = rayDir; mRays.push_back(ray); #endif return true; } //----------------------------------------------------------------------------// PxActor* Picking::letGo() { // let go any picked actor if(mMouseJoint) { mMouseJoint->release(); mMouseJoint = NULL; // SAFE_RELEASE(mMouseActor); // PT: releasing immediately crashes PX_ASSERT(!mMouseActorToDelete); mMouseActorToDelete = mMouseActor; // PT: instead, we mark for deletion next frame } PxActor* returnedActor = mSelectedActor; mSelectedActor = NULL; return returnedActor; } //----------------------------------------------------------------------------// void Picking::grabActor(const PxVec3& worldImpact, const PxVec3& rayOrigin) { if(!mSelectedActor || (mSelectedActor->getType() != PxActorType::eRIGID_DYNAMIC && mSelectedActor->getType() != PxActorType::eARTICULATION_LINK)) return; PxScene& scene = mFrame.getActiveScene(); PxPhysics& physics = scene.getPhysics(); //create a shape less actor for the mouse { mMouseActor = physics.createRigidDynamic(PxTransform(worldImpact, PxQuat(PxIdentity))); mMouseActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); mMouseActor->setMass(1.0f); mMouseActor->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); scene.addActor(*mMouseActor); } PxRigidActor* pickedActor = static_cast(mSelectedActor); #if USE_D6_JOINT_FOR_MOUSE mMouseJoint = PxD6JointCreate( physics, mMouseActor, PxTransform(PxIdentity), pickedActor, PxTransform(pickedActor->getGlobalPose().transformInv(worldImpact))); mMouseJoint->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); mMouseJoint->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); mMouseJoint->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); #elif USE_SPHERICAL_JOINT_FOR_MOUSE mMouseJoint = PxSphericalJointCreate(physics, mMouseActor, PxTransform(PxIdentity), pickedActor, PxTransform(pickedActor->getGlobalPose().transformInv(worldImpact))); #else mMouseJoint = PxDistanceJointCreate(physics, mMouseActor, PxTransform(PxIdentity), pickedActor, PxTransform(pickedActor->getGlobalPose().transformInv(worldImpact))); mMouseJoint->setMaxDistance(0.0f); mMouseJoint->setMinDistance(0.0f); mMouseJoint->setDistanceJointFlags(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED); #endif mDistanceToPicked = (worldImpact - rayOrigin).magnitude(); } //----------------------------------------------------------------------------// void Picking::moveActor(int x, int y) { if(!mMouseActor) return; PxVec3 rayOrig, rayDir; computeCameraRay(rayOrig, rayDir, x, y); const PxVec3 pos = rayOrig + mDistanceToPicked * rayDir; mMouseActor->setKinematicTarget(PxTransform(pos, PxQuat(PxIdentity))); } //----------------------------------------------------------------------------// PxVec3 Picking::unProject(int x, int y, float depth) const { SampleRenderer::Renderer* renderer = mFrame.getRenderer(); const SampleRenderer::RendererProjection& projection = mFrame.getCamera().getProjMatrix(); const PxTransform view = mFrame.getCamera().getViewMatrix().getInverse(); PxU32 windowWidth = 0; PxU32 windowHeight = 0; renderer->getWindowSize(windowWidth, windowHeight); const PxF32 outX = (float)x / (float)windowWidth; const PxF32 outY = (float)y / (float)windowHeight; return SampleRenderer::unproject(projection, view, outX * 2 -1, outY * 2 -1, depth * 2 - 1); } //----------------------------------------------------------------------------// void Picking::project(const physx::PxVec3& v, int& xi, int& yi, float& depth) const { SampleRenderer::Renderer* renderer = mFrame.getRenderer(); SampleRenderer::RendererProjection projection = mFrame.getCamera().getProjMatrix(); const PxTransform view = mFrame.getCamera().getViewMatrix().getInverse(); PxVec3 pos = SampleRenderer::project(projection, view, v); ///* Map x, y and z to range 0-1 */ pos.x = (pos.x + 1 ) * 0.5f; pos.y = (pos.y + 1 ) * 0.5f; pos.z = (pos.z + 1 ) * 0.5f; PxU32 windowWidth = 0; PxU32 windowHeight = 0; renderer->getWindowSize(windowWidth, windowHeight); /* Map x,y to viewport */ pos.x *= windowWidth; pos.y *= windowHeight; depth = (float)pos.z; xi = (int)(pos.x + 0.5); yi = (int)(pos.y + 0.5); }