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 ); } }
void UEditorEngine::polySplitOverlappingEdges( TArray<FPoly>* InPolyList, TArray<FPoly>* InResult ) { InResult->Empty(); for( int32 poly = 0 ; poly < InPolyList->Num() ; poly++ ) { FPoly* SrcPoly = &(*InPolyList)[poly]; FPoly NewPoly = *SrcPoly; for( int32 edge = 0 ; edge < SrcPoly->Vertices.Num() ; edge++ ) { FEdge SrcEdge = FEdge( SrcPoly->Vertices[edge], SrcPoly->Vertices[ edge+1 < SrcPoly->Vertices.Num() ? edge+1 : 0 ] ); FPlane SrcEdgePlane( SrcEdge.Vertex[0], SrcEdge.Vertex[1], SrcEdge.Vertex[0] + (SrcPoly->Normal * 16) ); for( int32 poly2 = 0 ; poly2 < InPolyList->Num() ; poly2++ ) { FPoly* CmpPoly = &(*InPolyList)[poly2]; // We can't compare to ourselves. if( CmpPoly == SrcPoly ) continue; for( int32 edge2 = 0 ; edge2 < CmpPoly->Vertices.Num() ; edge2++ ) { FEdge CmpEdge = FEdge( CmpPoly->Vertices[edge2], CmpPoly->Vertices[ edge2+1 < CmpPoly->Vertices.Num() ? edge2+1 : 0 ] ); // If both vertices on this edge lie on the same plane as the original edge, create // a sphere around the original 2 vertices. If either of this edges vertices are inside of // that sphere, we need to split the original edge by adding a vertex to it's poly. if( FMath::Abs( FVector::PointPlaneDist( CmpEdge.Vertex[0], SrcEdge.Vertex[0], SrcEdgePlane ) ) < THRESH_POINT_ON_PLANE && FMath::Abs( FVector::PointPlaneDist( CmpEdge.Vertex[1], SrcEdge.Vertex[0], SrcEdgePlane ) ) < THRESH_POINT_ON_PLANE ) { // // Check THIS edge against the SOURCE edge // FVector Dir = SrcEdge.Vertex[1] - SrcEdge.Vertex[0]; Dir.Normalize(); float Dist = FVector::Dist( SrcEdge.Vertex[1], SrcEdge.Vertex[0] ); FVector Origin = SrcEdge.Vertex[0] + (Dir * (Dist / 2.0f)); float Radius = Dist / 2.0f; for( int32 vtx = 0 ; vtx < 2 ; vtx++ ) if( FVector::Dist( Origin, CmpEdge.Vertex[vtx] ) && FVector::Dist( Origin, CmpEdge.Vertex[vtx] ) < Radius ) NewPoly.InsertVertex( edge2+1, CmpEdge.Vertex[vtx] ); } } } } new(*InResult)FPoly( NewPoly ); } }
// // Cut a partitioning poly by a list of polys, and add the resulting inside pieces to the // front list and back list. // static void SplitPartitioner ( UModel* Model, FPoly** PolyList, FPoly** FrontList, FPoly** BackList, int32 n, int32 nPolys, int32& nFront, int32& nBack, FPoly InfiniteEdPoly, TArray<FPoly*>& AllocatedFPolys ) { FPoly FrontPoly,BackPoly; while( n < nPolys ) { FPoly* Poly = PolyList[n]; switch( InfiniteEdPoly.SplitWithPlane(Poly->Vertices[0],Poly->Normal,&FrontPoly,&BackPoly,0) ) { case SP_Coplanar: // May occasionally happen. // UE_LOG(LogBSPOps, Log, TEXT("FilterBound: Got inficoplanar") ); break; case SP_Front: // Shouldn't happen if hull is correct. // UE_LOG(LogBSPOps, Log, TEXT("FilterBound: Got infifront") ); return; case SP_Split: InfiniteEdPoly = BackPoly; break; case SP_Back: break; } n++; } FPoly* New = new FPoly; *New = InfiniteEdPoly; New->Reverse(); New->iBrushPoly |= 0x40000000; FrontList[nFront++] = New; AllocatedFPolys.Add( New ); New = new FPoly; *New = InfiniteEdPoly; BackList[nBack++] = New; AllocatedFPolys.Add( New ); }
FVector FEdModeTexture::GetWidgetLocation() const { for ( TSelectedSurfaceIterator<> It(GetWorld()) ; It ; ++It ) { FBspSurf* Surf = *It; ABrush* BrushActor = ( ABrush* )Surf->Actor; if( BrushActor ) { FPoly* poly = &BrushActor->Brush->Polys->Element[ Surf->iBrushPoly ]; return BrushActor->ActorToWorld().TransformPosition( poly->GetMidPoint() ); } } return FEdMode::GetWidgetLocation(); }
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; }
/** * 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 }
FPoly FPoly::BuildInfiniteFPoly(const FPlane& InPlane) { FVector Axis1, Axis2; // Find two non-problematic axis vectors. InPlane.FindBestAxisVectors( Axis1, Axis2 ); // Set up the FPoly. FPoly EdPoly; EdPoly.Init(); EdPoly.Normal.X = InPlane.X; EdPoly.Normal.Y = InPlane.Y; EdPoly.Normal.Z = InPlane.Z; EdPoly.Base = EdPoly.Normal * InPlane.W; EdPoly.Vertices.Add( EdPoly.Base + Axis1*HALF_WORLD_MAX + Axis2*HALF_WORLD_MAX ); EdPoly.Vertices.Add( EdPoly.Base - Axis1*HALF_WORLD_MAX + Axis2*HALF_WORLD_MAX ); EdPoly.Vertices.Add( EdPoly.Base - Axis1*HALF_WORLD_MAX - Axis2*HALF_WORLD_MAX ); EdPoly.Vertices.Add( EdPoly.Base + Axis1*HALF_WORLD_MAX - Axis2*HALF_WORLD_MAX ); return EdPoly; }
// // Build an FPoly representing an "infinite" plane (which exceeds the maximum // dimensions of the world in all directions) for a particular Bsp node. // FPoly FBSPOps::BuildInfiniteFPoly( UModel* Model, int32 iNode ) { FBspNode &Node = Model->Nodes [iNode ]; FBspSurf &Poly = Model->Surfs [Node.iSurf ]; FVector Base = Poly.Plane * Poly.Plane.W; FVector Normal = Poly.Plane; FVector Axis1,Axis2; // Find two non-problematic axis vectors. Normal.FindBestAxisVectors( Axis1, Axis2 ); // Set up the FPoly. FPoly EdPoly; EdPoly.Init(); EdPoly.Normal = Normal; EdPoly.Base = Base; new(EdPoly.Vertices) FVector(Base + Axis1*WORLD_MAX + Axis2*WORLD_MAX); new(EdPoly.Vertices) FVector(Base - Axis1*WORLD_MAX + Axis2*WORLD_MAX); new(EdPoly.Vertices) FVector(Base - Axis1*WORLD_MAX - Axis2*WORLD_MAX); new(EdPoly.Vertices) FVector(Base + Axis1*WORLD_MAX - Axis2*WORLD_MAX); return EdPoly; }
/** * Rotates the specified brush's vertices. */ void FBSPOps::RotateBrushVerts(ABrush* Brush, const FRotator& Rotation, bool bClearComponents) { if(Brush->BrushComponent->Brush && Brush->BrushComponent->Brush->Polys) { for( int32 poly = 0 ; poly < Brush->BrushComponent->Brush->Polys->Element.Num() ; poly++ ) { FPoly* Poly = &(Brush->BrushComponent->Brush->Polys->Element[poly]); // Rotate the vertices. for( int32 vertex = 0 ; vertex < Poly->Vertices.Num() ; vertex++ ) { Poly->Vertices[vertex] = Brush->GetPrePivot() + FRotationMatrix( Rotation ).TransformVector( Poly->Vertices[vertex] - Brush->GetPrePivot() ); } Poly->Base = Brush->GetPrePivot() + FRotationMatrix( Rotation ).TransformVector( Poly->Base - Brush->GetPrePivot() ); // Rotate the texture vectors. Poly->TextureU = FRotationMatrix( Rotation ).TransformVector( Poly->TextureU ); Poly->TextureV = FRotationMatrix( Rotation ).TransformVector( Poly->TextureV ); // Recalc the normal for the poly. Poly->Normal = FVector::ZeroVector; Poly->Finalize(Brush,0); } Brush->BrushComponent->Brush->BuildBound(); if( !Brush->IsStaticBrush() ) { csgPrepMovingBrush( Brush ); } if ( bClearComponents ) { Brush->ReregisterAllComponents(); } } }
FPoly operator*(const FPoly& a,const FPoly& b) { FPoly prod; fterm *iptr,*pos; fterm *ptr=b.start; if (&a==&b) { // squaring pos=NULL; while (ptr!=NULL) { // diagonal terms pos=prod.addterm(ptr->an*ptr->an,ptr->n+ptr->n,pos); ptr=ptr->next; } ptr=b.start; while (ptr!=NULL) { // above the diagonal iptr=ptr->next; pos=NULL; while (iptr!=NULL) { pos=prod.addterm(2*ptr->an*iptr->an,ptr->n+iptr->n,pos); iptr=iptr->next; } ptr=ptr->next; } } else while (ptr!=NULL) { FPoly t=a; t.multerm(ptr->an,ptr->n); ptr=ptr->next; prod+=t; } return prod; }
FPoly FPoly::BuildAndCutInfiniteFPoly(const FPlane& InPlane, const TArray<FPlane>& InCutPlanes, ABrush* InOwnerBrush) { FPoly PolyMerged = BuildInfiniteFPoly( InPlane ); PolyMerged.Finalize( InOwnerBrush, 1 ); FPoly Front, Back; int32 result; for( int32 p = 0 ; p < InCutPlanes.Num() ; ++p ) { const FPlane* Plane = &InCutPlanes[p]; result = PolyMerged.SplitWithPlane( Plane->GetSafeNormal() * Plane->W, Plane->GetSafeNormal(), &Front, &Back, 1 ); if( result == SP_Split ) { PolyMerged = Back; } } PolyMerged.Reverse(); return PolyMerged; }
int32 FPoly::Faces( const FPoly &Test ) const { // Coplanar implies not facing. if( IsCoplanar( Test ) ) return 0; // If this poly is frontfaced relative to all of Test's points, they're not facing. for( int32 i=0; i<Test.Vertices.Num(); i++ ) { if( !IsBackfaced( Test.Vertices[i] ) ) { // If Test is frontfaced relative to on or more of this poly's points, they're facing. for( i=0; i<Vertices.Num(); i++ ) if( Test.IsBackfaced( Vertices[i] ) ) return 1; return 0; } } return 0; }
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; }
// // Pick a splitter poly then split a pool of polygons into front and back polygons and // recurse. // // iParent = Parent Bsp node, or INDEX_NONE if this is the root node. // IsFront = 1 if this is the front node of iParent, 0 of back (undefined if iParent==INDEX_NONE) // void FBSPOps::SplitPolyList ( UModel *Model, int32 iParent, ENodePlace NodePlace, int32 NumPolys, FPoly **PolyList, EBspOptimization Opt, int32 Balance, int32 PortalBias, int32 RebuildSimplePolys ) { FMemMark Mark(FMemStack::Get()); // Keeping track of allocated FPoly structures to delete later on. TArray<FPoly*> AllocatedFPolys; // To account for big EdPolys split up. int32 NumPolysToAlloc = NumPolys + 8 + NumPolys/4; int32 NumFront=0; FPoly **FrontList = new(FMemStack::Get(),NumPolysToAlloc)FPoly*; int32 NumBack =0; FPoly **BackList = new(FMemStack::Get(),NumPolysToAlloc)FPoly*; FPoly *SplitPoly = FindBestSplit( NumPolys, PolyList, Opt, Balance, PortalBias ); // Add the splitter poly to the Bsp with either a new BspSurf or an existing one. if( RebuildSimplePolys ) { SplitPoly->iLink = Model->Surfs.Num(); } int32 iOurNode = bspAddNode(Model,iParent,NodePlace,0,SplitPoly); int32 iPlaneNode = iOurNode; // Now divide all polygons in the pool into (A) polygons that are // in front of Poly, and (B) polygons that are in back of Poly. // Coplanar polys are inserted immediately, before recursing. // If any polygons are split by Poly, we ignrore the original poly, // split it into two polys, and add two new polys to the pool. FPoly *FrontEdPoly = new FPoly; FPoly *BackEdPoly = new FPoly; // Keep track of allocations. AllocatedFPolys.Add( FrontEdPoly ); AllocatedFPolys.Add( BackEdPoly ); for( int32 i=0; i<NumPolys; i++ ) { FPoly *EdPoly = PolyList[i]; if( EdPoly == SplitPoly ) { continue; } switch( EdPoly->SplitWithPlane( SplitPoly->Vertices[0], SplitPoly->Normal, FrontEdPoly, BackEdPoly, 0 ) ) { case SP_Coplanar: if( RebuildSimplePolys ) { EdPoly->iLink = Model->Surfs.Num()-1; } iPlaneNode = bspAddNode( Model, iPlaneNode, NODE_Plane, 0, EdPoly ); break; case SP_Front: FrontList[NumFront++] = PolyList[i]; break; case SP_Back: BackList[NumBack++] = PolyList[i]; break; case SP_Split: // Create front & back nodes. FrontList[NumFront++] = FrontEdPoly; BackList [NumBack ++] = BackEdPoly; FrontEdPoly = new FPoly; BackEdPoly = new FPoly; // Keep track of allocations. AllocatedFPolys.Add( FrontEdPoly ); AllocatedFPolys.Add( BackEdPoly ); break; } } // Recursively split the front and back pools. if( NumFront > 0 ) SplitPolyList( Model, iOurNode, NODE_Front, NumFront, FrontList, Opt, Balance, PortalBias, RebuildSimplePolys ); if( NumBack > 0 ) SplitPolyList( Model, iOurNode, NODE_Back, NumBack, BackList, Opt, Balance, PortalBias, RebuildSimplePolys ); // Delete FPolys allocated above. We cannot use FMemStack::Get() for FPoly as the array data FPoly contains will be allocated in regular memory. for( int32 i=0; i<AllocatedFPolys.Num(); i++ ) { FPoly* AllocatedFPoly = AllocatedFPolys[i]; delete AllocatedFPoly; } Mark.Pop(); }
// // Find the best splitting polygon within a pool of polygons, and return its // index (into the PolyList array). // static FPoly *FindBestSplit ( int32 NumPolys, FPoly** PolyList, FBSPOps::EBspOptimization Opt, int32 Balance, int32 InPortalBias ) { check(NumPolys>0); // No need to test if only one poly. if( NumPolys==1 ) return PolyList[0]; FPoly *Poly, *Best=NULL; float Score, BestScore; int32 i, Index, j, Inc; int32 Splits, Front, Back, Coplanar, AllSemiSolids; //PortalBias -- added by Legend on 4/12/2000 float PortalBias = InPortalBias / 100.0f; Balance &= 0xFF; // keep only the low byte to recover "Balance" //UE_LOG(LogBSPOps, Log, TEXT("Balance=%d PortalBias=%f"), Balance, PortalBias ); if (Opt==FBSPOps::BSP_Optimal) Inc = 1; // Test lots of nodes. else if (Opt==FBSPOps::BSP_Good) Inc = FMath::Max(1,NumPolys/20); // Test 20 nodes. else /* BSP_Lame */ Inc = FMath::Max(1,NumPolys/4); // Test 4 nodes. // See if there are any non-semisolid polygons here. for( i=0; i<NumPolys; i++ ) if( !(PolyList[i]->PolyFlags & PF_AddLast) ) break; AllSemiSolids = (i>=NumPolys); // Search through all polygons in the pool and find: // A. The number of splits each poly would make. // B. The number of front and back nodes the polygon would create. // C. Number of coplanars. BestScore = 0; for( i=0; i<NumPolys; i+=Inc ) { Splits = Front = Back = Coplanar = 0; Index = i-1; do { Index++; Poly = PolyList[Index]; } while( Index<(i+Inc) && Index<NumPolys && ( (Poly->PolyFlags & PF_AddLast) && !(Poly->PolyFlags & PF_Portal) ) && !AllSemiSolids ); if( Index>=i+Inc || Index>=NumPolys ) continue; for( j=0; j<NumPolys; j+=Inc ) if( j != Index ) { FPoly *OtherPoly = PolyList[j]; switch( OtherPoly->SplitWithPlaneFast( FPlane( Poly->Vertices[0], Poly->Normal), NULL, NULL ) ) { case SP_Coplanar: Coplanar++; break; case SP_Front: Front++; break; case SP_Back: Back++; break; case SP_Split: // Disfavor splitting polys that are zone portals. if( !(OtherPoly->PolyFlags & PF_Portal) ) Splits++; else Splits += 16; break; } } // added by Legend 1/31/1999 // Score optimization: minimize cuts vs. balance tree (as specified in BSP Rebuilder dialog) Score = ( 100.0 - float(Balance) ) * Splits + float(Balance) * FMath::Abs( Front - Back ); if( Poly->PolyFlags & PF_Portal ) { // PortalBias -- added by Legend on 4/12/2000 // // PortalBias enables level designers to control the effect of Portals on the BSP. // This effect can range from 0.0 (ignore portals), to 1.0 (portals cut everything). // // In builds prior to this (since the 221 build dating back to 1/31/1999) the bias // has been 1.0 causing the portals to cut the BSP in ways that will potentially // degrade level performance, and increase the BSP complexity. // // By setting the bias to a value between 0.3 and 0.7 the positive effects of // the portals are preserved without giving them unreasonable priority in the BSP. // // Portals should be weighted high enough in the BSP to separate major parts of the // level from each other (pushing entire rooms down the branches of the BSP), but // should not be so high that portals cut through adjacent geometry in a way that // increases complexity of the room being (typically, accidentally) cut. // Score -= ( 100.0 - float(Balance) ) * Splits * PortalBias; // ignore PortalBias of the split polys -- bias toward portal selection for cutting planes! } //UE_LOG(LogBSPOps, Log, " %4d: Score = %f (Front = %4d, Back = %4d, Splits = %4d, Flags = %08X)", Index, Score, Front, Back, Splits, Poly->PolyFlags ); //LEC if( Score<BestScore || !Best ) { Best = Poly; BestScore = Score; } } check(Best); return Best; }
// // Recursively filter a set of polys defining a convex hull down the Bsp, // splitting it into two halves at each node and adding in the appropriate // face polys at splits. // static void FilterBound ( UModel* Model, FBox* ParentBound, int32 iNode, FPoly** PolyList, int32 nPolys, int32 Outside ) { FMemMark Mark(FMemStack::Get()); FBspNode& Node = Model->Nodes [iNode]; FBspSurf& Surf = Model->Surfs [Node.iSurf]; FVector Base = Surf.Plane * Surf.Plane.W; FVector& Normal = Model->Vectors[Surf.vNormal]; FBox Bound(0); Bound.Min.X = Bound.Min.Y = Bound.Min.Z = +WORLD_MAX; Bound.Max.X = Bound.Max.Y = Bound.Max.Z = -WORLD_MAX; // Split bound into front half and back half. FPoly** FrontList = new(FMemStack::Get(),nPolys*2+16)FPoly*; int32 nFront=0; FPoly** BackList = new(FMemStack::Get(),nPolys*2+16)FPoly*; int32 nBack=0; // Keeping track of allocated FPoly structures to delete later on. TArray<FPoly*> AllocatedFPolys; FPoly* FrontPoly = new FPoly; FPoly* BackPoly = new FPoly; // Keep track of allocations. AllocatedFPolys.Add( FrontPoly ); AllocatedFPolys.Add( BackPoly ); for( int32 i=0; i<nPolys; i++ ) { FPoly *Poly = PolyList[i]; switch( Poly->SplitWithPlane( Base, Normal, FrontPoly, BackPoly, 0 ) ) { case SP_Coplanar: // UE_LOG(LogBSPOps, Log, TEXT("FilterBound: Got coplanar") ); FrontList[nFront++] = Poly; BackList[nBack++] = Poly; break; case SP_Front: FrontList[nFront++] = Poly; break; case SP_Back: BackList[nBack++] = Poly; break; case SP_Split: FrontList[nFront++] = FrontPoly; BackList [nBack++] = BackPoly; FrontPoly = new FPoly; BackPoly = new FPoly; // Keep track of allocations. AllocatedFPolys.Add( FrontPoly ); AllocatedFPolys.Add( BackPoly ); break; default: UE_LOG(LogBSPOps, Fatal, TEXT("FZoneFilter::FilterToLeaf: Unknown split code") ); } } if( nFront && nBack ) { // Add partitioner plane to front and back. FPoly InfiniteEdPoly = FBSPOps::BuildInfiniteFPoly( Model, iNode ); InfiniteEdPoly.iBrushPoly = iNode; SplitPartitioner(Model,PolyList,FrontList,BackList,0,nPolys,nFront,nBack,InfiniteEdPoly,AllocatedFPolys); } else { // if( !nFront ) UE_LOG(LogBSPOps, Log, TEXT("FilterBound: Empty fronthull") ); // if( !nBack ) UE_LOG(LogBSPOps, Log, TEXT("FilterBound: Empty backhull") ); } // Recursively update all our childrens' bounding volumes. if( nFront > 0 ) { if( Node.iFront != INDEX_NONE ) FilterBound( Model, &Bound, Node.iFront, FrontList, nFront, Outside || Node.IsCsg() ); else if( Outside || Node.IsCsg() ) UpdateBoundWithPolys( Bound, FrontList, nFront ); else UpdateConvolutionWithPolys( Model, iNode, FrontList, nFront ); } if( nBack > 0 ) { if( Node.iBack != INDEX_NONE) FilterBound( Model, &Bound,Node.iBack, BackList, nBack, Outside && !Node.IsCsg() ); else if( Outside && !Node.IsCsg() ) UpdateBoundWithPolys( Bound, BackList, nBack ); else UpdateConvolutionWithPolys( Model, iNode, BackList, nBack ); } // Update parent bound to enclose this bound. if( ParentBound ) *ParentBound += Bound; // Delete FPolys allocated above. We cannot use FMemStack::Get() for FPoly as the array data FPoly contains will be allocated in regular memory. for( int32 i=0; i<AllocatedFPolys.Num(); i++ ) { FPoly* AllocatedFPoly = AllocatedFPolys[i]; delete AllocatedFPoly; } Mark.Pop(); }
void class_poly(Complex& lam,Float *Fi2,Big A,Big B,Big C,Big D,BOOL conj,BOOL P1363) { Big ac,l,t; int i,j,k,g,e,m,n,dm8; Complex cinv; if (P1363) { g=1; if (D%3==0) g=3; ac=A*C; if (ac%2==1) { j=0; l=A-C+A*A*C; } if (C%2==0) j=1; if (A%2==0) j=2; if (A%2==0) { t=(C*C-1)/8; if (t%2==0) m=1; else m=-1; } else { t=(A*A-1)/8; if (t%2==0) m=1; else m=-1; } dm8=D%8; i=geti(D); k=getk(D); switch (dm8) { case 1: case 2: n=m; if (C%2==0) l=A+2*C-A*C*C; if (A%2==0) l=A-C-A*C*C; break; case 3: if (ac%2==1) n=1; else n=-m; if (C%2==0) l=A+2*C-A*C*C; if (A%2==0) l=A-C+5*A*C*C; break; case 5: n=1; if (C%2==0) l=A-C+A*A*C; if (A%2==0) l=A-C-A*C*C; break; case 6: n=m; if (C%2==0) l=A+2*C-A*C*C; if (A%2==0) l=A-C-A*C*C; break; case 7: if (ac%2==0) n=1; else n=m; if (C%2==0) l=A+2*C-A*C*C; if (A%2==0) l=A-C-A*C*C; break; default: break; } e=(k*B*l)%48; if (e<0) e+=48; cinv=pow(lam,e); cinv*=(n*Fi2[i]); cinv=pow(cinv*pow(F(j,A,B,C,D,0),k),g); } else { int N=getN(D); // adjust A and B if (N==2) { j=3; if (A%3!=0) { if (B%3!=0) { if ((B+A+A)%3!=0) B+=(4*A); else B+=(A+A); } } else { if (B%3!=0) { if (C%3!=0) { if ((C+B)%3!=0) B+=(4*A); else B+=(A+A); } } } A*=3; } else { j=4; if ((A%N)==0) { A=C; B=-B; } while (B%N!=0) B+=(A+A); A*=N; } cinv=F(j,A,B,C,D,N); } // multiply polynomial by new term(s) FPoly F; if (conj) { // conjugate pair // t^2-2a+(a^2+b^2) , where cinv=a+ib F.addterm((Float)1,2); F.addterm(-2*real(cinv),1); F.addterm(real(cinv)*real(cinv)+imaginary(cinv)*imaginary(cinv),0); } else { // t-cinv F.addterm((Float)1,1); F.addterm(-real(cinv),0); // store as a linear polynomial, or combine 2 to make a quadratic if (T[0].iszero()) { T[0]=F; return; } else { F=T[0]*F; // got a quadratic T[0].clear(); } } // accumulate Polynomial as 2^m degree components // This allows the use of karatsuba via the "special" function // This is the time critical bit.... for (i=1;;i++) { if (T[i].iszero()) { T[i]=F; // store this 2^i degree polynomial break; } else { F=special(T[i],F); // e.g. if i=1 two quadratics make a quartic.. T[i].clear(); } } }
BOOL get_curve(Complex& lam,Float *Fi2,ofstream& ofile,Big r,Big other_r,Big ord,unsigned long D,Big p,Big W,int m,BOOL always) { Poly polly; Big A0,B0,k; BOOL unstable; char c; int i,j,terms,class_number; BOOL P1363,pord=prime(ord); P1363=TRUE; if (!always && D>3 && D%8==3) P1363=FALSE; k=r; while (k%ord==0) k/=ord; if (!suppress) { if (D>1000 && D%3==0) cout << "D= " << D << " (Divisible by 3 - might need extra precision)" << endl; else cout << "D= " << D << endl; if (P1363) cout << "IEEE-1363 invariant" << endl; else { switch (getN(D)) { case 2: cout << "Gamma2 invariant" << endl; break; case 3: cout << "w3 invariant" << endl; break; case 5: cout << "w5 invariant" << endl; break; case 7: cout << "w7 invariant" << endl; break; case 13: cout << "w13 invariant" << endl; break; } } cout << "D mod 24 = " << D%24 << endl; if (prime(ord)) cout << "K= " << k << endl; } class_number=groups(lam,Fi2,D,FALSE,P1363); cout << "class number= " << class_number << endl; cout << "Group order= " << ord << endl; cout << "do you want to proceed with this one (Y/N)? " ; cin >> c; if (c=='N' || c=='n') { if (!suppress) cout << "finding a curve..." << endl; return FALSE; } modulo(p); // A.14.4.1 if (D==1) { A0=1; B0=0; } if (D==3) { A0=0; B0=1; } if (D!=1 && D!=3) { FPoly Acc; for (i=0;i<25;i++) T[i].clear(); if (!suppress) cout << "constructing polynomial"; groups(lam,Fi2,D,TRUE,P1363); // Construct Polynomial from its 2^m degree components.. for (i=24;i>=0;i--) { if (!T[i].iszero()) { Acc=T[i]; // find the first component.. T[i].clear(); break; } } if (i>0) { for (j=i-1;j>0;j--) { if (!T[j].iszero()) { Acc=special(Acc,T[j]); // special karatsuba function T[j].clear(); // multiply into accumulator } } if (!T[0].iszero()) { // check for a left-over linear poly Acc=Acc*T[0]; T[0].clear(); } } for (i=0;i<25;i++) T[i].clear(); terms=degree(Acc); Float f,rem; Big whole; int nbits,maxbits=0; unstable=FALSE; for (i=terms;i>=0;i--) { f=Acc.coeff(i); if (f>0) f+=makefloat(1,10000); else f-=makefloat(1,10000); whole=f.trunc(&rem); nbits=bits(whole); if (nbits>maxbits) maxbits=nbits; polly.addterm((ZZn)whole,i); if (fabs(rem)>makefloat(1,100)) { unstable=TRUE; break; } } Acc.clear(); if (!suppress) cout << endl; if (unstable) { if (!suppress) cout << "Curve abandoned - numerical instability!" << endl; if (!suppress) cout << "Curve abandoned - double MIRACL precision and try again!" << endl; if (!suppress) cout << "finding a curve..." << endl; return FALSE; } if (!suppress) { cout << polly << endl; cout << "Maximum precision required in bits= " << maxbits << endl; } } // save space with smaller miracl mirexit(); mip=mirsys(128,0); modulo(p); ECn pt,G; Big a,b,x,y; Big w,eps; int at; Poly g,spolly=polly; // smaller polly polly.clear(); forever { if (D!=1 && D!=3) { if (!suppress) cout << "Factoring polynomial of degree " << degree(spolly) << " ....." << endl; if (P1363) { if (W%2==0) { ZZn V; g=factor(spolly,1); V=-g.coeff(0); V=pow(V,24/(lgcd(D,3)*getk(D))); V*=pow((ZZn)2,(4*geti(D))/getk(D)); if (D%2!=0) V=-V; A0=(Big)((ZZn)(-3)*(V+64)*(V+16)); B0=(Big)((ZZn)2*(V+64)*(V+64)*(V-8)); } else { Poly V,w,w1,w2,w3,a,b; g=factor(spolly,3); if (D%3!=0) w.addterm(-1,24); else w.addterm(-256,8); V=w%g; w.clear(); w1=V; w2=V; w3=V; w1.addterm(64,0); w2.addterm(256,0); w3.addterm(-512,0); a=w1*w2; a.multerm(-3,0); a=a%g; b=w1*w1*w3; b.multerm(2,0); b=b%g; a=((a*a)*a)%g; b=(b*b)%g; for (int d=degree(g)-1;d>=0;d--) { ZZn t,c=a.coeff(d); if (c!=(ZZn)0) { t=b.coeff(d); A0=(Big)(c*t); B0=(Big)(c*t*t); if (d==1) break; } } } } else { ZZn X,J,X2,K; g=factor(spolly,1); X=-g.coeff(0); X2=X*X; switch (getN(D)) { case 2: J=X2*X; break; case 3: J=(pow((X+3),3)*(X+27))/X; break; case 5: J=pow((X2+10*X+5),3)/X; break; case 7: J=(pow((X2+5*X+1),3)*(X2+13*X+49))/X; break; case 13: J=(pow((X2*X2+7*X2*X+20*X2+19*X+1),3)*(X2+5*X+13))/X; default: break; } K=J/(J-1728); A0=-3*K; B0=2*K; } } // A.14.4.2 // but try -3 first, followed by small positive values for A a=A0; b=B0; at=-3; if (D==3) at=1; forever { if (D==1) { if (at<100) eps=(ZZn)at/(ZZn)A0; else eps=rand(p); a=modmult(A0,eps,p); } if (D==3) { if (at<100) eps=(ZZn)at/(ZZn)B0; else eps=rand(p); b=modmult(B0,eps,p); } if (D!=1 && D!=3) { if (at<100) { // transform a to be simple if possible w=(ZZn)at/ZZn(A0); if (jacobi(w,p)!=1) { if (at==-3) at=1; else at++; continue; } eps=sqrt(w,p); } else eps=rand(p); a=modmult(A0,pow(eps,2,p),p); b=modmult(B0,pow(eps,3,p),p); } ecurve(a,b,p,MR_PROJECTIVE); for (int xc=1;;xc++) { if (!pt.set((Big)xc)) continue; pt*=k; if (pt.iszero()) continue; break; } G=pt; // check its not the other one... if (r!=ord || !(other_r*G).iszero()) { pt*=ord; if (pt.iszero()) break; } if (at==-3) at=1; else at++; } if (a==(p-3)) a=-3; if (D!=1 && D!=3 && three && a!=-3) continue; // check MOV condition // A.12.1 BOOL MOV=TRUE; w=1; for (i=1;i<100;i++) { w=modmult(w,p,ord); if (w==1) { MOV=FALSE; if (!suppress) { if (i==1 || pord) cout << "\n*** Failed MOV condition - K = " << i << endl; else cout << "\n*** Failed MOV condition - K <= " << i << endl; } break; } } if (!suppress) { if (MOV) cout << "MOV condition OK" << endl; if (pord) cout << "\nCurve and Point Found" << endl; else cout << "\nCurve Found" << endl; } cout << "A= " << a << endl; cout << "B= " << b << endl; G.get(x,y); cout << "P= " << p << endl; cout << "R= " << ord; if (pord) { cout << " a " << bits(ord) << " bit prime" << endl; cout << "X= " << x << endl; cout << "Y= " << y << endl; } else cout << " NOT prime" << endl; cout << endl; if (D!=1 && D!=3 ) { cout << "Try for different random factorisation (Y/N)? "; cin >> c; if (c=='Y' || c=='y') continue; } break; }
void UPaperTerrainComponent::OnSplineEdited() { // Ensure we have the data structure for the desired collision method if (SpriteCollisionDomain == ESpriteCollisionMode::Use3DPhysics) { CachedBodySetup = NewObject<UBodySetup>(this); } else { CachedBodySetup = nullptr; } const float SlopeAnalysisTimeRate = 10.0f; const float FillRasterizationTimeRate = 100.0f; GeneratedSpriteGeometry.Empty(); if ((AssociatedSpline != nullptr) && (TerrainMaterial != nullptr)) { if (AssociatedSpline->ReparamStepsPerSegment != ReparamStepsPerSegment) { AssociatedSpline->ReparamStepsPerSegment = ReparamStepsPerSegment; AssociatedSpline->UpdateSpline(); } FRandomStream RandomStream(RandomSeed); const FInterpCurveVector& SplineInfo = AssociatedSpline->SplineInfo; float SplineLength = AssociatedSpline->GetSplineLength(); struct FTerrainRuleHelper { public: FTerrainRuleHelper(const FPaperTerrainMaterialRule* Rule) : StartWidth(0.0f) , EndWidth(0.0f) { for (const UPaperSprite* Sprite : Rule->Body) { if (Sprite != nullptr) { const float Width = GetSpriteRenderDataBounds2D(Sprite->BakedRenderData).GetSize().X; if (Width > 0.0f) { ValidBodies.Add(Sprite); ValidBodyWidths.Add(Width); } } } if (Rule->StartCap != nullptr) { const float Width = GetSpriteRenderDataBounds2D(Rule->StartCap->BakedRenderData).GetSize().X; if (Width > 0.0f) { StartWidth = Width; } } if (Rule->EndCap != nullptr) { const float Width = GetSpriteRenderDataBounds2D(Rule->EndCap->BakedRenderData).GetSize().X; if (Width > 0.0f) { EndWidth = Width; } } } float StartWidth; float EndWidth; TArray<const UPaperSprite*> ValidBodies; TArray<float> ValidBodyWidths; int32 GenerateBodyIndex(FRandomStream& InRandomStream) const { check(ValidBodies.Num() > 0); return InRandomStream.GetUnsignedInt() % ValidBodies.Num(); } }; // Split the spline into segments based on the slope rules in the material TArray<FTerrainSegment> Segments; FTerrainSegment* ActiveSegment = new (Segments) FTerrainSegment(); ActiveSegment->StartTime = 0.0f; ActiveSegment->EndTime = SplineLength; { float CurrentTime = 0.0f; while (CurrentTime < SplineLength) { const FTransform Frame(GetTransformAtDistance(CurrentTime)); const FVector UnitTangent = Frame.GetUnitAxis(EAxis::X); const float RawSlopeAngleRadians = FMath::Atan2(FVector::DotProduct(UnitTangent, PaperAxisY), FVector::DotProduct(UnitTangent, PaperAxisX)); const float RawSlopeAngle = FMath::RadiansToDegrees(RawSlopeAngleRadians); const float SlopeAngle = FMath::Fmod(FMath::UnwindDegrees(RawSlopeAngle) + 360.0f, 360.0f); const FPaperTerrainMaterialRule* DesiredRule = (TerrainMaterial->Rules.Num() > 0) ? &(TerrainMaterial->Rules[0]) : nullptr; for (const FPaperTerrainMaterialRule& TestRule : TerrainMaterial->Rules) { if ((SlopeAngle >= TestRule.MinimumAngle) && (SlopeAngle < TestRule.MaximumAngle)) { DesiredRule = &TestRule; } } if (ActiveSegment->Rule != DesiredRule) { if (ActiveSegment->Rule == nullptr) { ActiveSegment->Rule = DesiredRule; } else { ActiveSegment->EndTime = CurrentTime; // Segment is too small, delete it if (ActiveSegment->EndTime < ActiveSegment->StartTime + 2.0f * SegmentOverlapAmount) { Segments.Pop(false); } ActiveSegment = new (Segments)FTerrainSegment(); ActiveSegment->StartTime = CurrentTime; ActiveSegment->EndTime = SplineLength; ActiveSegment->Rule = DesiredRule; } } CurrentTime += SlopeAnalysisTimeRate; } } // Account for overlap for (FTerrainSegment& Segment : Segments) { Segment.StartTime -= SegmentOverlapAmount; Segment.EndTime += SegmentOverlapAmount; } // Convert those segments to actual geometry for (FTerrainSegment& Segment : Segments) { check(Segment.Rule); FTerrainRuleHelper RuleHelper(Segment.Rule); float RemainingSegStart = Segment.StartTime + RuleHelper.StartWidth; float RemainingSegEnd = Segment.EndTime - RuleHelper.EndWidth; const float BodyDistance = RemainingSegEnd - RemainingSegStart; float DistanceBudget = BodyDistance; bool bUseBodySegments = (DistanceBudget > 0.0f) && (RuleHelper.ValidBodies.Num() > 0); // Add the start cap if (RuleHelper.StartWidth > 0.0f) { new (Segment.Stamps) FTerrainSpriteStamp(Segment.Rule->StartCap, Segment.StartTime + RuleHelper.StartWidth * 0.5f, /*bIsEndCap=*/ bUseBodySegments); } // Add body segments if (bUseBodySegments) { int32 NumSegments = 0; float Position = RemainingSegStart; while (DistanceBudget > 0.0f) { const int32 BodyIndex = RuleHelper.GenerateBodyIndex(RandomStream); const UPaperSprite* Sprite = RuleHelper.ValidBodies[BodyIndex]; const float Width = RuleHelper.ValidBodyWidths[BodyIndex]; if ((NumSegments > 0) && ((Width * 0.5f) > DistanceBudget)) { break; } new (Segment.Stamps) FTerrainSpriteStamp(Sprite, Position + (Width * 0.5f), /*bIsEndCap=*/ false); DistanceBudget -= Width; Position += Width; ++NumSegments; } const float UsedSpace = (BodyDistance - DistanceBudget); const float OverallScaleFactor = BodyDistance / UsedSpace; // Stretch body segments float PositionCorrectionSum = 0.0f; for (int32 Index = 0; Index < NumSegments; ++Index) { FTerrainSpriteStamp& Stamp = Segment.Stamps[Index + (Segment.Stamps.Num() - NumSegments)]; const float WidthChange = (OverallScaleFactor - 1.0f) * Stamp.NominalWidth; const float FirstGapIsSmallerFactor = (Index == 0) ? 0.5f : 1.0f; PositionCorrectionSum += WidthChange * FirstGapIsSmallerFactor; Stamp.Scale = OverallScaleFactor; Stamp.Time += PositionCorrectionSum; } } else { // Stretch endcaps } // Add the end cap if (RuleHelper.EndWidth > 0.0f) { new (Segment.Stamps) FTerrainSpriteStamp(Segment.Rule->EndCap, Segment.EndTime - RuleHelper.EndWidth * 0.5f, /*bIsEndCap=*/ bUseBodySegments); } } // Convert stamps into geometry SpawnSegments(Segments, !bClosedSpline || (bClosedSpline && !bFilledSpline)); // Generate the background if the spline is closed if (bClosedSpline && bFilledSpline) { // Create a polygon from the spline FBox2D SplineBounds(ForceInit); TArray<FVector2D> SplinePolyVertices2D; TArray<float> SplineEdgeOffsetAmounts; { float CurrentTime = 0.0f; while (CurrentTime < SplineLength) { const float Param = AssociatedSpline->SplineReparamTable.Eval(CurrentTime, 0.0f); const FVector Position3D = AssociatedSpline->SplineInfo.Eval(Param, FVector::ZeroVector); const FVector2D Position2D = FVector2D(FVector::DotProduct(Position3D, PaperAxisX), FVector::DotProduct(Position3D, PaperAxisY)); SplineBounds += Position2D; SplinePolyVertices2D.Add(Position2D); // Find the collision offset for this sample point float CollisionOffset = 0; for (int SegmentIndex = 0; SegmentIndex < Segments.Num(); ++SegmentIndex) { FTerrainSegment& Segment = Segments[SegmentIndex]; if (CurrentTime >= Segment.StartTime && CurrentTime <= Segment.EndTime) { CollisionOffset = (Segment.Rule != nullptr) ? (Segment.Rule->CollisionOffset * 0.25f) : 0; break; } } SplineEdgeOffsetAmounts.Add(CollisionOffset); CurrentTime += FillRasterizationTimeRate; } } SimplifyPolygon(SplinePolyVertices2D, SplineEdgeOffsetAmounts); // Always CCW and facing forward regardless of spline winding TArray<FVector2D> CorrectedSplineVertices; PaperGeomTools::CorrectPolygonWinding(CorrectedSplineVertices, SplinePolyVertices2D, false); TArray<FVector2D> TriangulatedPolygonVertices; PaperGeomTools::TriangulatePoly(/*out*/TriangulatedPolygonVertices, CorrectedSplineVertices, false); GenerateCollisionDataFromPolygon(SplinePolyVertices2D, SplineEdgeOffsetAmounts, TriangulatedPolygonVertices); if (TerrainMaterial->InteriorFill != nullptr) { const UPaperSprite* FillSprite = TerrainMaterial->InteriorFill; FPaperTerrainSpriteGeometry& MaterialBatch = *new (GeneratedSpriteGeometry)FPaperTerrainSpriteGeometry(); //@TODO: Look up the existing one instead MaterialBatch.Material = FillSprite->GetDefaultMaterial(); FSpriteDrawCallRecord& FillDrawCall = *new (MaterialBatch.Records) FSpriteDrawCallRecord(); FillDrawCall.BuildFromSprite(FillSprite); FillDrawCall.RenderVerts.Empty(); FillDrawCall.Color = TerrainColor; FillDrawCall.Destination = PaperAxisZ * 0.1f; const FVector2D TextureSize = GetSpriteRenderDataBounds2D(FillSprite->BakedRenderData).GetSize(); const FVector2D SplineSize = SplineBounds.GetSize(); GenerateFillRenderDataFromPolygon(FillSprite, FillDrawCall, TextureSize, TriangulatedPolygonVertices); //@TODO: Add support for the fill sprite being smaller than the entire texture #if NOT_WORKING const float StartingDivisionPointX = FMath::CeilToFloat(SplineBounds.Min.X / TextureSize.X); const float StartingDivisionPointY = FMath::CeilToFloat(SplineBounds.Min.Y / TextureSize.Y); FPoly VerticalRemainder = SplineAsPolygon; for (float Y = StartingDivisionPointY; VerticalRemainder.Vertices.Num() > 0; Y += TextureSize.Y) { FPoly Top; FPoly Bottom; const FVector SplitBaseOuter = (Y * PaperAxisY); VerticalRemainder.SplitWithPlane(SplitBaseOuter, -PaperAxisY, &Top, &Bottom, 1); VerticalRemainder = Bottom; FPoly HorizontalRemainder = Top; for (float X = StartingDivisionPointX; HorizontalRemainder.Vertices.Num() > 0; X += TextureSize.X) { FPoly Left; FPoly Right; const FVector SplitBaseInner = (X * PaperAxisX) + (Y * PaperAxisY); HorizontalRemainder.SplitWithPlane(SplitBaseInner, -PaperAxisX, &Left, &Right, 1); HorizontalRemainder = Right; //BROKEN, function no longer exists (split into 2 parts) SpawnFromPoly(Segments, SplineEdgeOffsetAmounts, FillSprite, FillDrawCall, TextureSize, Left); } } #endif } } // Draw debug frames at the start and end of the spline #if PAPER_TERRAIN_DRAW_DEBUG { const float Time = 5.0f; { FTransform WorldTransform = GetTransformAtDistance(0.0f) * ComponentToWorld; DrawDebugCoordinateSystem(GetWorld(), WorldTransform.GetLocation(), FRotator(WorldTransform.GetRotation()), 30.0f, true, Time, SDPG_Foreground); } { FTransform WorldTransform = GetTransformAtDistance(SplineLength) * ComponentToWorld; DrawDebugCoordinateSystem(GetWorld(), WorldTransform.GetLocation(), FRotator(WorldTransform.GetRotation()), 30.0f, true, Time, SDPG_Foreground); } } #endif } RecreateRenderState_Concurrent(); }
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; }
bool FGeomObject::FinalizeSourceData() { if( !GEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) ) { return 0; } ABrush* brush = GetActualBrush(); bool Ret = false; double StartTime = FPlatformTime::Seconds(); const double TimeLimit = 10.0; // Remove invalid polygons from the brush for( int32 x = 0 ; x < brush->Brush->Polys->Element.Num() ; ++x ) { FPoly* Poly = &brush->Brush->Polys->Element[x]; if( Poly->Vertices.Num() < 3 ) { brush->Brush->Polys->Element.RemoveAt( x ); x = -1; } } for( int32 p = 0 ; p < brush->Brush->Polys->Element.Num() ; ++p ) { FPoly* Poly = &(brush->Brush->Polys->Element[p]); Poly->iLink = p; int32 SaveNumVertices = Poly->Vertices.Num(); const bool bTimeLimitExpired = TimeLimit < FPlatformTime::Seconds() - StartTime; if( !Poly->IsCoplanar() || !Poly->IsConvex() ) { // If the polygon is no longer coplanar and/or convex, break it up into separate triangles. FPoly WkPoly = *Poly; brush->Brush->Polys->Element.RemoveAt( p ); TArray<FPoly> Polygons; if( !bTimeLimitExpired && WkPoly.Triangulate( brush, Polygons ) > 0 ) { FPoly::OptimizeIntoConvexPolys( brush, Polygons ); for( int32 t = 0 ; t < Polygons.Num() ; ++t ) { brush->Brush->Polys->Element.Add( Polygons[t] ); } } p = -1; Ret = true; } else { int32 FixResult = Poly->Fix(); if( FixResult != SaveNumVertices ) { // If the polygon collapses after running "Fix" against it, it needs to be // removed from the brushes polygon list. if( bTimeLimitExpired || FixResult == 0 ) { brush->Brush->Polys->Element.RemoveAt( p ); } p = -1; Ret = true; } else { // If we get here, the polygon is valid and needs to be kept. Finalize its internals. Poly->Finalize(brush,1); } } } if (TimeLimit < FPlatformTime::Seconds() - StartTime) { UE_LOG(LogEditorGeometry, Error, TEXT("FGeomObject::FinalizeSourceData() failed because it took too long")); } brush->ReregisterAllComponents(); return Ret; }
int32 FPoly::Triangulate( ABrush* InOwnerBrush, TArray<FPoly>& OutTriangles ) { #if WITH_EDITOR if( Vertices.Num() < 3 ) { return 0; } FClipSMPolygon Polygon(0); for( int32 v = 0 ; v < Vertices.Num() ; ++v ) { FClipSMVertex vtx; vtx.Pos = Vertices[v]; // Init other data so that VertsAreEqual won't compare garbage vtx.TangentX = FVector::ZeroVector; vtx.TangentY = FVector::ZeroVector; vtx.TangentZ = FVector::ZeroVector; vtx.Color = FColor(0, 0, 0); for( int32 uvIndex=0; uvIndex<ARRAY_COUNT(vtx.UVs); ++uvIndex ) { vtx.UVs[uvIndex] = FVector2D(0.f, 0.f); } Polygon.Vertices.Add( vtx ); } Polygon.FaceNormal = Normal; // Attempt to triangulate this polygon TArray<FClipSMTriangle> Triangles; if( TriangulatePoly( Triangles, Polygon ) ) { // Create a set of FPolys from the triangles OutTriangles.Empty(); FPoly TrianglePoly; for( int32 p = 0 ; p < Triangles.Num() ; ++p ) { FClipSMTriangle* tri = &(Triangles[p]); TrianglePoly.Init(); TrianglePoly.Base = tri->Vertices[0].Pos; TrianglePoly.Vertices.Add( tri->Vertices[0].Pos ); TrianglePoly.Vertices.Add( tri->Vertices[1].Pos ); TrianglePoly.Vertices.Add( tri->Vertices[2].Pos ); if( TrianglePoly.Finalize( InOwnerBrush, 0 ) == 0 ) { OutTriangles.Add( TrianglePoly ); } } } #endif return OutTriangles.Num(); }