bool FConvexVolume::ClipPolygon(FPoly& Polygon) const { for(int32 PlaneIndex = 0;PlaneIndex < Planes.Num();PlaneIndex++) { const FPlane& Plane = Planes[PlaneIndex]; if(!Polygon.Split(-FVector(Plane),Plane * Plane.W)) return 0; } return 1; }
bool FKConvexElem::HullFromPlanes(const TArray<FPlane>& InPlanes, const TArray<FVector>& SnapVerts) { // Start by clearing this convex. Reset(); float TotalPolyArea = 0; for(int32 i=0; i<InPlanes.Num(); i++) { FPoly Polygon; Polygon.Normal = InPlanes[i]; FVector AxisX, AxisY; Polygon.Normal.FindBestAxisVectors(AxisX,AxisY); const FVector Base = InPlanes[i] * InPlanes[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<InPlanes.Num(); j++) { if(i != j) { if(!Polygon.Split(-FVector(InPlanes[j]), InPlanes[j] * InPlanes[j].W)) { Polygon.Vertices.Empty(); break; } } } // Do nothing if poly was completely clipped away. if(Polygon.Vertices.Num() > 0) { TotalPolyArea += Polygon.Area(); // Add vertices of polygon to convex primitive. for(int32 j=0; j<Polygon.Vertices.Num(); j++) { // We try and snap the vert to on of the ones supplied. int32 NearestVert = INDEX_NONE; float NearestDistSqr = BIG_NUMBER; for(int32 k=0; k<SnapVerts.Num(); k++) { const float DistSquared = (Polygon.Vertices[j] - SnapVerts[k]).SizeSquared(); if( DistSquared < NearestDistSqr ) { NearestVert = k; NearestDistSqr = DistSquared; } } // If we have found a suitably close vertex, use that if( NearestVert != INDEX_NONE && NearestDistSqr < LOCAL_EPS ) { const FVector localVert = SnapVerts[NearestVert]; AddVertexIfNotPresent(VertexData, localVert); } else { const FVector localVert = Polygon.Vertices[j]; AddVertexIfNotPresent(VertexData, localVert); } } } } // If the collision volume isn't closed, return an error so the model can be discarded if(TotalPolyArea < 0.001f) { UE_LOG(LogPhysics, Log, TEXT("Total Polygon Area invalid: %f"), TotalPolyArea ); return false; } // We need at least 4 vertices to make a convex hull with non-zero volume. // We shouldn't have the same vertex multiple times (using AddVertexIfNotPresent above) if(VertexData.Num() < 4) { return true; } // Check that not all vertices lie on a line (ie. find plane) // Again, this should be a non-zero vector because we shouldn't have duplicate verts. bool bFound = false; FVector Dir2, Dir1; Dir1 = VertexData[1] - VertexData[0]; Dir1.Normalize(); for(int32 i=2; i<VertexData.Num() && !bFound; i++) { Dir2 = VertexData[i] - VertexData[0]; Dir2.Normalize(); // If line are non-parallel, this vertex forms our plane if((Dir1 | Dir2) < (1.f - LOCAL_EPS)) { bFound = true; } } if(!bFound) { return true; } // Now we check that not all vertices lie on a plane, by checking at least one lies off the plane we have formed. FVector Normal = Dir1 ^ Dir2; Normal.Normalize(); const FPlane Plane(VertexData[0], Normal); bFound = false; for(int32 i=2; i<VertexData.Num() ; i++) { if(Plane.PlaneDot(VertexData[i]) > LOCAL_EPS) { bFound = true; break; } } // If we did not find a vert off the line - discard this hull. if(!bFound) { return true; } // calc bounding box of verts UpdateElemBox(); // We can continue adding primitives (mesh is not horribly broken) return true; }
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; }