void DecomposeUCXMesh( const TArray<FVector>& CollisionVertices, const TArray<int32>& CollisionFaceIdx, UBodySetup* BodySetup ) { // We keep no ref to this Model, so it will be GC'd at some point after the import. auto TempModel = NewObject<UModel>(); TempModel->Initialize(nullptr, 1); FMeshConnectivityBuilder ConnectivityBuilder; // Send triangles to connectivity builder for(int32 x = 0; x < CollisionFaceIdx.Num(); x += 3) { const FVector &VertexA = CollisionVertices[ CollisionFaceIdx[x + 2] ]; const FVector &VertexB = CollisionVertices[ CollisionFaceIdx[x + 1] ]; const FVector &VertexC = CollisionVertices[ CollisionFaceIdx[x + 0] ]; ConnectivityBuilder.AddTriangle( VertexA, VertexB, VertexC ); } ConnectivityBuilder.CreateConnectivityGroups(); // For each valid group build BSP and extract convex hulls for ( int32 i=0; i<ConnectivityBuilder.Groups.Num(); i++ ) { const FMeshConnectivityGroup &Group = ConnectivityBuilder.Groups[ i ]; // TODO: add some BSP friendly checks here // e.g. if group triangles form a closed mesh // Generate polygons from group triangles TempModel->Polys->Element.Empty(); for ( int32 j=0; j<Group.Triangles.Num(); j++ ) { const FMeshConnectivityTriangle &Triangle = ConnectivityBuilder.Triangles[ Group.Triangles[j] ]; FPoly* Poly = new( TempModel->Polys->Element ) FPoly(); Poly->Init(); Poly->iLink = j / 3; // Add vertices new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[0] ].Position ); new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[1] ].Position ); new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[2] ].Position ); // Update polygon normal Poly->CalcNormal(1); } // 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 ); // Convert collision model into a collection of convex hulls. // Generated convex hulls will be added to existing ones BodySetup->CreateFromModel( TempModel, false ); } }
/** * Creates a model from the triangles in a static mesh. */ void CreateModelFromStaticMesh(UModel* Model,AStaticMeshActor* StaticMeshActor) { #if TODO_STATICMESH UStaticMesh* StaticMesh = StaticMeshActor->StaticMeshComponent->StaticMesh; FMatrix ActorToWorld = StaticMeshActor->ActorToWorld().ToMatrixWithScale(); Model->Polys->Element.Empty(); const FStaticMeshTriangle* RawTriangleData = (FStaticMeshTriangle*) StaticMesh->LODModels[0].RawTriangles.Lock(LOCK_READ_ONLY); if(StaticMesh->LODModels[0].RawTriangles.GetElementCount()) { for(int32 TriangleIndex = 0; TriangleIndex < StaticMesh->LODModels[0].RawTriangles.GetElementCount(); TriangleIndex++) { const FStaticMeshTriangle& Triangle = RawTriangleData[TriangleIndex]; FPoly* Polygon = new(Model->Polys->Element) FPoly; Polygon->Init(); Polygon->iLink = Polygon - Model->Polys->Element.GetData(); Polygon->Material = StaticMesh->LODModels[0].Elements[Triangle.MaterialIndex].Material; Polygon->PolyFlags = PF_DefaultFlags; Polygon->SmoothingMask = Triangle.SmoothingMask; new(Polygon->Vertices) FVector(ActorToWorld.TransformPosition(Triangle.Vertices[2])); new(Polygon->Vertices) FVector(ActorToWorld.TransformPosition(Triangle.Vertices[1])); new(Polygon->Vertices) FVector(ActorToWorld.TransformPosition(Triangle.Vertices[0])); Polygon->CalcNormal(1); Polygon->Finalize(NULL,0); FTexCoordsToVectors(Polygon->Vertices[2],FVector(Triangle.UVs[0][0].X * UModel::GetGlobalBSPTexelScale(),Triangle.UVs[0][0].Y * UModel::GetGlobalBSPTexelScale(),1), Polygon->Vertices[1],FVector(Triangle.UVs[1][0].X * UModel::GetGlobalBSPTexelScale(),Triangle.UVs[1][0].Y * UModel::GetGlobalBSPTexelScale(),1), Polygon->Vertices[0],FVector(Triangle.UVs[2][0].X * UModel::GetGlobalBSPTexelScale(),Triangle.UVs[2][0].Y * UModel::GetGlobalBSPTexelScale(),1), &Polygon->Base,&Polygon->TextureU,&Polygon->TextureV); } } StaticMesh->LODModels[0].RawTriangles.Unlock(); Model->Linked = 1; FBSPOps::bspValidateBrush(Model,0,1); Model->BuildBound(); #endif // #if TODO_STATICMESH }
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; }