//Description: Physx Fix Own Project
//Create Date: 2022-05-23 18:17:39
//Author: channy
概述
使用和修改Physx应用到自己的项目中。
Debug颜色
PxRigidActor/PxBase 中增加颜色成员变量
// PxRigidActor.h
virtual PxVec3 getColor() { return PxVec3(colorx / 255.f, colory / 255.f, colorz / 255.f); }
virtual void setColor(const PxVec3& _color) {
colorx = static_cast<PxU8>(_color.x * 255);
colory = static_cast<PxU8>(_color.y * 255);
colorz = static_cast<PxU8>(_color.z * 255);
}
snippet样例中通过renderActors修改color
减少overlap的pair数量
创建场景 createScene(PGS/TGS)
createScene (NpPhysics) 创建scene
NpScene -> NpSceneQueries (create)
Scene (create)
createDynamicsContext/createTGSDynamicsContext
DynamicsContext.create 创建上下文
场景属性 PxSceneDesc
- gravity 重力大小和方向,
- PxSimulationEventCallback 模拟事件回调,
- PxContactModifyCallback 碰撞解算修改回调,
- PxCCDContactModifyCallback CCD的碰撞解算修改回调,
- filterShader 全局的碰撞分类处理函数,
- cpuDispatcher Cpu线程分配器
场景 PxScene
PxScene -> NpScene -> Scene, 其中在构造中创建且后续步骤使用较多的有:
- PxsContext* mLLContext 和 Context* mDynamicsContext 上下文
- AABBManager* mAABBManager
- BroadPhase* mBP 碰撞检测宽阶段算法
Kinematic
Kinematic型的rigidbody质量相当于无穷大,它基本不受力的影响,也基本不参与碰撞解算流程,它的运动是直接设定位置的,而非Kinematic型Rigidbody运动是通过速度,或者力进行计算的,而且碰到东西穿插了会进行复杂的碰撞解算。
Kinematic型由于不受力和碰撞的影响,所以比较适合随着动画运动的碰撞盒,或者由服务器同步运动的碰撞盒,中间不需要额外的计算干扰,比较省性能,也不会出现抖动。
弹簧常数kp和阻尼常数kd
ERP = hkp/(hkp + kd)
CFM = 1/(hkp + kd)
其中h为步长。这些数值将产生与用隐式一阶积分模拟弹簧-阻尼系统相同的效果
多线程任务 Task
采用引用计数方式建立task依赖
A.setContinuation(B)表示A执行完后执行B,set时B.count+1, A结束时B.count-1, B.count = 0时执行B
物理模拟或碰撞计算 simulate
- PxScene > NpSceneAccessor > NpSceneQueries > NpScene.simulate 开始物理模拟
- simulateOrCollide 物理模拟或碰撞计算,SimulationStage = eADVANCE/eCOLLIDE
- visualize debug显示相关
- updateDirtyShaders //do nothing
- NpConstraint.updateConstants
- Scene.updateLowLevelMaterial 材质相关
- PxTaskManager.startSimulation 分发Task
- NpScene.executeScene / NpScene.executeCollide
- (ScbScene -> ScScene).simulate / Scene::collide
- prepareCollide 基本初始化操作
- Scene.stepSetupCollide
- kinematicsSetup (ScKinematicUpdateTask) 设置kinematic转成dynamic的物体
- NPhaseCore.updateDirtyInteractions 处理标脏碰撞对
- Scene.collideStep 串联Task
- Scene::preRigidBodyNarrowPhase
- DirtyShapeUpdatesTask 更新场景中dirtyShapes的AABB
- Scene::rigidBodyNarrowPhase 串联Task
- Scene.broadPhase 更新mDirtyAggregates
- AABBManager.updateAABBsAndBP 更新add/remove/update的handle数据,以Aggregate为单位更新AABB
- finalizeUpdate (FinalizeUpdateTask)
- update (BroadPhase,为 BroadPhaseSap[少动态物体] 或 BroadPhaseMBP[需指定bound] 或 BroadPhaseABP[多动态物体],由创建scene时的参数确定,默认为ABP)
- Scene::postBroadPhase
- AABBManager.postBroadPhase
- BroadPhase.fetchBroadPhaseResults // do nothing
- processBPPairs (DeletedPairHandler.processPair,只BroadPhaseSap会赋值mDeletedPairsSize)
- SortAggregateBoundsParallel 对每个Aggregate中的bounds排序
- Aggregate.getSortedMinBounds
- AABBManager.postBpStage2 (PostBroadPhaseStage2Task)
- ProcessSelfCollisionPairsParallel 对每个Aggregate,处理selfCollisionPairs,并把task加入到mAggPairTasks中
- updatePairs
- PersistentPairs.updatePairs
- PersistentActorAggregatePair.findOverlaps
- putBpCacheData
- processAggregatePairs/processAggregatePairsParallel (ProcessAggPairsParallelTask) 有优化点:hashMap可用array代替 //处理的aggregate数据在processBPCreatedPair中赋值,task也加入到mAggPairTasks中
- AABBManager.postBpStage3
- resetOrClear 重置mDirtyAggregates
- processBPPairs (CreatedPairHandler.processPair) 创建新碰撞对,保存到mAggregateAggregatePairs/mActorAggregatePairs中 , 处理outOfBound刚体
- AABBManager.processBPCreatedPair
- createOverlap 写入AABBMananger.mCreatedOverlaps的碰撞对数据
- Scene::postBroadPhaseContinuation
- finishBroadPhase 获取粗阶段的结果,mNbNewPairs
- AABBManager.getCreatedOverlaps
- onOverlapCreated
- createRbElementInteraction
- filterRbCollisionPair
- createRbElementInteraction 写入NPhaseCore.mFilterPairManager
- OverlapFilterTask 过滤不必要的碰撞对,如约束之间的碰撞
- NPhaseCore.runOverlapFilters 会根据scene的filterShader函数过滤不必要的碰撞对
- filterRbCollisionPair
- filterRbCollisionPairSecondStage
- runFilterShapeSim context.mFilterShader
- preallocateContactManagers
- filterRbCollisionPairSecondStage
- OnOverlapCreatedTask
- createRbElementInteraction
- Scene::postBroadPhaseStage2
- processLostTouchPairs 唤醒mLostTouchPairs
- islandInsertion
- registerContactManagers
- registerInteractions
- registerSceneInteractions
- Scene.postBroadPhaseStage3
- finishBroadPhaseStage2
- processLostTouchPairs ccd会对mLostTouchPairs有新的改动
- ScScene.advanceStep
- Scene::secondPassNarrowPhase
- SimpleIslandManager.additionalSpeculativeActivation 唤醒机制
- Scene::postNarrowPhase
- PxsContext.fetchUpdateContactManager
- PxsNphaseImplementationContext.fetchUpdateContactManager // do nothing here
- mergeCMDiscreteUpdateResults
- releaseConstraints 提高稳定性牺牲部分性能eENABLE_STABILIZATION
- Scene::islandGen 串联Task
- fetchPatchEvents
- PxsContext.fillManagerPatchChangedEvents
- processLostSolverPatches
- DynamicsContext.processLostPatches //do nothing
- processNarrowPhaseTouchEvents 处理touch事件
- PxsContext.fillManagerTouchEvents
- setEdgesConnected
- secondPassIslandGen 唤醒island
- wakeIslands
- processNewEdges
- removeDestroyedEdges
- processLostEdges
- wakeObjectsUp 唤醒actor
- processNarrowPhaseTouchEventsStage2
- NphaseCore.managerNewTouch
- Scene::postIslandGen
- NphaseCore.processTriggerInteractions
- Scene::solver
- Scene::updateBodiesAndShapes
- Scene::updateDynamics
- DynamicsContext.update (更新动态物体,详)
- processLostContacts
- processNarrowPhaseLostTouchEventsIslands 断开island
- processNarrowPhaseLostTouchEvents 不再接触的pair加入到list中
- Scene::updateSimulationController // do nothing
- Scene::postSolver
- DynamicsContext.mergeResults
- integrateKinematicPose (ScKinematicPoseUpdateTask) 更新设置kinematic的transform
- Scene::afterIntegration
- Scene::finalizationPhase
- fireOnAdvanceCallback 回调PxSimulationEventCallback.onAdvance
- checkConstraintBreakage 检测约束是否break
BroadPhase 三种算法
BroadPhase.update 输入参数BroadPhaseUpdateData中包含了add/remove/update的handle索引,毛数据存储于mBoxBounds中,输出BroadPhasePair
- BroadPhaseABP.update
- setUpdateData 传入参数BroadPhaseUpdateData存储了新增/删除/位置更新的object,只存索引,即mBoxBounds的下标,实际数据在mBoxBounds中,即AABBManager.mBoundsArray。
有优化点:可能存在不必要的copy
- removeObjects 对remove中的每一个handle,调用ABP.removeObject。其中handle虽然是ShapeHandle,实际上是BoundHandle
- ABP.removeObject 只把mInToOut_Sleeping/mInToOut_Updated的index标记为INVALID_ID
- addObjects
- ABP.addDynamicObjects
- BoxManager.addObjects 在mInToOut_Updated中分batch增加index,先把batch的index填充,再批量加入到mInToOut_Updated中
- updateObjects 从sleep移到update中
- ABP.Region_prepareOverlaps //static、dynamic、kinematic三个BoxManager
- prepareData 遍历BoxManager中的每个物体,更新bounding,对新加入的sleeping物体合并排序,存储在mInToOut_Sleeping中,及对update也进行排序存储在mInToOut_Updated中
- purgeRemovedFromSleeping 删除已经remove的sleeping的物体
- RadixSort.sort
- notifyStaticChange // do nothing here
- update
- ABP.findOverlaps
- Region_findOverlaps 查找碰撞对(ABP_PairManager/PairManagerData.mNbActivePairs)
- Region_prepareOverlaps
- findAllOverlaps //static-dynamic、dynamic-dynamic
- doCompleteBoxPruning_ //dynamic-dynamic
- doBipartiteBoxPruning_Leaf //sleeping-active dynamic, 判断AABB相交
- doCompleteBoxPruning_Leaf
- outputPair 赋值index给ABP的ABP_PairManager.mInToOut/ABP_MM
- doBipartiteBoxPruning_Leaf 有优化点:bit冗余 // acitve dynamic - static, dynamic - kinematic
- postUpdate
- ABP.finalize
- ABP_PairManager.computeCreatedDeletedPairs copy新增和删除的Pairs到BroadPhaseABP.mCreated/mDeleted,处理碰撞开始/持续/分离期。没有标记新增和更新的,除了可能是删除的pair外,还可能是sleeping的pair。MBP与ABP的不同点在于多了一个decodeHandle_Index获取实际index的步骤。除了persistentPairs,只有此处会调用PairManager.removePair
- BroadPhaseSap.update
- SapUpdateWorkTask
- update
- batchUpdate (BroadPhaseBatchUpdateWorkTask,对每个axis,输出BroadPhasePair保存到每个batch中)
- SapPostUpdateWorkTask
- postUpdate addPair到SapPairManager中,保存了所有相交的Box-Box对
- batchCreate 有优化点:把静态/动态物体分开可以加速
- ComputeSortedLists
- performBoxPruningNewNew
- performBoxPruningNewOld
- ComputeCreatedDeletedPairsLists
- BroadPhaseMBP.update 输出存储在MBP_PairManager中
- setUpdateData
- removeObjects 对removed中的每一个handle索引,调用MBP.removeObject。这里的mMapping数组,是从handle到MBP_Handle的映射。MBP_Handle为PxU32类型,最后一位是isStatic标志,倒数第2位是flipFlop标志,其它存储objectIndex,即MBP.mMBP_Objects中的索引。
- MBP.removeObject 从每个region中删除物体
- purgeHandles 若nbHandles == 1,直接存放在MBP_Object的mHandle变量中; 若nbHandles > 1,即一个MBP_Object跨了多个region,则放在二维数组mHandles中
- addObjects 计算物体的aabb,存储到mMBP_Objects中,遍历每个region,如果该region与物体相交,就调用region的addObject
- updateObjects
- prepareOverlaps
- update (MBPUpdateWorkTask)
- MBP.findOverlaps
- Region.findOverlaps
- doCompleteBoxPruning 遍历每个活动的物体A,找出与A(在x轴上)相交的一个或多个sleeping物体; 遍历每个sleeping物体B,找出与B(在x轴上)相交的一个或多个活动的物体
- doBipartiteBoxPruning
- postUpdate (MBPPostUpdateWorkTask)
- finalize
- MBP_PairManager.computeCreatedDeletedPairs
- NpSceneQueries.sweep
- multiQuery
- AABBPruner.sweep
- AABBTreeRaycast
- doLeafTest
- MultiQueryCallback
- applyAllPreFiltersSQ
- PxQueryFilterCallback.preFilter 碰撞分组
约束的不同类型
enum ConstraintType
{
eCONTACT_CONSTRAINT, //!< Defines this pair is a contact constraint
eJOINT_CONSTRAINT //!< Defines this pair is a joint constraint
};
enum SolverConstraintType
- NpPhysics.createConstraint
更新动态物体 DynamicsContext.update
- DynamicsContext.update 约束求解,更新动态物体,每个island单独求解
- UpdateContinuationTask.runInternal
- updatePostKinematic
- PxsForceThresholdTask
- createForceChangeThresholdStream
- createSolverTaskChain 对每一个island
- PxsSolverStartTask
- startTasks
- integrate
- DynamicsContext.preIntegrationParallel
- PxsPreIntegrateTask.runInternal 默认为 4 次位置迭代和 1 次速度迭代。
- preIntegrationParallel
- bodyCoreComputeUnconstrainedVelocity
- copyToSolverBodyData
- setupDescTask
- PxsSolverConstraintPostProcessTask 对每一个header
- articulationTask
- SolverArticulationUpdateTask
- PxsSolverConstraintPartitionTask
- PxsSolverCreateFinalizeConstraintsTask (约束数据填充,详)
- PxsSolverSetupSolveTask (约束求解,详)
- PxsSolverEndTask
- KinematicCopyTask
PhysX 中的求解器
batch constraints对提供的constraints重新排序并生成 batchHeaders ,solver可以使用这些batchHeaders来加速约束求解(constraint solving),方法是将独立约束组合在一起并使用 SIMD 寄存器中的多个通道并行求解。
- PxsSolverCreateFinalizeConstraintsTask 分割组装BatchHeader,以blockSize为单位求解约束,设置基本数值、初步计算
- PxsCreateFinalizeContactsTask 对每一个block求解
- createFinalizeContacts_Parallel (PxsCreateFinalizeContactsTask)
- ConstraintHelper.setupSolverConstraint
- PxsCreateArticConstraintsTask 对每一个articulation
PGS vs TGS (Projected/Temporal Gauss-Seidel Solver)
TGS: 速度快
约束求解
- PxsSolverSetupSolveTask 求解
-
- PxsParallelSolverTask (numTasks > 1)
- solveParallel -> SolverCoreGeneral.solveV_Blocks
- SolveBlockParallel 不同类型的约束分开求解,不同type对应gVTableSolveBlock中的求解函数
- solve1DBlock 关节约束求解
- solve1D (DySolverConstraints.cpp) SI,一维约束迭代。$\lambda_{k+1} = m_{i}^{eff} (b - J v_{k}) + \lambda_{k}$。其中unclampedForce对应$\lambda_{k+1}$,appliedForce对应$\lambda_{k}$,normalVel对应$J v_{k}$,vMul对应$-m_{i}^{eff}$,新增冲量$\Delta \lambda = \lambda_{k + 1} - \lambda_{k}$用于更新刚体速度。angState为$\sqrt{M} \omega$,即$\sqrt{M} \omega_{k + 1} = 1 / \sqrt{M} F^{k} + \sqrt{M} \omega_{k}$。invInertiaScale是一个标量,是PhysX为修改特定的约束表现留的一个量,用它缩放物体对特定约束的质量/惯量表现。
- solve1DConcludeBlock
- solve1DBlockWriteBack
- solveContactBlock 碰撞求解
- solveContact 计算摩擦
- solveDynamicContacts 计算物体与动态物体的碰撞
- solveContact_BStaticBlock 碰撞求解,计算物体与静态物体的碰撞
//DySolverConstraintsShared.h/solveDynamicContacts
//KS - clamp the maximum force
const FloatV _deltaF = FMax(FNegScaleSub(normalVel, velMultiplier, biasedErr), FNeg(appliedForce));
const FloatV _newForce = FAdd(appliedForce, _deltaF);
const FloatV newForce = FMin(_newForce, maxImpulse);
const FloatV deltaF = FSub(newForce, appliedForce);
第一行是为了保证_deltaF与appliedForce的和总是大于等于0的,即保证了法线方向的总冲量不会有负值。后面三行计算出截断后真正的deltaF。
// DySolverConstraints.cpp/solveContact
// appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation
const FloatV tmp1 = FNegScaleSub(FSub(bias, targetVel),velMultiplier,appliedForce);
// Algorithm:
// if abs(appliedForce + deltaF) > maxFrictionImpulse
// clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse]
// (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce]
// set broken flag to true || broken flag
// FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier);
// FloatV potentialSumF = FAdd(appliedForce, deltaF);
const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1);
// On XBox this clamping code uses the vector simple pipe rather than vector float,
// which eliminates a lot of stall cycles
const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse);
const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse));
const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse);
broken = BOr(broken, clamp);
FloatV deltaF = FSub(newAppliedForce, appliedForce);
计算截断的时候要先与静摩擦的最大冲量比,如果比静摩擦的大才截断到动摩擦的。
Solver将contacts分组为friction patches;friction patches是一组contacts,它们共享相同的材料并具有相似的contact normals。但是,Solver允许每个contact manager(一对Shape)最多有 32 个friction patches。如果产生超过 32 个friction patches(这可能是由于非常复杂的碰撞几何Shape或非常大的contact offsets),Solver将忽略剩余的friction patches。发生这种情况时,checked/debug版本中将出现警告。
动态与静态的碰撞
PhysX 中的PGS求解器
- PxsCreateFinalizeContactsTask.runInternal
- createFinalizeContacts_Parallel 赋值SolverConstraintShaderPrepDesc
- SetupSolverConstraint (DyConstraintSetup.cpp) 对每一个header.stride,求解约束;
- DistanceJointSolverPrep 如果有约束,具体的约束赋值
- SetupSolverConstraint 计算有效质量$ m_{i}^{eff} = 1 / (J_{i} M^{-1} J_{i}^{T}) = 1 / (J_{i}SS^{T}J_{i}^{T}) = 1 / (ZZ^{T}) = 1 / |Z|^2) $
其中$J_{i}$为由四个Vec3组成的广义速度 [lin0, ang0, lin1, ang1]。PhysX利用了$M^{-1}$的正定对称性,令$S=S^{T} = \sqrt{M^{-1}}$。其中unitResponse即为上述$|Z|^2$,存入s的ang0实为$S ang0$
- preprocessRows
- setSolverConstants 进一步计算一些迭代解用到的常量,包括软约束,其中velMultiplier对应$-m_{i}^{eff}$,constant对应$m_{i}^{eff} (b - Jv_{x})$
- createFinalizeSolverContacts 碰撞接触约束
- setupFinalizeSolverConstraints
- constructContactConstraint 计算法线方向上相关参数,其中velMultiplier对应$1/K$,penetrationInvDtPt8对应$v = 0.8D_p / \Delta t$,$D_p$是穿透深度,有穿透时小于0,使得相互穿插而无初速度的物体可以弹出来。
//DyContactPrepShared.h/constructContactConstraint
FloatV scaledBias = FMul(velMultiplier, penetrationInvDtPt8);
const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt));
const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration);
scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias);
const FloatV sumVRel(vrel);
FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero));
//Note - we add on the initial target velocity
targetVelocity = FSub(targetVelocity, vrel);
const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias));
const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero))));
- isGreater2:只有当恢复系数大于0、碰撞的前的速度大小大于一个bounceThreshold的阈值、且速度大小还大于$D_p / \Delta t$时,才将这次碰撞中的目标速度中考虑反弹速度的贡献。isGreater2为true时一般是高速碰撞,为false是低速接触。低速接触的解算目标是为了让物体能停在接触面上。
- ccdSeparationCondition:给Speculative CCD 使用的参数,默认为PX_MAX_F32。
- scaledBias:Baumgarte stabilization的速度乘以 ,这项在高速碰撞下一般为0。
- cTargetVel:如果用户在碰撞修改(ContactModify)中设置了目标速度则会取到非零值,默认是0。
- targetVelocity不是$v_{k+1}$而是$v_{k}$。这里targetVelocity =FSub(targetVelocity, vrel) 是为了让解算的时候速度初值都是0,而非碰撞时的世界空间的值。
- biasedErr和unbiasedErr:
高速碰撞时两者相同,对应于$ v / K $
低速碰撞时:biasedErr等于$(v - 0.8D_p / \Delta t) / K$; unbiasedErr在无穿透时($D_p > 0$)等于$(v - 0.8D_p / \Delta t) / K$,有穿透时为$ v / K $。
区分biasedErr和unbiasedErr是因为PhysX在动力学解算中是不改变刚体位置的。迭代整体被分为了先位置迭代(默认4次)再速度迭代(默认1次),而位置迭代的解算中只会用到biasedErr,速度迭代只会用到unbiasedErr。位置迭代完了的速度会用来计算刚体的位置、角度,而随后的速度迭代完了得到的速度是刚体在模拟结束后该时刻的速度。其实也就是把平均速度和瞬时速度分开了。这么做的目的是为了让Baumgarte stablization引入的修正量在速度迭代中被略去,以保证不引入能量增益。
//DyContactPrep.cpp/setupFinalizeSolverConstraints
const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero);
FloatV targetVel = V3Dot(tvel, t0);
const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0));
const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1));
const FloatV vrel = FSub(vrel1, vrel2);
targetVel = FSub(targetVel, vrel);
f0->normalXYZ_appliedForceW = V4SetW(t0, zero);
f0->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier);
f0->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t0, error), invDt));
FStore(targetVel, &f0->targetVel);
- velMultiplier:$0.8/K$;
- tvel:如果用户在碰撞修改(ContactModify)中设置了目标速度则会取到非零值,默认是0。
- error:两个刚体上碰撞点(Anchor)之间的向量,从1指向0。因为物理模拟是离散的,碰撞检测并不是两个刚体之间刚好接触才产生碰撞约束。
- biasW:切线方向的Baumgarte stabilization。
- raXnXYZ:$S (r \times \xi)$ 。
- updatePostKinematic.runInternal
- DynamicsContext.updatePostKinematic
PhysX的LCP求解器
- onSubstep
- updateVehicleManager
- updateAndRecordTelemetryData (SampleVehicle_VehicleManager)
- PxVehicleUpdateSingleVehicleAndStoreTelemetryData
- updateSingleVehicleAndStoreTelemetryData
- update
- updateDriveNW
- solveDriveNWInternalDynamicsEnginePlusDrivenWheels
- MatrixNGaussSeidelSolver.solve 使用LDL-GS求解Ax=b
- MatrixNNLUSolver 使用LU分解求解Ax=b
回写结果
- NpScene.fetchResults
- fetchResultsPreContactCallbacks
- Scene.endSimulation
- NPhaseCore.fireCustomFilteringCallbacks
- refilterInteraction
- callPairLost (最终调用到自定义的PxSimulationFilterCallback)
- fireQueuedContactCallbacks 回调PxSimulationEventCallback.onContact
- fetchResultsPostContactCallbacks
- Scene.syncSceneQueryBounds 更新mDirtyShapeSimMap
- SceneQueryManager.afterSync
PhysX 其它笔记
PxFrictionType 摩擦求解模型
PxAggregate 多个actor合并
自定义约束
PxConstraintConnector 接口,PxConstraintShaderTable函数类
测试场景构建
- 高度地图
- Part: 7种基于PxGeometry的零件类型
- Constraint: 7种基于PxJoint的约束及各种组合约束
- Static、Dynamic、Kit
reference
PhysX
PhysX doc