Example #1
0
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 );
    }
}
Example #2
0
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 );
	}

}
Example #3
0
//
// 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();
}
Example #5
0
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;
}
Example #6
0
/**
 * 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
}
Example #7
0
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;
}
Example #8
0
//
// 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;
}
Example #9
0
/**
 * 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();
		}
	}
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
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;
}
Example #14
0
//
// 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();
}
Example #15
0
//
// 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;
}
Example #16
0
//
// 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();
}
Example #17
0
File: cm.cpp Project: asgene/sm2
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();
        }
    }
}
Example #18
0
File: cm.cpp Project: asgene/sm2
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;
}
Example #22
0
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();
}