/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include namespace PhysX { namespace ShapeConstants { // 48 is the number of stacks/slices used when generating mesh geo for spheres in legacy physics // we default to these values for consistency constexpr size_t NumStacks = 48; constexpr size_t NumSlices = 48; } Shape::Shape(Shape&& shape) : m_pxShape(AZStd::move(shape.m_pxShape)) , m_materials(AZStd::move(shape.m_materials)) , m_collisionLayer(AZStd::move(shape.m_collisionLayer)) , m_collisionGroup(AZStd::move(shape.m_collisionGroup)) { if (m_pxShape) { m_pxShape->userData = this; } } Shape& Shape::operator=(Shape&& shape) { m_pxShape = AZStd::move(shape.m_pxShape); m_materials = AZStd::move(shape.m_materials); m_collisionLayer = AZStd::move(shape.m_collisionLayer); m_collisionGroup = AZStd::move(shape.m_collisionGroup); if (m_pxShape) { m_pxShape->userData = this; } return *this; } void Shape::ReleasePxShape(physx::PxShape* shape) { if (shape != nullptr) { PHYSX_SCENE_WRITE_LOCK(GetScene()); shape->userData = nullptr; shape->release(); } } Shape::Shape(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration) : m_collisionLayer(colliderConfiguration.m_collisionLayer) { if (physx::PxShape* newShape = Utils::CreatePxShapeFromConfig(colliderConfiguration, shapeConfiguration, m_collisionGroup)) { m_pxShape = PxShapeUniquePtr(newShape, AZStd::bind(&Shape::ReleasePxShape, this, newShape)); m_pxShape->userData = this; ExtractMaterialsFromPxShape(); m_tag = AZ::Crc32(colliderConfiguration.m_tag); } } Shape::Shape(physx::PxShape* nativeShape) { m_pxShape = PxShapeUniquePtr(nativeShape, AZStd::bind(&Shape::ReleasePxShape, this, nativeShape)); m_pxShape->acquireReference(); m_pxShape->userData = this; ExtractMaterialsFromPxShape(); } Shape::~Shape() { //release the shape here, so when Shape::ReleasePxShape is called to delete the physx::PxShape* we can still acquire the scene lock. m_pxShape.reset(); m_pxShape = nullptr; m_attachedActor = nullptr; } physx::PxShape* Shape::GetPxShape() { if (m_pxShape) { return m_pxShape.get(); } return nullptr; } void Shape::SetMaterial(const AZStd::shared_ptr& material) { if (auto materialWrapper = AZStd::rtti_pointer_cast(material)) { m_materials.clear(); m_materials.emplace_back(materialWrapper); BindMaterialsWithPxShape(); } else { AZ_Warning("PhysX Shape", false, "Trying to assign material of unknown type"); } } AZStd::shared_ptr Shape::GetMaterial() const { if (!m_materials.empty()) { return m_materials[0]; } return nullptr; } void Shape::SetMaterials(const AZStd::vector>& materials) { m_materials = materials; BindMaterialsWithPxShape(); } void Shape::BindMaterialsWithPxShape() { if (m_pxShape) { AZStd::vector pxMaterials; pxMaterials.reserve(m_materials.size()); for (const auto& material : m_materials) { pxMaterials.emplace_back(material->GetPxMaterial()); } AZ_Warning("PhysX Shape", m_materials.size() < std::numeric_limits::max(), "Trying to assign too many materials, cutting down"); size_t materialsCount = AZStd::GetMin(m_materials.size(), static_cast(std::numeric_limits::max())); m_pxShape->setMaterials(&pxMaterials[0], static_cast(materialsCount)); } } void Shape::ExtractMaterialsFromPxShape() { if (m_pxShape == nullptr) { return; } const int BufferSize = 100; AZ_Warning("PhysX Shape", m_pxShape->getNbMaterials() < BufferSize, "Shape has too many materials, consider increasing the buffer"); physx::PxMaterial* assignedMaterials[BufferSize]; int materialsCount = m_pxShape->getMaterials(assignedMaterials, BufferSize, 0); m_materials.clear(); m_materials.reserve(materialsCount); for (int i = 0; i < materialsCount; ++i) { if (assignedMaterials[i]->userData == nullptr) { AZ_Warning("PhysX Shape", false, "Trying to assign material with no user data. Make sure you are creating materials using MaterialManager"); continue; } m_materials.push_back(static_cast(PhysX::Utils::GetUserData(assignedMaterials[i]))->shared_from_this()); } } const AZStd::vector>& Shape::GetMaterials() { return m_materials; } void Shape::SetCollisionLayer(const Physics::CollisionLayer& layer) { m_collisionLayer = layer; PHYSX_SCENE_WRITE_LOCK(GetScene()); physx::PxFilterData filterData = m_pxShape->getSimulationFilterData(); Collision::SetLayer(layer, filterData); m_pxShape->setSimulationFilterData(filterData); m_pxShape->setQueryFilterData(filterData); } Physics::CollisionLayer Shape::GetCollisionLayer() const { return m_collisionLayer; } void Shape::SetCollisionGroup(const Physics::CollisionGroup& group) { m_collisionGroup = group; PHYSX_SCENE_WRITE_LOCK(GetScene()); physx::PxFilterData filterData = m_pxShape->getSimulationFilterData(); Collision::SetGroup(m_collisionGroup, filterData); m_pxShape->setSimulationFilterData(filterData); m_pxShape->setQueryFilterData(filterData); } Physics::CollisionGroup Shape::GetCollisionGroup() const { return m_collisionGroup; } void Shape::SetName(const char* name) { if (m_pxShape) { m_pxShape->setName(name); } } void Shape::SetLocalPose(const AZ::Vector3& offset, const AZ::Quaternion& rotation) { PHYSX_SCENE_WRITE_LOCK(GetScene()); physx::PxTransform pxShapeTransform = PxMathConvert(offset, rotation); AZ_Warning("Physics::Shape", m_pxShape->isExclusive(), "Non-exclusive shapes are not mutable after they're attached to a body."); if (m_pxShape->getGeometryType() == physx::PxGeometryType::eCAPSULE) { physx::PxQuat lyToPxRotation(AZ::Constants::HalfPi, physx::PxVec3(0.0f, 1.0f, 0.0f)); pxShapeTransform.q *= lyToPxRotation; } m_pxShape->setLocalPose(pxShapeTransform); } AZStd::pair Shape::GetLocalPose() const { PHYSX_SCENE_READ_LOCK(GetScene()); physx::PxTransform pose = m_pxShape->getLocalPose(); return { PxMathConvert(pose.p), PxMathConvert(pose.q) }; } float Shape::GetRestOffset() const { return m_pxShape->getRestOffset(); } float Shape::GetContactOffset() const { return m_pxShape->getContactOffset(); } void Shape::SetRestOffset(float restOffset) { float contactOffset = GetContactOffset(); if (restOffset >= contactOffset) { AZ_Error("PhysX Shape", false, "Requested rest offset (%e) must be less than contact offset (%e).", restOffset, contactOffset); return; } m_pxShape->setRestOffset(restOffset); } void Shape::SetContactOffset(float contactOffset) { if (contactOffset <= 0.0f) { AZ_Error("PhysX Shape", false, "Requested contact offset (%e) must exceed 0."); return; } float restOffset = GetRestOffset(); if (contactOffset <= restOffset) { AZ_Error("PhysX Shape", false, "Requested contact offset (%e) must exceed rest offset (%e).", contactOffset, restOffset); return; } m_pxShape->setContactOffset(contactOffset); } void* Shape::GetNativePointer() { return m_pxShape.get(); } AZ::Crc32 Shape::GetTag() const { return m_tag; } bool Shape::IsTrigger() const { if (m_pxShape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE) { return true; } return false; } void Shape::AttachedToActor(void* actor) { physx::PxActor* pxActor = static_cast(actor); if (pxActor != nullptr) { m_attachedActor = pxActor; } } void Shape::DetachedFromActor() { m_attachedActor = nullptr; } Physics::RayCastHit Shape::RayCastInternal(const Physics::RayCastRequest& worldSpaceRequest, const physx::PxTransform& pose) { if (const bool shouldCollide = worldSpaceRequest.m_collisionGroup.GetMask() & m_collisionLayer.GetMask(); !shouldCollide) { return Physics::RayCastHit(); } const physx::PxVec3 start = PxMathConvert(worldSpaceRequest.m_start); const physx::PxVec3 unitDir = PxMathConvert(worldSpaceRequest.m_direction); const physx::PxU32 maxHits = 1; const physx::PxHitFlags hitFlags = Utils::RayCast::GetPxHitFlags(worldSpaceRequest.m_hitFlags); physx::PxRaycastHit hitInfo; const bool hit = physx::PxGeometryQuery::raycast(start, unitDir, m_pxShape->getGeometry().any(), pose, worldSpaceRequest.m_distance, hitFlags, maxHits, &hitInfo); if (hit) { // Fill actor and shape, as they won't be filled from PxGeometryQuery hitInfo.actor = static_cast(m_attachedActor); // This cast is safe since GetHitFromPxHit() only uses PxActor:: functions hitInfo.shape = GetPxShape(); return Utils::RayCast::GetHitFromPxHit(hitInfo); } return Physics::RayCastHit(); } Physics::RayCastHit Shape::RayCast(const Physics::RayCastRequest& worldSpaceRequest, const AZ::Transform& worldTransform) { physx::PxTransform localPose; { PHYSX_SCENE_READ_LOCK(GetScene()); localPose = m_pxShape->getLocalPose(); } return RayCastInternal(worldSpaceRequest, PxMathConvert(worldTransform) * localPose); } Physics::RayCastHit Shape::RayCastLocal(const Physics::RayCastRequest& localSpaceRequest) { physx::PxTransform localPose; { PHYSX_SCENE_READ_LOCK(GetScene()); localPose = m_pxShape->getLocalPose(); } return RayCastInternal(localSpaceRequest, localPose); } AZ::Aabb Shape::GetAabb(const AZ::Transform& worldTransform) const { physx::PxTransform localPose; { PHYSX_SCENE_READ_LOCK(GetScene()); localPose = m_pxShape->getLocalPose(); } return PxMathConvert(physx::PxGeometryQuery::getWorldBounds(m_pxShape->getGeometry().any(), PxMathConvert(worldTransform) * localPose, 1.0f)); } AZ::Aabb Shape::GetAabbLocal() const { physx::PxTransform localPose; { PHYSX_SCENE_READ_LOCK(GetScene()); localPose = m_pxShape->getLocalPose(); } return PxMathConvert(physx::PxGeometryQuery::getWorldBounds(m_pxShape->getGeometry().any(), localPose, 1.0f)); } physx::PxScene* Shape::GetScene() const { if (m_attachedActor != nullptr) { return m_attachedActor->getScene(); } return nullptr; } void Shape::GetGeometry(AZStd::vector& vertices, AZStd::vector& indices, AZ::Aabb* optionalBounds) { if (!m_pxShape) { return; } PHYSX_SCENE_READ_LOCK(GetScene()); if (m_pxShape->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH) { physx::PxTriangleMeshGeometry geometry{}; if (m_pxShape->getTriangleMeshGeometry(geometry) && geometry.triangleMesh && geometry.isValid()) { Utils::Geometry::GetTriangleMeshGeometry(geometry, vertices, indices); } } else if (m_pxShape->getGeometryType() == physx::PxGeometryType::eCONVEXMESH) { physx::PxConvexMeshGeometry geometry{}; if (m_pxShape->getConvexMeshGeometry(geometry) && geometry.convexMesh && geometry.isValid()) { Utils::Geometry::GetConvexMeshGeometry(geometry, vertices, indices); } } else if (m_pxShape->getGeometryType() == physx::PxGeometryType::eHEIGHTFIELD) { physx::PxHeightFieldGeometry geometry{}; if (m_pxShape->getHeightFieldGeometry(geometry) && geometry.heightField && geometry.isValid()) { Utils::Geometry::GetHeightFieldGeometry(geometry, vertices, indices, optionalBounds); } } else if (m_pxShape->getGeometryType() == physx::PxGeometryType::eBOX) { physx::PxBoxGeometry geometry{}; if (m_pxShape->getBoxGeometry(geometry) && geometry.isValid()) { Utils::Geometry::GetBoxGeometry(geometry, vertices, indices); } } else if (m_pxShape->getGeometryType() == physx::PxGeometryType::eSPHERE) { physx::PxSphereGeometry geometry{}; if (m_pxShape->getSphereGeometry(geometry) && geometry.isValid()) { Utils::Geometry::GetSphereGeometry(geometry, vertices, indices, ShapeConstants::NumStacks, ShapeConstants::NumSlices); } } else if (m_pxShape->getGeometryType() == physx::PxGeometryType::eCAPSULE) { physx::PxCapsuleGeometry geometry{}; if (m_pxShape->getCapsuleGeometry(geometry) && geometry.isValid()) { Utils::Geometry::GetCapsuleGeometry(geometry, vertices, indices, ShapeConstants::NumStacks, ShapeConstants::NumSlices); } } else { AZ_TracePrintf("Shape", "GetGeometry for PxGeometryType %d is not supported", static_cast(m_pxShape->getGeometryType())); } } }