// 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-2025 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 "ScScene.h" #include "ScNPhaseCore.h" #include "ScShapeInteraction.h" #include "ScConstraintSim.h" #include "ScConstraintCore.h" #include "CmVisualization.h" using namespace physx; using namespace Sc; // PT: Sc-level visualization code has been moved to this dedicated file (like we did in NpDebugViz.cpp) static void visualize(const ConstraintSim& sim, Cm::ConstraintImmediateVisualizer& viz, PxU32 flags, const PxTransform& idt) { ConstraintCore& core = sim.getCore(); if(!(core.getFlags() & PxConstraintFlag::eVISUALIZATION)) return; const Dy::Constraint& llc = sim.getLowLevelConstraint(); PxsRigidBody* b0 = llc.body0; PxsRigidBody* b1 = llc.body1; const PxTransform& t0 = b0 ? b0->getPose() : idt; const PxTransform& t1 = b1 ? b1->getPose() : idt; core.getVisualize()(viz, llc.constantBlock, t0, t1, flags); } void Sc::ShapeInteraction::visualize(PxRenderOutput& out, PxsContactManagerOutputIterator& outputs, float scale, float contactImpulse, float contactNormal, float contactError, float contactPoint, float frictionImpulse, float frictionNormal, float frictionPoint) { if(mManager) // sleeping pairs have no contact points -> do not visualize { Sc::ActorSim* actorSim0 = &getActor0(); Sc::ActorSim* actorSim1 = &getActor1(); if(!actorSim0->isNonRigid() && !actorSim1->isNonRigid()) { PxU32 offset; PxU32 nextOffset = 0; do { const void* contactPatches; const void* contactPoints; PxU32 contactDataSize; PxU32 contactPointCount; PxU32 contactPatchCount; const PxReal* impulses; const void* frictionPatches; offset = nextOffset; nextOffset = getContactPointData(contactPatches, contactPoints, contactDataSize, contactPointCount, contactPatchCount, impulses, offset, outputs, frictionPatches); const PxU32* faceIndices = reinterpret_cast(impulses + contactPointCount); PxContactStreamIterator iter(reinterpret_cast(contactPatches), reinterpret_cast(contactPoints), faceIndices, contactPatchCount, contactPointCount); PxFrictionAnchorStreamIterator fricIter(reinterpret_cast(contactPatches), reinterpret_cast(frictionPatches), contactPatchCount); PxU32 i = 0; while(iter.hasNextPatch()) { iter.nextPatch(); while(iter.hasNextContact()) { iter.nextContact(); if((contactImpulse != 0.0f) && impulses) { out << PxU32(PxDebugColor::eARGB_RED); out.outputSegment(iter.getContactPoint(), iter.getContactPoint() + iter.getContactNormal() * (scale * contactImpulse * impulses[i])); } else if(contactNormal != 0.0f) { out << PxU32(PxDebugColor::eARGB_BLUE); out.outputSegment(iter.getContactPoint(), iter.getContactPoint() + iter.getContactNormal() * (scale * contactNormal)); } else if(contactError != 0.0f) { out << PxU32(PxDebugColor::eARGB_YELLOW); out.outputSegment(iter.getContactPoint(), iter.getContactPoint() + iter.getContactNormal() * PxAbs(scale * contactError * PxMin(0.f, iter.getSeparation()))); } if(contactPoint != 0.0f) { const PxReal s = scale * 0.1f; const PxVec3& point = iter.getContactPoint(); //if (0) //temp debug to see identical contacts // point.x += scale * 0.01f * (contactPointCount - i + 1); out << PxU32(PxDebugColor::eARGB_RED); out.outputSegment(point + PxVec3(-s, 0, 0), point + PxVec3(s, 0, 0)); out.outputSegment(point + PxVec3(0, -s, 0), point + PxVec3(0, s, 0)); out.outputSegment(point + PxVec3(0, 0, -s), point + PxVec3(0, 0, s)); } i++; } if (fricIter.hasNextPatch()) { fricIter.nextPatch(); while (fricIter.hasNextFrictionAnchor()) { fricIter.nextFrictionAnchor(); if (frictionImpulse != 0.0f) { out << PxU32(PxDebugColor::eARGB_DARKRED); out.outputSegment(fricIter.getPosition(), fricIter.getPosition() + fricIter.getImpulse() * (scale * frictionImpulse)); } else if (frictionNormal != 0.0f) { out << PxU32(PxDebugColor::eARGB_BLUE); out.outputSegment(fricIter.getPosition(), fricIter.getPosition() + fricIter.getNormal() * (scale * frictionNormal)); } if (frictionPoint != 0.0f) { const PxReal s = scale * 0.1f; const PxVec3& p = fricIter.getPosition(); out << PxU32(PxDebugColor::eARGB_DARKRED); out.outputSegment(p + PxVec3(-s, 0, 0), p + PxVec3(s, 0, 0)); out.outputSegment(p + PxVec3(0, -s, 0), p + PxVec3(0, s, 0)); out.outputSegment(p + PxVec3(0, 0, -s), p + PxVec3(0, 0, s)); } } } } } while (nextOffset != offset); } } } // Render objects before simulation starts void Sc::Scene::visualizeStartStep() { PX_PROFILE_ZONE("Sim.visualizeStartStep", mContextId); // Update from visualization parameters if(mVisualizationParameterChanged) { mVisualizationParameterChanged = false; // Update SIPs if visualization is enabled if( getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_IMPULSE) || getVisualizationParameter(PxVisualizationParameter::eFRICTION_POINT) || getVisualizationParameter(PxVisualizationParameter::eFRICTION_NORMAL) || getVisualizationParameter(PxVisualizationParameter::eFRICTION_IMPULSE)) mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION; } #if PX_ENABLE_DEBUG_VISUALIZATION const PxReal scale = getVisualizationScale(); if(scale==0.0f) { // make sure visualization inside simulate was skipped PX_ASSERT(getRenderBuffer().empty()); return; // early out if visualization scale is 0 } PxRenderOutput out(getRenderBuffer()); if(getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)) mAABBManager->visualize(out); // Visualize joints { const float frameScale = scale * getVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES); const float limitScale = scale * getVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS); if(frameScale!=0.0f || limitScale!=0.0f) { Cm::ConstraintImmediateVisualizer viz(frameScale, limitScale, out); PxU32 flags = 0; if(frameScale!=0.0f) flags |= PxConstraintVisualizationFlag::eLOCAL_FRAMES; if(limitScale!=0.0f) flags |= PxConstraintVisualizationFlag::eLIMITS; const PxTransform idt(PxIdentity); Sc::ConstraintCore*const * constraints = mConstraints.getEntries(); for(PxU32 i=0, size = mConstraints.size();igetSim(); if(sim) visualize(*sim, viz, flags, idt); } } } #else PX_CATCH_UNDEFINED_ENABLE_DEBUG_VISUALIZATION #endif } // Render contacts at the simulation frame end void Sc::Scene::visualizeContacts() { PX_PROFILE_ZONE("Sim.visualizeContacts", mContextId); #if PX_ENABLE_DEBUG_VISUALIZATION const PxReal scale = getVisualizationScale(); if (scale == 0.0f) { // make sure visualization inside simulate was skipped PX_ASSERT(getRenderBuffer().empty()); return; // early out if visualization scale is 0 } PxRenderOutput out(getRenderBuffer()); { // PT: put common reads here to avoid doing them for each interaction const PxReal contactImpulse = getVisualizationParameter(PxVisualizationParameter::eCONTACT_IMPULSE); const PxReal contactNormal = getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL); const PxReal contactError = getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR); const PxReal contactPoint = getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT); const PxReal frictionImpulse = getVisualizationParameter(PxVisualizationParameter::eFRICTION_IMPULSE); const PxReal frictionNormal = getVisualizationParameter(PxVisualizationParameter::eFRICTION_NORMAL); const PxReal frictionPoint = getVisualizationParameter(PxVisualizationParameter::eFRICTION_POINT); if(contactImpulse !=0.0f || contactNormal!=0.0f || contactError!=0.0f || contactPoint!=0.0f || frictionImpulse != 0.0f || frictionNormal != 0.0f || frictionPoint != 0.0f) { PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); ElementSimInteraction** interactions = getActiveInteractions(InteractionType::eOVERLAP); PxU32 nbActiveInteractions = getNbActiveInteractions(InteractionType::eOVERLAP); while(nbActiveInteractions--) static_cast(*interactions++)->visualize( out, outputs, scale, contactImpulse, contactNormal, contactError, contactPoint, frictionImpulse, frictionNormal, frictionPoint); } } #else PX_CATCH_UNDEFINED_ENABLE_DEBUG_VISUALIZATION #endif }