int32 GenerateBoxAsSimpleCollision(UStaticMesh* StaticMesh) { if (!PromptToRemoveExistingCollision(StaticMesh)) { return INDEX_NONE; } UBodySetup* bs = StaticMesh->BodySetup; // Calculate bounding Box. FRawMesh RawMesh; FStaticMeshSourceModel& SrcModel = StaticMesh->SourceModels[0]; SrcModel.RawMeshBulkData->LoadRawMesh(RawMesh); FVector unitVec = bs->BuildScale3D; FVector Center, Extents; CalcBoundingBox(RawMesh, Center, Extents, unitVec); bs->Modify(); // Create new GUID bs->InvalidatePhysicsData(); FKBoxElem BoxElem; BoxElem.Center = Center; BoxElem.X = Extents.X * 2.0f; BoxElem.Y = Extents.Y * 2.0f; BoxElem.Z = Extents.Z * 2.0f; bs->AggGeom.BoxElems.Add(BoxElem); // refresh collision change back to staticmesh components RefreshCollisionChange(StaticMesh); // Mark staticmesh as dirty, to help make sure it gets saved. StaticMesh->MarkPackageDirty(); StaticMesh->bCustomizedCollision = true; //mark the static mesh for collision customization return bs->AggGeom.BoxElems.Num() - 1; }
void AddConvexElemsToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); for (int32 i = 0; i < BodySetup->AggGeom.ConvexElems.Num(); i++) { const FKConvexElem& ConvexElem = BodySetup->AggGeom.ConvexElems[i]; if (ConvexElem.ConvexMesh) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); PxConvexMeshGeometry PConvexGeom; PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh; PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs()); FTransform ConvexTransform = ConvexElem.GetTransform(); if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0) { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(BodySetup->GetOuter()), i); } if (ConvexTransform.IsValid()) { PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM); PLocalPose.q *= PElementTransform.q; PLocalPose.p = PElementTransform.p; PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; if (PConvexGeom.isValid()) { PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents(); ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement()); AttachShape_AssumesLocked(PConvexGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *BodySetup->GetPathName()); } } }
void AddSphylsToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); float ScaleRadius = FMath::Max(Scale3DAbs.X, Scale3DAbs.Y); float ScaleLength = Scale3DAbs.Z; for (int32 i = 0; i < BodySetup->AggGeom.SphylElems.Num(); i++) { const FKSphylElem& SphylElem = BodySetup->AggGeom.SphylElems[i]; // this is a bit confusing since radius and height is scaled // first apply the scale first float Radius = FMath::Max(SphylElem.Radius * ScaleRadius, 0.1f); float Length = SphylElem.Length + SphylElem.Radius * 2.f; float HalfLength = Length * ScaleLength * 0.5f; Radius = FMath::Clamp(Radius, 0.1f, HalfLength); //radius is capped by half length float HalfHeight = HalfLength - Radius; HalfHeight = FMath::Max(0.1f, HalfHeight); PxCapsuleGeometry PCapsuleGeom; PCapsuleGeom.halfHeight = HalfHeight; PCapsuleGeom.radius = Radius; if (PCapsuleGeom.isValid()) { // The stored sphyl transform assumes the sphyl axis is down Z. In PhysX, it points down X, so we twiddle the matrix a bit here (swap X and Z and negate Y). PxTransform PLocalPose(U2PVector(RelativeTM.TransformPosition(SphylElem.Center)), U2PQuat(SphylElem.Orientation) * U2PSphylBasis); PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PCapsuleGeom.radius); AttachShape_AssumesLocked(PCapsuleGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddSphylsToRigidActor: [%s] SphylElems[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } }
void AddBoxesToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); for (int32 i = 0; i < BodySetup->AggGeom.BoxElems.Num(); i++) { const FKBoxElem& BoxElem = BodySetup->AggGeom.BoxElems[i]; PxBoxGeometry PBoxGeom; PBoxGeom.halfExtents.x = (0.5f * BoxElem.X * Scale3DAbs.X); PBoxGeom.halfExtents.y = (0.5f * BoxElem.Y * Scale3DAbs.Y); PBoxGeom.halfExtents.z = (0.5f * BoxElem.Z * Scale3DAbs.Z); FTransform BoxTransform = BoxElem.GetTransform() * RelativeTM; if (PBoxGeom.isValid() && BoxTransform.IsValid()) { PxTransform PLocalPose(U2PTransform(BoxTransform)); PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoxGeom.halfExtents.minElement()); AttachShape_AssumesLocked(PBoxGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddBoxesToRigidActor: [%s] BoxElems[%d] invalid or has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } }
void AddSpheresToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); for (int32 i = 0; i < BodySetup->AggGeom.SphereElems.Num(); i++) { const FKSphereElem& SphereElem = BodySetup->AggGeom.SphereElems[i]; PxSphereGeometry PSphereGeom; PSphereGeom.radius = (SphereElem.Radius * MinScaleAbs); if (ensure(PSphereGeom.isValid())) { FVector LocalOrigin = RelativeTM.TransformPosition(SphereElem.Center); PxTransform PLocalPose(U2PVector(LocalOrigin)); PLocalPose.p *= MinScale; ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PSphereGeom.radius); AttachShape_AssumesLocked(PSphereGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddSpheresToRigidActor: [%s] SphereElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } }
int32 GenerateKDopAsSimpleCollision(UStaticMesh* StaticMesh, const TArray<FVector> &Dirs) { // Make sure rendering is done - so we are not changing data being used by collision drawing. FlushRenderingCommands(); if (!PromptToRemoveExistingCollision(StaticMesh)) { return INDEX_NONE; } UBodySetup* bs = StaticMesh->BodySetup; // Do k- specific stuff. int32 kCount = Dirs.Num(); TArray<float> maxDist; for(int32 i=0; i<kCount; i++) maxDist.Add(-MY_FLTMAX); // Construct temporary UModel for kdop creation. We keep no refs to it, so it can be GC'd. auto TempModel = NewObject<UModel>(); TempModel->Initialize(nullptr, 1); // For each vertex, project along each kdop direction, to find the max in that direction. const FStaticMeshLODResources& RenderData = StaticMesh->RenderData->LODResources[0]; for(int32 i=0; i<RenderData.GetNumVertices(); i++) { for(int32 j=0; j<kCount; j++) { float dist = RenderData.PositionVertexBuffer.VertexPosition(i) | Dirs[j]; maxDist[j] = FMath::Max(dist, maxDist[j]); } } // Inflate kdop to ensure it is no degenerate const float MinSize = 0.1f; for(int32 i=0; i<kCount; i++) { maxDist[i] += MinSize; } // Now we have the planes of the kdop, we work out the face polygons. TArray<FPlane> planes; for(int32 i=0; i<kCount; i++) planes.Add( FPlane(Dirs[i], maxDist[i]) ); for(int32 i=0; i<planes.Num(); i++) { FPoly* Polygon = new(TempModel->Polys->Element) FPoly(); FVector Base, AxisX, AxisY; Polygon->Init(); Polygon->Normal = planes[i]; Polygon->Normal.FindBestAxisVectors(AxisX,AxisY); Base = planes[i] * planes[i].W; new(Polygon->Vertices) FVector(Base + AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX); new(Polygon->Vertices) FVector(Base + AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX); new(Polygon->Vertices) FVector(Base - AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX); new(Polygon->Vertices) FVector(Base - AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX); for(int32 j=0; j<planes.Num(); j++) { if(i != j) { if(!Polygon->Split(-FVector(planes[j]), planes[j] * planes[j].W)) { Polygon->Vertices.Empty(); break; } } } if(Polygon->Vertices.Num() < 3) { // If poly resulted in no verts, remove from array TempModel->Polys->Element.RemoveAt(TempModel->Polys->Element.Num()-1); } else { // Other stuff... Polygon->iLink = i; Polygon->CalcNormal(1); } } if(TempModel->Polys->Element.Num() < 4) { TempModel = NULL; return INDEX_NONE; } // Build bounding box. TempModel->BuildBound(); // Build BSP for the brush. FBSPOps::bspBuild(TempModel,FBSPOps::BSP_Good,15,70,1,0); FBSPOps::bspRefresh(TempModel,1); FBSPOps::bspBuildBounds(TempModel); bs->Modify(); bs->CreateFromModel(TempModel, false); // create all body instances RefreshCollisionChange(StaticMesh); // Mark staticmesh as dirty, to help make sure it gets saved. StaticMesh->MarkPackageDirty(); return bs->AggGeom.ConvexElems.Num() - 1; }
void AddTriMeshToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); if (BodySetup->TriMesh || BodySetup->TriMeshNegX) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); // Only case where TriMeshNegX should be null is BSP, which should not require negX version if (bUseNegX && BodySetup->TriMeshNegX == NULL) { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: Want to use NegX but it doesn't exist! %s"), *BodySetup->GetPathName()); } PxTriangleMesh* UseTriMesh = bUseNegX ? BodySetup->TriMeshNegX : BodySetup->TriMesh; if (UseTriMesh != NULL) { PxTriangleMeshGeometry PTriMeshGeom; PTriMeshGeom.triangleMesh = bUseNegX ? BodySetup->TriMeshNegX : BodySetup->TriMesh; PTriMeshGeom.scale.scale = U2PVector(Scale3DAbs); if (BodySetup->bDoubleSidedGeometry) { PTriMeshGeom.meshFlags |= PxMeshGeometryFlag::eDOUBLE_SIDED; } if (PTriMeshGeom.isValid()) { ensure(PLocalPose.isValid()); // Create without 'sim shape' flag, problematic if it's kinematic, and it gets set later anyway. { if (!AttachShape_AssumesLocked(PTriMeshGeom, PLocalPose, MaxContactOffset, PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eVISUALIZATION)) { UE_LOG(LogPhysics, Log, TEXT("Can't create new mesh shape in AddShapesToRigidActor")); } } } else { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: TriMesh invalid")); } } } }