RuntimeMeshSlicer.cpp
Go to the documentation of this file.
1 // Copyright 2016-2018 Chris Conway (Koderz). All Rights Reserved.
2 
3 #include "RuntimeMeshSlicer.h"
5 #include "GeomTools.h"
6 #include "RuntimeMesh.h"
7 #include "RuntimeMeshData.h"
8 #include "RuntimeMeshComponent.h"
9 #include "PhysicsEngine/BodySetup.h"
10 
11 
12 int32 URuntimeMeshSlicer::CompareBoxPlane(const FBox& InBox, const FPlane& InPlane)
13 {
14  FVector BoxCenter, BoxExtents;
15  InBox.GetCenterAndExtents(BoxCenter, BoxExtents);
16 
17  // Find the distance from the plane to the center of the box
18  float BoxCenterDist = InPlane.PlaneDot(BoxCenter);
19 
20  // Find the size of the box in direction of plane normal
21  float BoxSize = FVector::BoxPushOut(InPlane, BoxExtents);
22 
23  return (BoxCenterDist > BoxSize) ? 1 : ((BoxCenterDist < -BoxSize) ? -1 : 0);
24 }
25 
27 {
29 
30  // Handle bad alpha
31  if (FMath::IsNaN(Alpha) || !FMath::IsFinite(Alpha))
32  {
33  Result = V1;
34  return Result;
35  }
36 
37  Result.Position = FMath::Lerp(V0.Position, V1.Position, Alpha);
38 
39  // We assume the sign of the binormal (W component of Normal) doesn't change
40  Result.Normal = FMath::Lerp(V0.Normal, V1.Normal, Alpha);
41  Result.Tangent = FMath::Lerp(V0.Tangent, V1.Tangent, Alpha);
42 
43  Result.Color.R = FMath::Clamp(FMath::TruncToInt(FMath::Lerp(float(V0.Color.R), float(V1.Color.R), Alpha)), 0, 255);
44  Result.Color.G = FMath::Clamp(FMath::TruncToInt(FMath::Lerp(float(V0.Color.G), float(V1.Color.G), Alpha)), 0, 255);
45  Result.Color.B = FMath::Clamp(FMath::TruncToInt(FMath::Lerp(float(V0.Color.B), float(V1.Color.B), Alpha)), 0, 255);
46  Result.Color.A = FMath::Clamp(FMath::TruncToInt(FMath::Lerp(float(V0.Color.A), float(V1.Color.A), Alpha)), 0, 255);
47 
48  // We should never have a different number of UVs between the vertices
49  check(V0.UVs.Num() == V1.UVs.Num());
50 
51  Result.UVs.SetNum(V0.UVs.Num());
52  for (int32 Index = 0; Index < Result.UVs.Num(); Index++)
53  {
54  Result.UVs[Index] = FMath::Lerp(V0.UVs[Index], V1.UVs[Index], Alpha);
55  }
56 
57  return Result;
58 }
59 
60 
61 void URuntimeMeshSlicer::Transform2DPolygonTo3D(const FUtilPoly2D& InPoly, const FMatrix& InMatrix, TSharedPtr<FRuntimeMeshAccessor> OutMesh)
62 {
63  // Set the W component to 1 to not flip the binormal
64  FVector4 PolyNormal = FVector4(-InMatrix.GetUnitAxis(EAxis::Z), 1.0f);
65  FVector PolyTangent = InMatrix.GetUnitAxis(EAxis::X);
66 
67  int32 NumUVs = OutMesh->NumUVChannels();
68 
69  for (int32 VertexIndex = 0; VertexIndex < InPoly.Verts.Num(); VertexIndex++)
70  {
71  const FUtilVertex2D& InVertex = InPoly.Verts[VertexIndex];
72 
74 
75  int32 NewIndex = OutMesh->AddVertex(InMatrix.TransformPosition(FVector(InVertex.Pos.X, InVertex.Pos.Y, 0.f)));
76  OutMesh->SetNormal(NewIndex, PolyNormal);
77  OutMesh->SetTangent(NewIndex, PolyTangent);
78  OutMesh->SetColor(NewIndex, InVertex.Color);
79 
80  NewVert.UVs.SetNum(NumUVs);
81  for (int32 Index = 0; Index < NumUVs; Index++)
82  {
83  OutMesh->SetUV(NewIndex, Index, InVertex.UV);
84  }
85  }
86 }
87 
88 bool URuntimeMeshSlicer::TriangulatePoly(TSharedPtr<FRuntimeMeshAccessor> Mesh, int32 VertBase, const FVector& PolyNormal)
89 {
90  // Bail if we don't have enough vertices
91  int32 NumVerts = Mesh->NumVertices() - VertBase;
92  if (NumVerts <= 3)
93  {
94  Mesh->AddIndex(FMath::Clamp(0 + VertBase, 0, NumVerts));
95  Mesh->AddIndex(FMath::Clamp(2 + VertBase, 0, NumVerts));
96  Mesh->AddIndex(FMath::Clamp(1 + VertBase, 0, NumVerts));
97 
98  return true;
99  }
100 
101  // Store the starting size of the indices.
102  const int32 OriginalNumIndices = Mesh->NumIndices();
103 
104  // Initialize starting array of indices
105  TArray<int32> VertIndices;
106  VertIndices.SetNumUninitialized(NumVerts);
107  for (int VertIndex = 0; VertIndex < NumVerts; VertIndex++)
108  {
109  VertIndices[VertIndex] = VertBase + VertIndex;
110  }
111 
112  // Find ears until there are no vertices
113  while (VertIndices.Num() >= 3)
114  {
115  // Look for an 'ear' triangle
116  bool bFoundEar = false;
117  for (int32 EarVertexIndex = 0; EarVertexIndex < VertIndices.Num(); EarVertexIndex++)
118  {
119  // Triangle is 'this' vert plus the one before and after it
120  const int32 Index1 = (EarVertexIndex == 0) ? VertIndices.Num() - 1 : EarVertexIndex - 1;
121  const int32 Index2 = EarVertexIndex;
122  const int32 Index3 = (EarVertexIndex + 1) % VertIndices.Num();
123 
124  const FRuntimeMeshAccessorVertex Vert1 = Mesh->GetVertex(VertIndices[Index1]);
125  const FRuntimeMeshAccessorVertex Vert2 = Mesh->GetVertex(VertIndices[Index2]);
126  const FRuntimeMeshAccessorVertex Vert3 = Mesh->GetVertex(VertIndices[Index3]);
127 
128  // Check that this vertex is convex (cross product must be positive)
129  const FVector Edge12 = Vert2.Position - Vert1.Position;
130  const FVector Edge13 = Vert3.Position - Vert1.Position;
131  const float Determinant = (Edge12 ^ Edge13) | PolyNormal;
132  if (Determinant > 0.f)
133  {
134  continue;
135  }
136 
137  bool bFoundVertInside = false;
138  // Look through all verts before this in array to see if any are inside triangle
139  for (int32 VertexIndex = 0; VertexIndex < VertIndices.Num(); VertexIndex++)
140  {
141  const FRuntimeMeshAccessorVertex& TestVert = Mesh->GetVertex(VertIndices[VertexIndex]);
142 
143  if (VertexIndex != Index1 &&
144  VertexIndex != Index2 &&
145  VertexIndex != Index3 &&
146  FGeomTools::PointInTriangle(Vert1.Position, Vert2.Position, Vert3.Position, TestVert.Position))
147  {
148  bFoundVertInside = true;
149  break;
150  }
151  }
152 
153  // Triangle with no verts inside - its an 'ear'!
154  if (!bFoundVertInside)
155  {
156  Mesh->AddIndex(VertIndices[Index1]);
157  Mesh->AddIndex(VertIndices[Index3]);
158  Mesh->AddIndex(VertIndices[Index2]);
159 
160  // And remove vertex from polygon
161  VertIndices.RemoveAt(EarVertexIndex);
162 
163  bFoundEar = true;
164  break;
165  }
166  }
167 
168  // If we couldn't find an 'ear' it indicates something is bad with this polygon - discard triangles and return.
169  if (!bFoundEar)
170  {
171  Mesh->SetNumIndices(OriginalNumIndices);
172  return false;
173  }
174  }
175 
176  return true;
177 }
178 
179 void URuntimeMeshSlicer::SliceConvexElem(const FKConvexElem& InConvex, const FPlane& SlicePlane, TArray<FVector>& OutConvexVerts)
180 {
181  FPlane SlicePlaneFlipped = SlicePlane.Flip();
182 
183  // Get set of planes that make up hull
184  TArray<FPlane> ConvexPlanes;
185  InConvex.GetPlanes(ConvexPlanes);
186 
187  if (ConvexPlanes.Num() >= 4)
188  {
189  // Add on the slicing plane (need to flip as it culls geom in the opposite sense to our geom culling code)
190  ConvexPlanes.Add(SlicePlaneFlipped);
191 
192  // Create output convex based on new set of planes
193  FKConvexElem SlicedElem;
194  bool bSuccess = SlicedElem.HullFromPlanes(ConvexPlanes, InConvex.VertexData);
195  if (bSuccess)
196  {
197  OutConvexVerts = SlicedElem.VertexData;
198  }
199  }
200 }
201 
202 
203 void URuntimeMeshSlicer::SliceRuntimeMeshConvexCollision(URuntimeMesh* InRuntimeMesh, URuntimeMesh* OutOtherHalf, FVector PlanePosition, FVector PlaneNormal)
204 {
205  bool bCreateOtherHalf = OutOtherHalf != nullptr;
206 
207  PlaneNormal.Normalize();
208  FPlane SlicePlane(PlanePosition, PlaneNormal);
209 
210  // Array of sliced collision shapes
211  TArray<TArray<FVector>> SlicedCollision;
212  TArray<TArray<FVector>> OtherSlicedCollision;
213 
214  UBodySetup* BodySetup = InRuntimeMesh->GetBodySetup();
215 
216  if (!BodySetup)
217  {
218  return;
219  }
220 
221  for (int32 ConvexIndex = 0; ConvexIndex < BodySetup->AggGeom.ConvexElems.Num(); ConvexIndex++)
222  {
223  FKConvexElem& BaseConvex = BodySetup->AggGeom.ConvexElems[ConvexIndex];
224 
225  int32 BoxCompare = CompareBoxPlane(BaseConvex.ElemBox, SlicePlane);
226 
227  // If box totally clipped, add to other half (if desired)
228  if (BoxCompare == -1)
229  {
230  if (bCreateOtherHalf)
231  {
232  OtherSlicedCollision.Add(BaseConvex.VertexData);
233  }
234  }
235  // If box totally valid, just keep mesh as is
236  else if (BoxCompare == 1)
237  {
238  SlicedCollision.Add(BaseConvex.VertexData);
239  }
240  // Need to actually slice the convex shape
241  else
242  {
243  TArray<FVector> SlicedConvexVerts;
244  SliceConvexElem(BaseConvex, SlicePlane, SlicedConvexVerts);
245  // If we got something valid, add it
246  if (SlicedConvexVerts.Num() >= 4)
247  {
248  SlicedCollision.Add(SlicedConvexVerts);
249  }
250 
251  // Slice again to get the other half of the collision, if desired
252  if (bCreateOtherHalf)
253  {
254  TArray<FVector> OtherSlicedConvexVerts;
255  SliceConvexElem(BaseConvex, SlicePlane.Flip(), OtherSlicedConvexVerts);
256  if (OtherSlicedConvexVerts.Num() >= 4)
257  {
258  OtherSlicedCollision.Add(OtherSlicedConvexVerts);
259  }
260  }
261  }
262  }
263 
264  // Update collision of runtime mesh
265  InRuntimeMesh->SetCollisionConvexMeshes(SlicedCollision);
266 
267  // Set collision for other mesh
268  if (bCreateOtherHalf)
269  {
270  OutOtherHalf->SetCollisionConvexMeshes(OtherSlicedCollision);
271  }
272 }
273 
274 void URuntimeMeshSlicer::SliceRuntimeMeshSection(const FRuntimeMeshDataPtr& InRuntimeMesh, const FRuntimeMeshDataPtr& OutOtherHalf, int32 SectionIndex, const FPlane& SlicePlane, TArray<FUtilEdge3D>& ClipEdges)
275 {
276  bool bShouldCreateOtherHalf = OutOtherHalf.IsValid();
277 
278  auto SourceMeshData = InRuntimeMesh->GetSectionReadonly(SectionIndex);
279  TSharedPtr<FRuntimeMeshBuilder> NewMeshData = MakeRuntimeMeshBuilder(*SourceMeshData.Get());
280  TSharedPtr<FRuntimeMeshBuilder> OtherMeshData = bShouldCreateOtherHalf ? MakeRuntimeMeshBuilder(*SourceMeshData.Get()) : TSharedPtr<FRuntimeMeshBuilder>(nullptr);
281 
282  // Map of base vert index to sliced vert index
283  TMap<int32, int32> BaseToSlicedVertIndex;
284  TMap<int32, int32> BaseToOtherSlicedVertIndex;
285 
286  const int32 NumBaseVerts = SourceMeshData->NumVertices();
287 
288  // Distance of each base vert from slice plane
289  TArray<float> VertDistance;
290  VertDistance.SetNumUninitialized(NumBaseVerts);
291 
292  // Build vertex buffer
293  for (int32 BaseVertIndex = 0; BaseVertIndex < NumBaseVerts; BaseVertIndex++)
294  {
295  FRuntimeMeshAccessorVertex BaseVert = SourceMeshData->GetVertex(BaseVertIndex);
296 
297  // Calculate distance from plane
298  VertDistance[BaseVertIndex] = SlicePlane.PlaneDot(BaseVert.Position);
299 
300  // See if vert is being kept in this section
301  if (VertDistance[BaseVertIndex] > 0.f)
302  {
303  // Copy to sliced v buffer
304  int32 SlicedVertIndex = NewMeshData->AddVertex(BaseVert);
305 
306  // Add to map
307  BaseToSlicedVertIndex.Add(BaseVertIndex, SlicedVertIndex);
308  }
309  // Or add to other half if desired
310  else if (bShouldCreateOtherHalf)
311  {
312  int32 SlicedVertIndex = OtherMeshData->AddVertex(BaseVert);
313 
314  BaseToOtherSlicedVertIndex.Add(BaseVertIndex, SlicedVertIndex);
315  }
316  }
317 
318 
319  // Iterate over base triangles (IE. 3 indices at a time)
320  int32 NumBaseIndices = SourceMeshData->NumIndices();
321  for (int32 BaseIndex = 0; BaseIndex < NumBaseIndices; BaseIndex += 3)
322  {
323  int32 BaseV[3]; // Triangle vert indices in original mesh
324  int32* SlicedV[3]; // Pointers to vert indices in new v buffer
325  int32* SlicedOtherV[3]; // Pointers to vert indices in new 'other half' v buffer
326 
327  // For each vertex..
328  for (int32 i = 0; i < 3; i++)
329  {
330  // Get triangle vert index
331  BaseV[i] = SourceMeshData->GetIndex(BaseIndex + i);
332  // Look up in sliced v buffer
333  SlicedV[i] = BaseToSlicedVertIndex.Find(BaseV[i]);
334  // Look up in 'other half' v buffer (if desired)
335  if (bShouldCreateOtherHalf)
336  {
337  SlicedOtherV[i] = BaseToOtherSlicedVertIndex.Find(BaseV[i]);
338  // Each base vert _must_ exist in either BaseToSlicedVertIndex or BaseToOtherSlicedVertIndex
339  check((SlicedV[i] != nullptr) != (SlicedOtherV[i] != nullptr));
340  }
341  }
342 
343  // If all verts survived plane cull, keep the triangle
344  if (SlicedV[0] != nullptr && SlicedV[1] != nullptr && SlicedV[2] != nullptr)
345  {
346  NewMeshData->AddIndex(*SlicedV[0]);
347  NewMeshData->AddIndex(*SlicedV[1]);
348  NewMeshData->AddIndex(*SlicedV[2]);
349  }
350  // If all verts were removed by plane cull
351  else if (SlicedV[0] == nullptr && SlicedV[1] == nullptr && SlicedV[2] == nullptr)
352  {
353  // If creating other half, add all verts to that
354  if (bShouldCreateOtherHalf)
355  {
356  OtherMeshData->AddIndex(*SlicedOtherV[0]);
357  OtherMeshData->AddIndex(*SlicedOtherV[1]);
358  OtherMeshData->AddIndex(*SlicedOtherV[2]);
359  }
360  }
361  // If partially culled, clip to create 1 or 2 new triangles
362  else
363  {
364  int32 FinalVerts[4];
365  int32 NumFinalVerts = 0;
366 
367  int32 OtherFinalVerts[4];
368  int32 NumOtherFinalVerts = 0;
369 
370  FUtilEdge3D NewClipEdge;
371  int32 ClippedEdges = 0;
372 
373  float PlaneDist[3];
374  PlaneDist[0] = VertDistance[BaseV[0]];
375  PlaneDist[1] = VertDistance[BaseV[1]];
376  PlaneDist[2] = VertDistance[BaseV[2]];
377 
378  for (int32 EdgeIdx = 0; EdgeIdx < 3; EdgeIdx++)
379  {
380  int32 ThisVert = EdgeIdx;
381 
382  // If start vert is inside, add it.
383  if (SlicedV[ThisVert] != nullptr)
384  {
385  check(NumFinalVerts < 4);
386  FinalVerts[NumFinalVerts++] = *SlicedV[ThisVert];
387  }
388  // If not, add to other side
389  else if (bShouldCreateOtherHalf)
390  {
391  check(NumOtherFinalVerts < 4);
392  OtherFinalVerts[NumOtherFinalVerts++] = *SlicedOtherV[ThisVert];
393  }
394 
395  // If start and next vert are on opposite sides, add intersection
396  int32 NextVert = (EdgeIdx + 1) % 3;
397 
398  if ((SlicedV[EdgeIdx] == nullptr) != (SlicedV[NextVert] == nullptr))
399  {
400  // Find distance along edge that plane is
401  float Alpha = -PlaneDist[ThisVert] / (PlaneDist[NextVert] - PlaneDist[ThisVert]);
402  // Interpolate vertex params to that point
404  SourceMeshData->GetVertex(BaseV[ThisVert]),
405  SourceMeshData->GetVertex(BaseV[NextVert]),
406  FMath::Clamp(Alpha, 0.0f, 1.0f));
407 
408  // Add to vertex buffer
409  int32 InterpVertIndex = NewMeshData->AddVertex(InterpVert);
410 
411  // Save vert index for this poly
412  check(NumFinalVerts < 4);
413  FinalVerts[NumFinalVerts++] = InterpVertIndex;
414 
415  // If desired, add to the poly for the other half as well
416  if (bShouldCreateOtherHalf)
417  {
418  int32 OtherInterpVertIndex = OtherMeshData->AddVertex(InterpVert);
419 
420  check(NumOtherFinalVerts < 4);
421  OtherFinalVerts[NumOtherFinalVerts++] = OtherInterpVertIndex;
422  }
423 
424  // When we make a new edge on the surface of the clip plane, save it off.
425  check(ClippedEdges < 2);
426  if (ClippedEdges == 0)
427  {
428  NewClipEdge.V0 = InterpVert.Position;
429  }
430  else
431  {
432  NewClipEdge.V1 = InterpVert.Position;
433  }
434 
435  ClippedEdges++;
436  }
437  }
438 
439  // Triangulate the clipped polygon.
440  for (int32 VertexIndex = 2; VertexIndex < NumFinalVerts; VertexIndex++)
441  {
442  NewMeshData->AddIndex(FinalVerts[0]);
443  NewMeshData->AddIndex(FinalVerts[VertexIndex - 1]);
444  NewMeshData->AddIndex(FinalVerts[VertexIndex]);
445  }
446 
447  // If we are making the other half, triangulate that as well
448  if (bShouldCreateOtherHalf)
449  {
450  for (int32 VertexIndex = 2; VertexIndex < NumOtherFinalVerts; VertexIndex++)
451  {
452  OtherMeshData->AddIndex(OtherFinalVerts[0]);
453  OtherMeshData->AddIndex(OtherFinalVerts[VertexIndex - 1]);
454  OtherMeshData->AddIndex(OtherFinalVerts[VertexIndex]);
455  }
456  }
457 
458  check(ClippedEdges != 1); // Should never clip just one edge of the triangle
459 
460  // If we created a new edge, save that off here as well
461  if (ClippedEdges == 2)
462  {
463  ClipEdges.Add(NewClipEdge);
464  }
465  }
466  }
467 
468  // Add the mesh data to the other mesh if we're building the other mesh and have valid geometry.
469  if (bShouldCreateOtherHalf && OtherMeshData->NumVertices() > 0 && OtherMeshData->NumIndices() > 0)
470  {
471  OutOtherHalf->CreateMeshSection(SectionIndex, MoveTemp(OtherMeshData));
472  }
473 
474  // Update this runtime mesh, or clear it if we have no geometry
475  if (NewMeshData->NumVertices() > 0 && NewMeshData->NumIndices() > 0)
476  {
477  InRuntimeMesh->UpdateMeshSection(SectionIndex, MoveTemp(NewMeshData));
478  }
479  else
480  {
481  InRuntimeMesh->ClearMeshSection(SectionIndex);
482  }
483 }
484 
485 int32 URuntimeMeshSlicer::CapMeshSlice(const FRuntimeMeshDataPtr& InRuntimeMesh, const FRuntimeMeshDataPtr& OutOtherHalf, TArray<FUtilEdge3D>& ClipEdges, const FPlane& SlicePlane, FVector PlaneNormal, ERuntimeMeshSlicerCapOption CapOption)
486 {
487  bool bShouldCreateOtherHalf = OutOtherHalf.IsValid();
488 
489  int32 NewCapSectionIndex = InRuntimeMesh->GetLastSectionIndex() + 1;
490  if (bShouldCreateOtherHalf)
491  {
492  NewCapSectionIndex = FMath::Max(NewCapSectionIndex, OutOtherHalf->GetLastSectionIndex() + 1);
493  }
494 
495  // Create cap geometry (if some edges to create it from)
496  if (CapOption != ERuntimeMeshSlicerCapOption::NoCap && ClipEdges.Num() > 0)
497  {
498  TSharedPtr<FRuntimeMeshBuilder> CapSection;
499  int32 CapSectionIndex = INDEX_NONE;
500 
501  // If using an existing section, copy that info first
503  {
504  CapSectionIndex = InRuntimeMesh->GetLastSectionIndex();
505  auto ExistingMesh = InRuntimeMesh->GetSectionReadonly(NewCapSectionIndex);
506  CapSection = MakeRuntimeMeshBuilder(*ExistingMesh.Get());
507  ExistingMesh->CopyTo(CapSection);
508  }
509  // Adding new section for cap
510  else
511  {
512  CapSection = MakeRuntimeMeshBuilder<FRuntimeMeshTangents, FVector2DHalf, uint16>();
513  CapSectionIndex = NewCapSectionIndex;
514  }
515 
516  // Project 3D edges onto slice plane to form 2D edges
517  TArray<FUtilEdge2D> Edges2D;
518  FUtilPoly2DSet PolySet;
519  FGeomTools::ProjectEdges(Edges2D, PolySet.PolyToWorld, ClipEdges, SlicePlane);
520 
521  // Find 2D closed polygons from this edge soup
522  FGeomTools::Buid2DPolysFromEdges(PolySet.Polys, Edges2D, FColor(255, 255, 255, 255));
523 
524  // Remember start point for vert and index buffer before adding and cap geom
525  int32 CapVertBase = CapSection->NumVertices();
526  int32 CapIndexBase = CapSection->NumIndices();
527 
528  // Triangulate each poly
529  for (int32 PolyIdx = 0; PolyIdx < PolySet.Polys.Num(); PolyIdx++)
530  {
531  // Generate UVs for the 2D polygon.
532  FGeomTools::GeneratePlanarTilingPolyUVs(PolySet.Polys[PolyIdx], 64.f);
533 
534  // Remember start of vert buffer before adding triangles for this poly
535  int32 PolyVertBase = CapSection->NumVertices();
536 
537  // Transform from 2D poly verts to 3D
538  Transform2DPolygonTo3D(PolySet.Polys[PolyIdx], PolySet.PolyToWorld, CapSection);
539 
540  // Triangulate this polygon
541  TriangulatePoly(CapSection, PolyVertBase, PlaneNormal);
542  }
543 
544  // Set geom for cap section
546  {
547  InRuntimeMesh->UpdateMeshSection(CapSectionIndex, MoveTemp(CapSection));
548  }
549  else
550  {
551  InRuntimeMesh->CreateMeshSection(CapSectionIndex, MoveTemp(CapSection));
552  }
553 
554  // If creating the other half, copy cap geom into other half sections
555  if (bShouldCreateOtherHalf)
556  {
557  TSharedPtr<FRuntimeMeshBuilder> OtherCapSection;
558  int32 OtherCapSectionIndex = INDEX_NONE;
559 
560  // If using an existing section, copy that info first
562  {
563  OtherCapSectionIndex = OutOtherHalf->GetLastSectionIndex();
564  auto ExistingMesh = OutOtherHalf->GetSectionReadonly(CapSectionIndex);
565  OtherCapSection = MakeRuntimeMeshBuilder(*ExistingMesh.Get());
566  ExistingMesh->CopyTo(OtherCapSection);
567  }
568  // Adding new section for cap
569  else
570  {
571  OtherCapSection = MakeRuntimeMeshBuilder<FRuntimeMeshTangents, FVector2DHalf, uint16>();
572  OtherCapSectionIndex = NewCapSectionIndex;
573  }
574 
575  // Remember current base index for verts in 'other cap section'
576  int32 OtherCapVertBase = OtherCapSection->NumVertices();
577 
578  // Copy verts from cap section into other cap section
579  int32 CapSectionLength = CapSection->NumVertices();
580  for (int32 VertIdx = CapVertBase; VertIdx < CapSectionLength; VertIdx++)
581  {
582  FRuntimeMeshAccessorVertex OtherCapVert = CapSection->GetVertex(VertIdx);
583 
584  // Flip normal and tangent TODO: FlipY?
585  float Sign = OtherCapVert.Normal.W;
586  OtherCapVert.Normal *= -1.f;
587  OtherCapVert.Normal.W = Sign;
588  OtherCapVert.Tangent *= -1.f;
589 
590  // Add to other cap v buffer
591  OtherCapSection->AddVertex(OtherCapVert);
592  }
593 
594  // Find offset between main cap verts and other cap verts
595  int32 VertOffset = OtherCapVertBase - CapVertBase;
596 
597  // Copy indices over as well
598  int32 NumCapIndices = CapSection->NumIndices();
599  for (int32 IndexIdx = CapIndexBase; IndexIdx < NumCapIndices; IndexIdx += 3)
600  {
601  // Need to offset and change winding
602  OtherCapSection->AddIndex(CapSection->GetIndex(IndexIdx + 0) + VertOffset);
603  OtherCapSection->AddIndex(CapSection->GetIndex(IndexIdx + 2) + VertOffset);
604  OtherCapSection->AddIndex(CapSection->GetIndex(IndexIdx + 1) + VertOffset);
605  }
606 
607  // Set geom for cap section
609  {
610  OutOtherHalf->UpdateMeshSection(OtherCapSectionIndex, MoveTemp(OtherCapSection));
611  }
612  else
613  {
614  OutOtherHalf->CreateMeshSection(OtherCapSectionIndex, MoveTemp(OtherCapSection));
615  }
616  }
617  }
618  return NewCapSectionIndex;
619 }
620 
621 
622 void URuntimeMeshSlicer::SliceRuntimeMesh(URuntimeMesh* InRuntimeMesh, FVector PlanePosition, FVector PlaneNormal, URuntimeMesh* OutOtherHalf, ERuntimeMeshSlicerCapOption CapOption, UMaterialInterface* CapMaterial)
623 {
624  if (InRuntimeMesh)
625  {
626  FRuntimeMeshDataPtr InMeshData = InRuntimeMesh->GetRuntimeMeshData();
627  FRuntimeMeshDataPtr OtherHalfData = OutOtherHalf ? OutOtherHalf->GetRuntimeMeshData() : FRuntimeMeshDataPtr(nullptr);
628 
629 
630  PlaneNormal.Normalize();
631  FPlane SlicePlane(PlanePosition, PlaneNormal);
632 
633  bool bShouldCreateOtherHalf = OutOtherHalf != nullptr;
634 
635  // Set of new edges created by clipping polys by plane
636  TArray<FUtilEdge3D> ClipEdges;
637 
638  for (int32 SectionIndex = 0; SectionIndex < InRuntimeMesh->GetNumSections(); SectionIndex++)
639  {
640  // Skip if the section doesn't exist
641  if (!InRuntimeMesh->DoesSectionExist(SectionIndex))
642  {
643  continue;
644  }
645 
646  auto MeshData = InRuntimeMesh->GetSectionReadonly(SectionIndex);
647 
648  // Skip if we don't have mesh data
649  if (MeshData->NumVertices() < 3 || MeshData->NumIndices() < 3)
650  {
651  continue;
652  }
653 
654  // Compare bounding box of section with slicing plane
655  int32 BoxCompare = CompareBoxPlane(InRuntimeMesh->GetSectionBoundingBox(SectionIndex), SlicePlane);
656 
657  if (BoxCompare == 1)
658  {
659  // Box totally on the close side of the plane, do nothing
660  continue;
661  }
662 
663  if (BoxCompare == -1)
664  {
665  // Box totally on the far side of the plane, move the entire section to the other RMC if it exists
666  if (bShouldCreateOtherHalf)
667  {
668  auto SourceMeshData = InRuntimeMesh->GetSectionReadonly(SectionIndex);
669 
670  auto NewBuilder = MakeRuntimeMeshBuilder(*SourceMeshData.Get());
671  SourceMeshData->CopyTo(NewBuilder);
672 
673  OutOtherHalf->CreateMeshSection(SectionIndex, MoveTemp(NewBuilder));
674  OutOtherHalf->SetSectionMaterial(SectionIndex, InRuntimeMesh->GetSectionMaterial(SectionIndex));
675  }
676 
677  InRuntimeMesh->ClearMeshSection(SectionIndex);
678  continue;
679  }
680 
681  check(BoxCompare == 0);
682 
683  UMaterialInterface* Material = InRuntimeMesh->GetSectionMaterial(SectionIndex);
684 
685  SliceRuntimeMeshSection(InMeshData, OtherHalfData, SectionIndex, SlicePlane, ClipEdges);
686 
687  if (bShouldCreateOtherHalf && OtherHalfData->DoesSectionExist(SectionIndex))
688  {
689  OutOtherHalf->SetSectionMaterial(SectionIndex, Material);
690  }
691  }
692 
693  int32 CapSectionIndex = CapMeshSlice(InMeshData, OtherHalfData, ClipEdges, SlicePlane, PlaneNormal, CapOption);
694 
696  {
697  InRuntimeMesh->SetSectionMaterial(CapSectionIndex, CapMaterial);
698  if (bShouldCreateOtherHalf)
699  {
700  OutOtherHalf->SetSectionMaterial(CapSectionIndex, CapMaterial);
701  }
702  }
703 
704  // Slice the collision
705  SliceRuntimeMeshConvexCollision(InRuntimeMesh, OutOtherHalf, PlanePosition, PlaneNormal);
706  }
707 }
708 
709 void URuntimeMeshSlicer::SliceRuntimeMeshComponent(URuntimeMeshComponent* InRuntimeMesh, FVector PlanePosition, FVector PlaneNormal, bool bCreateOtherHalf, URuntimeMeshComponent*& OutOtherHalf, ERuntimeMeshSlicerCapOption CapOption, UMaterialInterface* CapMaterial)
710 {
711  if (InRuntimeMesh && InRuntimeMesh->GetRuntimeMesh())
712  {
713  // Transform plane from world to local space
714  FTransform ComponentToWorld = InRuntimeMesh->GetComponentToWorld();
715  FVector LocalPlanePos = ComponentToWorld.InverseTransformPosition(PlanePosition);
716  FVector LocalPlaneNormal = ComponentToWorld.InverseTransformVectorNoScale(PlaneNormal);
717  LocalPlaneNormal = LocalPlaneNormal.GetSafeNormal(); // Ensure normalized
718 
719 
720  if (bCreateOtherHalf)
721  {
722  OutOtherHalf = NewObject<URuntimeMeshComponent>(InRuntimeMesh->GetOuter());
723  OutOtherHalf->SetWorldTransform(InRuntimeMesh->GetComponentTransform());
724  }
725 
726  SliceRuntimeMesh(InRuntimeMesh->GetRuntimeMesh(), LocalPlanePos, LocalPlaneNormal, bCreateOtherHalf ? OutOtherHalf->GetOrCreateRuntimeMesh() : nullptr, CapOption, CapMaterial);
727 
728 
729  if (bCreateOtherHalf)
730  {
731  if (OutOtherHalf->GetNumSections() > 0)
732  {
733  OutOtherHalf->SetCollisionProfileName(InRuntimeMesh->GetCollisionProfileName());
734  OutOtherHalf->SetCollisionEnabled(InRuntimeMesh->GetCollisionEnabled());
735  OutOtherHalf->SetCollisionUseComplexAsSimple(InRuntimeMesh->IsCollisionUsingComplexAsSimple());
736 
737  // Copy overridden materials
738  for (int32 Index = 0; Index < InRuntimeMesh->GetNumOverrideMaterials(); Index++)
739  {
740  if (UMaterialInterface* Material = InRuntimeMesh->GetOverrideMaterial(Index))
741  {
742  OutOtherHalf->SetMaterial(Index, Material);
743  }
744  }
745 
746  OutOtherHalf->RegisterComponent();
747  }
748  else
749  {
750  OutOtherHalf->DestroyComponent();
751  }
752  }
753 
754  }
755 }
int32 GetNumSections() const
Definition: RuntimeMesh.h:611
ERuntimeMeshSlicerCapOption
FRuntimeMeshDataRef GetRuntimeMeshData() const
Definition: RuntimeMesh.h:121
list Z
Definition: rmse.py:133
void ClearMeshSection(int32 SectionIndex)
Definition: RuntimeMesh.h:501
void CreateMeshSection(int32 SectionIndex, bool bWantsHighPrecisionTangents, bool bWantsHighPrecisionUVs, int32 NumUVs, bool bWants32BitIndices, bool bCreateCollision, EUpdateFrequency UpdateFrequency=EUpdateFrequency::Average)
Definition: RuntimeMesh.h:135
static void SliceConvexElem(const FKConvexElem &InConvex, const FPlane &SlicePlane, TArray< FVector > &OutConvexVerts)
static void SliceRuntimeMesh(URuntimeMesh *InRuntimeMesh, FVector PlanePosition, FVector PlaneNormal, URuntimeMesh *OutOtherHalf, ERuntimeMeshSlicerCapOption CapOption, UMaterialInterface *CapMaterial)
static void Transform2DPolygonTo3D(const FUtilPoly2D &InPoly, const FMatrix &InMatrix, TSharedPtr< FRuntimeMeshAccessor > OutMesh)
FBox GetSectionBoundingBox(int32 SectionIndex)
Definition: RuntimeMesh.h:552
static int32 CapMeshSlice(const FRuntimeMeshDataPtr &InRuntimeMesh, const FRuntimeMeshDataPtr &OutOtherHalf, TArray< FUtilEdge3D > &ClipEdges, const FPlane &SlicePlane, FVector PlaneNormal, ERuntimeMeshSlicerCapOption CapOption)
void SetSectionMaterial(int32 SectionId, UMaterialInterface *Material)
Definition: RuntimeMesh.h:517
TSharedPtr< FRuntimeMeshData, ESPMode::ThreadSafe > FRuntimeMeshDataPtr
UMaterialInterface * GetSectionMaterial(int32 SectionId)
Definition: RuntimeMesh.h:534
GLdouble f
static void SliceRuntimeMeshConvexCollision(URuntimeMesh *InRuntimeMesh, URuntimeMesh *OutOtherHalf, FVector PlanePosition, FVector PlaneNormal)
static const textual_icon check
Definition: model-views.h:260
TUniquePtr< FRuntimeMeshScopedUpdater > GetSectionReadonly(int32 SectionId)
Definition: RuntimeMesh.h:379
struct Index Index
Definition: sqlite3.c:11789
UBodySetup * GetBodySetup()
Definition: RuntimeMesh.h:841
TArray< FVector2D, TInlineAllocator< RUNTIMEMESH_MAXTEXCOORDS > > UVs
bool DoesSectionExist(int32 SectionIndex) const
Definition: RuntimeMesh.h:619
static int32 CompareBoxPlane(const FBox &InBox, const FPlane &InPlane)
static void SliceRuntimeMeshComponent(URuntimeMeshComponent *InRuntimeMesh, FVector PlanePosition, FVector PlaneNormal, bool bCreateOtherHalf, URuntimeMeshComponent *&OutOtherHalf, ERuntimeMeshSlicerCapOption CapOption, UMaterialInterface *CapMaterial)
static void SliceRuntimeMeshSection(const FRuntimeMeshDataPtr &InRuntimeMesh, const FRuntimeMeshDataPtr &OutOtherHalf, int32 SectionIndex, const FPlane &SlicePlane, TArray< FUtilEdge3D > &ClipEdges)
FORCEINLINE TSharedRef< FRuntimeMeshBuilder > MakeRuntimeMeshBuilder()
int i
list X
Definition: rmse.py:131
static FRuntimeMeshAccessorVertex InterpolateVert(const FRuntimeMeshAccessorVertex &V0, const FRuntimeMeshAccessorVertex &V1, float Alpha)
void SetCollisionConvexMeshes(const TArray< TArray< FVector >> &ConvexMeshes)
Definition: RuntimeMesh.h:693
static bool TriangulatePoly(TSharedPtr< FRuntimeMeshAccessor > Mesh, int32 VertBase, const FVector &PolyNormal)


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:47:41