void UBoxComponent::UpdateBodySetup() { if(ShapeBodySetup == NULL || ShapeBodySetup->IsPendingKill()) { ShapeBodySetup = NewObject<UBodySetup>(this); ShapeBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex; ShapeBodySetup->AggGeom.BoxElems.Add(FKBoxElem()); } check(ShapeBodySetup->AggGeom.BoxElems.Num() == 1); FKBoxElem* se = ShapeBodySetup->AggGeom.BoxElems.GetData(); // @todo UE4 do we allow this now? // check for malformed values if( BoxExtent.X < KINDA_SMALL_NUMBER ) { BoxExtent.X = 1.0f; } if( BoxExtent.Y < KINDA_SMALL_NUMBER ) { BoxExtent.Y = 1.0f; } if( BoxExtent.Z < KINDA_SMALL_NUMBER ) { BoxExtent.Z = 1.0f; } // now set the PhysX data values se->SetTransform( FTransform::Identity ); se->X = BoxExtent.X*2; se->Y = BoxExtent.Y*2; se->Z = BoxExtent.Z*2; }
void UPaperTerrainComponent::SpawnSegments(const TArray<FTerrainSegment>& TerrainSegments, bool bGenerateSegmentColliders) { #ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS TArray<FVector2D> CollisionPolygonPoints; #endif for (const FTerrainSegment& Segment : TerrainSegments) { for (const FTerrainSpriteStamp& Stamp : Segment.Stamps) { const class UPaperSprite* NewSprite = Stamp.Sprite; float Position = Stamp.Time; float HorizontalScale = Stamp.Scale; float NominalWidth = Stamp.NominalWidth; const struct FPaperTerrainMaterialRule* Rule = Segment.Rule; if (bGenerateSegmentColliders && (Rule->bEnableCollision) && (CachedBodySetup != nullptr)) { const FTransform LocalTransformAtCenter(GetTransformAtDistance(Position)); #ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS // Check note be low Re: bClosedSplines FVector2D BoxExtents( 0.5f * NominalWidth * HorizontalScale, 0.5f * FMath::Max<float>(1.0f, FMath::Abs<float>(Rule->CollisionOffset * 0.5f)) ); FVector BoxPoints[4]; BoxPoints[0] = LocalTransformAtCenter.TransformPosition(FVector(BoxExtents.X, 0, BoxExtents.Y)); BoxPoints[1] = LocalTransformAtCenter.TransformPosition(FVector(-BoxExtents.X, 0, BoxExtents.Y)); BoxPoints[2] = LocalTransformAtCenter.TransformPosition(FVector(-BoxExtents.X, 0, -BoxExtents.Y)); BoxPoints[3] = LocalTransformAtCenter.TransformPosition(FVector(BoxExtents.X, 0, -BoxExtents.Y)); FVector2D BoxPoints2D[4]; BoxPoints2D[0].Set(BoxPoints[0].X, BoxPoints[0].Z); BoxPoints2D[1].Set(BoxPoints[1].X, BoxPoints[1].Z); BoxPoints2D[2].Set(BoxPoints[2].X, BoxPoints[2].Z); BoxPoints2D[3].Set(BoxPoints[3].X, BoxPoints[3].Z); // If there is a previous polygon, try to merge if (CollisionPolygonPoints.Num() >= 4) { int InsertPoint = CollisionPolygonPoints.Num() / 2 - 1; float LengthV0 = FVector2D::Distance(CollisionPolygonPoints[InsertPoint], BoxPoints2D[0]); float LengthV1 = FVector2D::Distance(CollisionPolygonPoints[InsertPoint + 1], BoxPoints2D[3]); float MergeThreshold = 10; bool bMergeIntoPolygon = LengthV0 < MergeThreshold && LengthV1 < MergeThreshold; if (bMergeIntoPolygon) { CollisionPolygonPoints.Insert(BoxPoints2D[2], InsertPoint + 1); CollisionPolygonPoints.Insert(BoxPoints2D[1], InsertPoint + 1); } else { InsertConvexCollisionDataFromPolygon(CollisionPolygonPoints); CollisionPolygonPoints.Empty(0); CollisionPolygonPoints.Add(BoxPoints2D[0]); CollisionPolygonPoints.Add(BoxPoints2D[1]); CollisionPolygonPoints.Add(BoxPoints2D[2]); CollisionPolygonPoints.Add(BoxPoints2D[3]); } } else { CollisionPolygonPoints.Add(BoxPoints2D[0]); CollisionPolygonPoints.Add(BoxPoints2D[1]); CollisionPolygonPoints.Add(BoxPoints2D[2]); CollisionPolygonPoints.Add(BoxPoints2D[3]); } #else FKBoxElem Box; // The spline is never "closed" properly right now //if (bClosedSpline) //{ // //@TODO: Add proper support for this! // Box.SetTransform(LocalTransformAtCenter); // Box.X = NominalWidth * HorizontalScale; // Box.Y = CollisionThickness; // Box.Z = 30.0f; //} //else { Box.SetTransform(LocalTransformAtCenter); Box.X = NominalWidth * HorizontalScale; Box.Y = CollisionThickness; Box.Z = FMath::Max<float>(1.0f, FMath::Abs<float>(Rule->CollisionOffset * 0.5f)); } CachedBodySetup->AggGeom.BoxElems.Add(Box); #endif } if (NewSprite) { FPaperTerrainSpriteGeometry& MaterialBatch = *new (GeneratedSpriteGeometry) FPaperTerrainSpriteGeometry(); //@TODO: Look up the existing one instead MaterialBatch.Material = NewSprite->GetDefaultMaterial(); MaterialBatch.DrawOrder = Rule->DrawOrder; FSpriteDrawCallRecord& Record = *new (MaterialBatch.Records) FSpriteDrawCallRecord(); Record.BuildFromSprite(NewSprite); Record.Color = TerrainColor; // Work out if the sprite is likely to be bent > X deg (folded over itself) const FVector ForwardVector(1, 0, 0); const FTransform LocalTransformAtBack(GetTransformAtDistance(Position - 0.5f * NominalWidth * HorizontalScale)); FVector StartForwardVector = LocalTransformAtBack.TransformVector(ForwardVector).GetSafeNormal(); const FTransform LocalTransformAtFront(GetTransformAtDistance(Position + 0.5f * NominalWidth * HorizontalScale)); FVector EndForwardVector = LocalTransformAtFront.TransformVector(ForwardVector).GetSafeNormal(); bool bIsSpriteBent = FVector::DotProduct(StartForwardVector, EndForwardVector) < 0.0f;// 0.7071f; // (45deg looks worse) for (FVector4& XYUV : Record.RenderVerts) { FTransform LocalTransformAtX(GetTransformAtDistance(Position + (XYUV.X * HorizontalScale))); // When the quad is overly bent, inherit rotation from the start of the quad to unfold it if (bIsSpriteBent) { LocalTransformAtX.SetRotation(LocalTransformAtFront.GetRotation()); } const FVector SourceVector = (XYUV.Y * PaperAxisY); const FVector NewVector = LocalTransformAtX.TransformPosition(SourceVector); const float NewX = FVector::DotProduct(NewVector, PaperAxisX); const float NewY = FVector::DotProduct(NewVector, PaperAxisY); XYUV.X = NewX; XYUV.Y = NewY; } } } } //@TODO: Sort by draw order first, materials next - Merge batches with the same material GeneratedSpriteGeometry.Sort([](const FPaperTerrainSpriteGeometry& A, const FPaperTerrainSpriteGeometry& B) { return B.DrawOrder > A.DrawOrder; }); #ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS // For whatever is remaining if (CollisionPolygonPoints.Num() > 0) { InsertConvexCollisionDataFromPolygon(CollisionPolygonPoints); } #endif }
/** * Function for adding a box collision primitive to the supplied collision geometry based on the mesh of the box. * * We keep a list of triangle normals found so far. For each normal direction, * we should have 2 distances from the origin (2 parallel box faces). If the * mesh is a box, we should have 3 distinct normal directions, and 2 distances * found for each. The difference between these distances should be the box * dimensions. The 3 directions give us the key axes, and therefore the * box transformation matrix. This shouldn't rely on any vertex-ordering on * the triangles (normals are compared +ve & -ve). It also shouldn't matter * about how many triangles make up each side (but it will take longer). * We get the centre of the box from the centre of its AABB. */ void AddBoxGeomFromTris( const TArray<FPoly>& Tris, FKAggregateGeom* AggGeom, const TCHAR* ObjName ) { TArray<FPlaneInfo> Planes; for(int32 i=0; i<Tris.Num(); i++) { bool bFoundPlane = false; for(int32 j=0; j<Planes.Num() && !bFoundPlane; j++) { // if this triangle plane is already known... if( AreParallel( Tris[i].Normal, Planes[j].Normal ) ) { // Always use the same normal when comparing distances, to ensure consistent sign. float Dist = Tris[i].Vertices[0] | Planes[j].Normal; // we only have one distance, and its not that one, add it. if( Planes[j].DistCount == 1 && !AreEqual(Dist, Planes[j].PlaneDist[0]) ) { Planes[j].PlaneDist[1] = Dist; Planes[j].DistCount = 2; } // if we have a second distance, and its not that either, something is wrong. else if( Planes[j].DistCount == 2 && !AreEqual(Dist, Planes[j].PlaneDist[1]) ) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Found more than 2 planes with different distances."), ObjName); return; } bFoundPlane = true; } } // If this triangle does not match an existing plane, add to list. if(!bFoundPlane) { check( Planes.Num() < Tris.Num() ); FPlaneInfo NewPlane; NewPlane.Normal = Tris[i].Normal; NewPlane.DistCount = 1; NewPlane.PlaneDist[0] = Tris[i].Vertices[0] | NewPlane.Normal; Planes.Add(NewPlane); } } // Now we have our candidate planes, see if there are any problems // Wrong number of planes. if(Planes.Num() != 3) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Not very box-like (need 3 sets of planes)."), ObjName); return; } // If we don't have 3 pairs, we can't carry on. if((Planes[0].DistCount != 2) || (Planes[1].DistCount != 2) || (Planes[2].DistCount != 2)) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Incomplete set of planes (need 2 per axis)."), ObjName); return; } FMatrix BoxTM = FMatrix::Identity; BoxTM.SetAxis(0, Planes[0].Normal); BoxTM.SetAxis(1, Planes[1].Normal); // ensure valid TM by cross-product FVector ZAxis = Planes[0].Normal ^ Planes[1].Normal; if( !AreParallel(ZAxis, Planes[2].Normal) ) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Box axes are not perpendicular."), ObjName); return; } BoxTM.SetAxis(2, ZAxis); // OBB centre == AABB centre. FBox Box(0); for(int32 i=0; i<Tris.Num(); i++) { Box += Tris[i].Vertices[0]; Box += Tris[i].Vertices[1]; Box += Tris[i].Vertices[2]; } BoxTM.SetOrigin( Box.GetCenter() ); // Allocate box in array FKBoxElem BoxElem; BoxElem.SetTransform( FTransform( BoxTM ) ); // distance between parallel planes is box edge lengths. BoxElem.X = FMath::Abs(Planes[0].PlaneDist[0] - Planes[0].PlaneDist[1]); BoxElem.Y = FMath::Abs(Planes[1].PlaneDist[0] - Planes[1].PlaneDist[1]); BoxElem.Z = FMath::Abs(Planes[2].PlaneDist[0] - Planes[2].PlaneDist[1]); AggGeom->BoxElems.Add(BoxElem); }