Files
cocos-engine/native/cocos/physics/physx/PhysXWorld.cpp
2022-01-24 17:23:03 +08:00

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