mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-09 17:20:33 +08:00
319 lines
12 KiB
C++
319 lines
12 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2020-2022 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated engine source code (the "Software"), a limited,
|
|
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
|
to use Cocos Creator solely to develop games on your target platforms. You shall
|
|
not use Cocos Creator software for developing other software or tools that's
|
|
used for developing games. You are not granted to publish, distribute,
|
|
sublicense, and/or sell copies of Cocos Creator.
|
|
|
|
The software or tools in this License Agreement are licensed, not sold.
|
|
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "physics/physx/PhysXWorld.h"
|
|
#include "physics/physx/PhysXFilterShader.h"
|
|
#include "physics/physx/PhysXInc.h"
|
|
#include "physics/physx/PhysXUtils.h"
|
|
#include "physics/physx/joints/PhysXJoint.h"
|
|
#include "physics/spec/IWorld.h"
|
|
|
|
namespace cc {
|
|
namespace physics {
|
|
|
|
PhysXWorld *PhysXWorld::instance = nullptr;
|
|
PhysXWorld &PhysXWorld::getInstance() {
|
|
return *instance;
|
|
}
|
|
|
|
physx::PxFoundation &PhysXWorld::getFundation() {
|
|
return *getInstance()._mFoundation;
|
|
}
|
|
|
|
physx::PxCooking &PhysXWorld::getCooking() {
|
|
return *getInstance()._mCooking;
|
|
}
|
|
|
|
physx::PxPhysics &PhysXWorld::getPhysics() {
|
|
return *getInstance()._mPhysics;
|
|
}
|
|
|
|
PhysXWorld::PhysXWorld() {
|
|
instance = this;
|
|
static physx::PxDefaultAllocator gAllocator;
|
|
static physx::PxDefaultErrorCallback gErrorCallback;
|
|
_mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);
|
|
physx::PxTolerancesScale scale{};
|
|
_mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *_mFoundation, physx::PxCookingParams(scale));
|
|
|
|
physx::PxPvd *pvd = nullptr;
|
|
#ifdef CC_DEBUG
|
|
pvd = _mPvd = physx::PxCreatePvd(*_mFoundation);
|
|
physx::PxPvdTransport *transport = physx::PxDefaultPvdSocketTransportCreate("127.0.0.1", 5425, 10);
|
|
_mPvd->connect(*transport, physx::PxPvdInstrumentationFlag::eALL);
|
|
#endif
|
|
_mPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *_mFoundation, scale, true, pvd);
|
|
PxInitExtensions(*_mPhysics, pvd);
|
|
_mDispatcher = physx::PxDefaultCpuDispatcherCreate(0);
|
|
|
|
_mEventMgr = new PhysXEventManager();
|
|
|
|
physx::PxSceneDesc sceneDesc(_mPhysics->getTolerancesScale());
|
|
sceneDesc.gravity = physx::PxVec3(0.0F, -10.0F, 0.0F);
|
|
sceneDesc.cpuDispatcher = _mDispatcher;
|
|
sceneDesc.kineKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
|
|
sceneDesc.staticKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
|
|
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
|
|
sceneDesc.filterShader = simpleFilterShader;
|
|
sceneDesc.simulationEventCallback = &_mEventMgr->getEventCallback();
|
|
_mScene = _mPhysics->createScene(sceneDesc);
|
|
|
|
_mCollisionMatrix[0] = 1;
|
|
|
|
createMaterial(0, 0.6F, 0.6F, 0.1F, 2, 2);
|
|
}
|
|
|
|
PhysXWorld::~PhysXWorld() {
|
|
auto &materialMap = getPxMaterialMap();
|
|
// clear material cache
|
|
materialMap.clear();
|
|
delete _mEventMgr;
|
|
PhysXJoint::releaseTempRigidActor();
|
|
PX_RELEASE(_mScene);
|
|
PX_RELEASE(_mDispatcher);
|
|
PX_RELEASE(_mPhysics);
|
|
#ifdef CC_DEBUG
|
|
physx::PxPvdTransport *transport = _mPvd->getTransport();
|
|
PX_RELEASE(_mPvd);
|
|
PX_RELEASE(transport);
|
|
#endif
|
|
// release cooking before foundation
|
|
PX_RELEASE(_mCooking);
|
|
PxCloseExtensions();
|
|
PX_RELEASE(_mFoundation);
|
|
}
|
|
|
|
void PhysXWorld::step(float fixedTimeStep) {
|
|
_mScene->simulate(fixedTimeStep);
|
|
_mScene->fetchResults(true);
|
|
syncPhysicsToScene();
|
|
}
|
|
|
|
void PhysXWorld::setGravity(float x, float y, float z) {
|
|
_mScene->setGravity(physx::PxVec3(x, y, z));
|
|
}
|
|
|
|
void PhysXWorld::destroy() {
|
|
}
|
|
|
|
void PhysXWorld::setCollisionMatrix(uint32_t index, uint32_t mask) {
|
|
if (index > 31) return;
|
|
_mCollisionMatrix[index] = mask;
|
|
}
|
|
|
|
uintptr_t PhysXWorld::createConvex(ConvexDesc &desc) {
|
|
physx::PxConvexMeshDesc convexDesc;
|
|
convexDesc.points.count = desc.positionLength;
|
|
convexDesc.points.stride = sizeof(physx::PxVec3);
|
|
convexDesc.points.data = static_cast<physx::PxVec3 *>(desc.positions);
|
|
convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
|
|
physx::PxConvexMesh *convexMesh = getCooking().createConvexMesh(convexDesc, PxGetPhysics().getPhysicsInsertionCallback());
|
|
return reinterpret_cast<uintptr_t>(convexMesh);
|
|
}
|
|
|
|
uintptr_t PhysXWorld::createTrimesh(TrimeshDesc &desc) {
|
|
physx::PxTriangleMeshDesc meshDesc;
|
|
meshDesc.points.count = desc.positionLength;
|
|
meshDesc.points.stride = sizeof(physx::PxVec3);
|
|
meshDesc.points.data = static_cast<physx::PxVec3 *>(desc.positions);
|
|
meshDesc.triangles.count = desc.triangleLength;
|
|
if (desc.isU16) {
|
|
meshDesc.triangles.stride = 3 * sizeof(physx::PxU16);
|
|
meshDesc.triangles.data = static_cast<physx::PxU16 *>(desc.triangles);
|
|
meshDesc.flags = physx::PxMeshFlag::e16_BIT_INDICES;
|
|
} else {
|
|
meshDesc.triangles.stride = 3 * sizeof(physx::PxU32);
|
|
meshDesc.triangles.data = static_cast<physx::PxU32 *>(desc.triangles);
|
|
}
|
|
physx::PxTriangleMesh *triangleMesh = getCooking().createTriangleMesh(meshDesc, PxGetPhysics().getPhysicsInsertionCallback());
|
|
return reinterpret_cast<uintptr_t>(triangleMesh);
|
|
}
|
|
|
|
uintptr_t PhysXWorld::createHeightField(HeightFieldDesc &desc) {
|
|
const auto rows = desc.rows;
|
|
const auto columns = desc.columns;
|
|
const physx::PxU32 counts = rows * columns;
|
|
auto * samples = new physx::PxHeightFieldSample[counts];
|
|
for (physx::PxU32 r = 0; r < rows; r++) {
|
|
for (physx::PxU32 c = 0; c < columns; c++) {
|
|
const auto index = c + r * columns;
|
|
auto v = (static_cast<int16_t *>(desc.samples))[index];
|
|
samples[index].height = v;
|
|
}
|
|
}
|
|
physx::PxHeightFieldDesc hfDesc;
|
|
hfDesc.nbRows = rows;
|
|
hfDesc.nbColumns = columns;
|
|
hfDesc.samples.data = samples;
|
|
hfDesc.samples.stride = sizeof(physx::PxHeightFieldSample);
|
|
physx::PxHeightField *hf = getCooking().createHeightField(hfDesc, PxGetPhysics().getPhysicsInsertionCallback());
|
|
delete[] samples;
|
|
return reinterpret_cast<uintptr_t>(hf);
|
|
}
|
|
|
|
uintptr_t PhysXWorld::createMaterial(uint16_t id, float f, float df, float r,
|
|
uint8_t m0, uint8_t m1) {
|
|
physx::PxMaterial *mat;
|
|
auto & m = getPxMaterialMap();
|
|
if (m.find(id) == m.end()) {
|
|
mat = PxGetPhysics().createMaterial(f, df, r);
|
|
// add reference count avoid auto releasing by physx
|
|
mat->acquireReference();
|
|
m[id] = reinterpret_cast<uintptr_t>(mat);
|
|
mat->setFrictionCombineMode(physx::PxCombineMode::Enum(m0));
|
|
mat->setRestitutionCombineMode(physx::PxCombineMode::Enum(m1));
|
|
} else {
|
|
mat = reinterpret_cast<physx::PxMaterial *>(m[id]);
|
|
mat->setStaticFriction(f);
|
|
mat->setDynamicFriction(df);
|
|
mat->setRestitution(r);
|
|
mat->setFrictionCombineMode(physx::PxCombineMode::Enum(m0));
|
|
mat->setRestitutionCombineMode(physx::PxCombineMode::Enum(m1));
|
|
}
|
|
return uintptr_t(mat);
|
|
}
|
|
|
|
void PhysXWorld::emitEvents() {
|
|
_mEventMgr->refreshPairs();
|
|
}
|
|
|
|
void PhysXWorld::syncSceneToPhysics() {
|
|
for (auto const &sb : _mSharedBodies) {
|
|
sb->syncSceneToPhysics();
|
|
}
|
|
}
|
|
|
|
uint32_t PhysXWorld::getMaskByIndex(uint32_t i) {
|
|
if (i > 31) i = 0;
|
|
return _mCollisionMatrix[i];
|
|
}
|
|
|
|
void PhysXWorld::syncPhysicsToScene() {
|
|
for (auto const &sb : _mSharedBodies) {
|
|
sb->syncPhysicsToScene();
|
|
}
|
|
}
|
|
|
|
void PhysXWorld::syncSceneWithCheck() {
|
|
for (auto const &sb : _mSharedBodies) {
|
|
sb->syncSceneWithCheck();
|
|
}
|
|
}
|
|
|
|
void PhysXWorld::setAllowSleep(bool val) {
|
|
}
|
|
|
|
void PhysXWorld::addActor(const PhysXSharedBody &sb) {
|
|
auto beg = _mSharedBodies.begin();
|
|
auto end = _mSharedBodies.end();
|
|
auto iter = find(beg, end, &sb);
|
|
if (iter == end) {
|
|
_mScene->addActor(*(const_cast<PhysXSharedBody &>(sb).getImpl().rigidActor));
|
|
_mSharedBodies.push_back(&const_cast<PhysXSharedBody &>(sb));
|
|
}
|
|
}
|
|
|
|
void PhysXWorld::removeActor(const PhysXSharedBody &sb) {
|
|
auto beg = _mSharedBodies.begin();
|
|
auto end = _mSharedBodies.end();
|
|
auto iter = find(beg, end, &sb);
|
|
if (iter != end) {
|
|
_mScene->removeActor(*(const_cast<PhysXSharedBody &>(sb).getImpl().rigidActor), true);
|
|
_mSharedBodies.erase(iter);
|
|
}
|
|
}
|
|
|
|
bool PhysXWorld::raycast(RaycastOptions &opt) {
|
|
physx::PxQueryCache *cache = nullptr;
|
|
const auto o = opt.origin;
|
|
const auto ud = opt.unitDir;
|
|
physx::PxVec3 origin{o.x, o.y, o.z};
|
|
physx::PxVec3 unitDir{ud.x, ud.y, ud.z};
|
|
unitDir.normalize();
|
|
physx::PxHitFlags flags = physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL;
|
|
physx::PxSceneQueryFilterData filterData;
|
|
filterData.data.word0 = opt.mask;
|
|
filterData.data.word3 = QUERY_FILTER | (opt.queryTrigger ? 0 : QUERY_CHECK_TRIGGER);
|
|
filterData.flags = physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::ePREFILTER;
|
|
auto & hitBuffer = getPxRaycastHitBuffer();
|
|
bool result = false;
|
|
const auto nbTouches = physx::PxSceneQueryExt::raycastMultiple(
|
|
getScene(), origin, unitDir, opt.distance, flags, hitBuffer.data(),
|
|
static_cast<physx::PxU32>(hitBuffer.size()), result, filterData, &getQueryFilterShader(), cache);
|
|
if (nbTouches == 0 || nbTouches == -1) return false;
|
|
auto &r = raycastResult();
|
|
r.resize(nbTouches);
|
|
for (physx::PxI32 i = 0; i < nbTouches; i++) {
|
|
const auto &shapeIter = getPxShapeMap().find(reinterpret_cast<uintptr_t>(hitBuffer[i].shape));
|
|
if (shapeIter == getPxShapeMap().end()) return false;
|
|
r[i].shape = shapeIter->second;
|
|
r[i].distance = hitBuffer[i].distance;
|
|
pxSetVec3Ext(r[i].hitNormal, hitBuffer[i].normal);
|
|
pxSetVec3Ext(r[i].hitPoint, hitBuffer[i].position);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<RaycastResult> &PhysXWorld::raycastResult() {
|
|
static std::vector<RaycastResult> hits;
|
|
return hits;
|
|
}
|
|
|
|
bool PhysXWorld::raycastClosest(RaycastOptions &opt) {
|
|
physx::PxRaycastHit hit;
|
|
physx::PxQueryCache *cache = nullptr;
|
|
const auto o = opt.origin;
|
|
const auto ud = opt.unitDir;
|
|
physx::PxVec3 origin{o.x, o.y, o.z};
|
|
physx::PxVec3 unitDir{ud.x, ud.y, ud.z};
|
|
unitDir.normalize();
|
|
physx::PxHitFlags flags = physx::PxHitFlag::ePOSITION | physx::PxHitFlag::eNORMAL;
|
|
physx::PxSceneQueryFilterData filterData;
|
|
filterData.data.word0 = opt.mask;
|
|
filterData.data.word3 = QUERY_FILTER | (opt.queryTrigger ? 0 : QUERY_CHECK_TRIGGER) | QUERY_SINGLE_HIT;
|
|
filterData.flags = physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::ePREFILTER;
|
|
const auto result = physx::PxSceneQueryExt::raycastSingle(
|
|
getScene(), origin, unitDir, opt.distance, flags,
|
|
hit, filterData, &getQueryFilterShader(), cache);
|
|
if (result) {
|
|
auto & r = raycastClosestResult();
|
|
const auto &shapeIter = getPxShapeMap().find(reinterpret_cast<uintptr_t>(hit.shape));
|
|
if (shapeIter == getPxShapeMap().end()) return false;
|
|
r.shape = shapeIter->second;
|
|
r.distance = hit.distance;
|
|
pxSetVec3Ext(r.hitPoint, hit.position);
|
|
pxSetVec3Ext(r.hitNormal, hit.normal);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
RaycastResult &PhysXWorld::raycastClosestResult() {
|
|
static RaycastResult hit;
|
|
return hit;
|
|
}
|
|
|
|
} // namespace physics
|
|
} // namespace cc
|