Пример #1
0
void UBoxComponent::UpdateBodySetup()
{
	if(ShapeBodySetup == NULL || ShapeBodySetup->IsPendingKill())
	{
		ShapeBodySetup = NewObject<UBodySetup>(this);
		ShapeBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
		ShapeBodySetup->AggGeom.BoxElems.Add(FKBoxElem());
	}

	check(ShapeBodySetup->AggGeom.BoxElems.Num() == 1);
	FKBoxElem* se = ShapeBodySetup->AggGeom.BoxElems.GetData();

	// @todo UE4 do we allow this now?
	// check for malformed values
	if( BoxExtent.X < KINDA_SMALL_NUMBER )
	{
		BoxExtent.X = 1.0f;
	}

	if( BoxExtent.Y < KINDA_SMALL_NUMBER )
	{
		BoxExtent.Y = 1.0f;
	}

	if( BoxExtent.Z < KINDA_SMALL_NUMBER )
	{
		BoxExtent.Z = 1.0f;
	}

	// now set the PhysX data values
	se->SetTransform( FTransform::Identity );
	se->X = BoxExtent.X*2;
	se->Y = BoxExtent.Y*2;
	se->Z = BoxExtent.Z*2;
}
Пример #2
0
void UPaperTerrainComponent::SpawnSegments(const TArray<FTerrainSegment>& TerrainSegments, bool bGenerateSegmentColliders)
{
#ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS
	TArray<FVector2D> CollisionPolygonPoints;
#endif

	for (const FTerrainSegment& Segment : TerrainSegments)
	{
		for (const FTerrainSpriteStamp& Stamp : Segment.Stamps)
		{
			const class UPaperSprite* NewSprite = Stamp.Sprite;
			float Position = Stamp.Time;
			float HorizontalScale = Stamp.Scale;
			float NominalWidth = Stamp.NominalWidth;
			const struct FPaperTerrainMaterialRule* Rule = Segment.Rule;

			if (bGenerateSegmentColliders && (Rule->bEnableCollision) && (CachedBodySetup != nullptr))
			{
				const FTransform LocalTransformAtCenter(GetTransformAtDistance(Position));

#ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS
				// Check note be low Re: bClosedSplines
				FVector2D BoxExtents( 0.5f * NominalWidth * HorizontalScale, 0.5f * FMath::Max<float>(1.0f, FMath::Abs<float>(Rule->CollisionOffset * 0.5f)) );
				
				FVector BoxPoints[4];
				BoxPoints[0] = LocalTransformAtCenter.TransformPosition(FVector(BoxExtents.X, 0, BoxExtents.Y));
				BoxPoints[1] = LocalTransformAtCenter.TransformPosition(FVector(-BoxExtents.X, 0, BoxExtents.Y));
				BoxPoints[2] = LocalTransformAtCenter.TransformPosition(FVector(-BoxExtents.X, 0, -BoxExtents.Y));
				BoxPoints[3] = LocalTransformAtCenter.TransformPosition(FVector(BoxExtents.X, 0, -BoxExtents.Y));

				FVector2D BoxPoints2D[4];
				BoxPoints2D[0].Set(BoxPoints[0].X, BoxPoints[0].Z);
				BoxPoints2D[1].Set(BoxPoints[1].X, BoxPoints[1].Z);
				BoxPoints2D[2].Set(BoxPoints[2].X, BoxPoints[2].Z);
				BoxPoints2D[3].Set(BoxPoints[3].X, BoxPoints[3].Z);

				// If there is a previous polygon, try to merge
				if (CollisionPolygonPoints.Num() >= 4)
				{
					int InsertPoint = CollisionPolygonPoints.Num() / 2 - 1;
					float LengthV0 = FVector2D::Distance(CollisionPolygonPoints[InsertPoint], BoxPoints2D[0]);
					float LengthV1 = FVector2D::Distance(CollisionPolygonPoints[InsertPoint + 1], BoxPoints2D[3]);
					float MergeThreshold = 10;
					bool bMergeIntoPolygon = LengthV0 < MergeThreshold && LengthV1 < MergeThreshold;
					if (bMergeIntoPolygon)
					{
						CollisionPolygonPoints.Insert(BoxPoints2D[2], InsertPoint + 1);
						CollisionPolygonPoints.Insert(BoxPoints2D[1], InsertPoint + 1);
					}
					else
					{
						InsertConvexCollisionDataFromPolygon(CollisionPolygonPoints);
						CollisionPolygonPoints.Empty(0);
						CollisionPolygonPoints.Add(BoxPoints2D[0]);
						CollisionPolygonPoints.Add(BoxPoints2D[1]);
						CollisionPolygonPoints.Add(BoxPoints2D[2]);
						CollisionPolygonPoints.Add(BoxPoints2D[3]);
					}
				}
				else
				{
					CollisionPolygonPoints.Add(BoxPoints2D[0]);
					CollisionPolygonPoints.Add(BoxPoints2D[1]);
					CollisionPolygonPoints.Add(BoxPoints2D[2]);
					CollisionPolygonPoints.Add(BoxPoints2D[3]);
				}
#else
				FKBoxElem Box;
				// The spline is never "closed" properly right now
				//if (bClosedSpline)
				//{
				//	//@TODO: Add proper support for this!
				//	Box.SetTransform(LocalTransformAtCenter);
				//	Box.X = NominalWidth * HorizontalScale;
				//	Box.Y = CollisionThickness;
				//	Box.Z = 30.0f;
				//}
				//else
				{
					Box.SetTransform(LocalTransformAtCenter);
					Box.X = NominalWidth * HorizontalScale;
					Box.Y = CollisionThickness;

					Box.Z = FMath::Max<float>(1.0f, FMath::Abs<float>(Rule->CollisionOffset * 0.5f));
				}
				CachedBodySetup->AggGeom.BoxElems.Add(Box);
#endif
			}

			if (NewSprite)
			{
				FPaperTerrainSpriteGeometry& MaterialBatch = *new (GeneratedSpriteGeometry) FPaperTerrainSpriteGeometry(); //@TODO: Look up the existing one instead
				MaterialBatch.Material = NewSprite->GetDefaultMaterial();
				MaterialBatch.DrawOrder = Rule->DrawOrder;

				FSpriteDrawCallRecord& Record = *new (MaterialBatch.Records) FSpriteDrawCallRecord();
				Record.BuildFromSprite(NewSprite);
				Record.Color = TerrainColor;

				// Work out if the sprite is likely to be bent > X deg (folded over itself)
				const FVector ForwardVector(1, 0, 0);
				const FTransform LocalTransformAtBack(GetTransformAtDistance(Position - 0.5f * NominalWidth * HorizontalScale));
				FVector StartForwardVector = LocalTransformAtBack.TransformVector(ForwardVector).GetSafeNormal();
				const FTransform LocalTransformAtFront(GetTransformAtDistance(Position + 0.5f * NominalWidth * HorizontalScale));
				FVector EndForwardVector = LocalTransformAtFront.TransformVector(ForwardVector).GetSafeNormal();
				bool bIsSpriteBent = FVector::DotProduct(StartForwardVector, EndForwardVector) < 0.0f;// 0.7071f; // (45deg looks worse)

				for (FVector4& XYUV : Record.RenderVerts)
				{
					FTransform LocalTransformAtX(GetTransformAtDistance(Position + (XYUV.X * HorizontalScale)));

					// When the quad is overly bent, inherit rotation from the start of the quad to unfold it
					if (bIsSpriteBent)
					{
						LocalTransformAtX.SetRotation(LocalTransformAtFront.GetRotation());
					}

					const FVector SourceVector = (XYUV.Y * PaperAxisY);
					const FVector NewVector = LocalTransformAtX.TransformPosition(SourceVector);

					const float NewX = FVector::DotProduct(NewVector, PaperAxisX);
					const float NewY = FVector::DotProduct(NewVector, PaperAxisY);

					XYUV.X = NewX;
					XYUV.Y = NewY;
				}
			}
		}
	}

	//@TODO: Sort by draw order first, materials next - Merge batches with the same material
	GeneratedSpriteGeometry.Sort([](const FPaperTerrainSpriteGeometry& A, const FPaperTerrainSpriteGeometry& B) { return B.DrawOrder > A.DrawOrder; });

#ifdef USE_SIMPLIFIED_POLYGON_COLLIDERS_FOR_SEGMENTS
	// For whatever is remaining
	if (CollisionPolygonPoints.Num() > 0)
	{
		InsertConvexCollisionDataFromPolygon(CollisionPolygonPoints);
	}
#endif
}
Пример #3
0
/**
 *	Function for adding a box collision primitive to the supplied collision geometry based on the mesh of the box.
 *
 *	We keep a list of triangle normals found so far. For each normal direction,
 *	we should have 2 distances from the origin (2 parallel box faces). If the
 *	mesh is a box, we should have 3 distinct normal directions, and 2 distances
 *	found for each. The difference between these distances should be the box
 *	dimensions. The 3 directions give us the key axes, and therefore the
 *	box transformation matrix. This shouldn't rely on any vertex-ordering on
 *	the triangles (normals are compared +ve & -ve). It also shouldn't matter
 *	about how many triangles make up each side (but it will take longer).
 *	We get the centre of the box from the centre of its AABB.
 */
void AddBoxGeomFromTris( const TArray<FPoly>& Tris, FKAggregateGeom* AggGeom, const TCHAR* ObjName )
{
    TArray<FPlaneInfo> Planes;

    for(int32 i=0; i<Tris.Num(); i++)
    {
        bool bFoundPlane = false;
        for(int32 j=0; j<Planes.Num() && !bFoundPlane; j++)
        {
            // if this triangle plane is already known...
            if( AreParallel( Tris[i].Normal, Planes[j].Normal ) )
            {
                // Always use the same normal when comparing distances, to ensure consistent sign.
                float Dist = Tris[i].Vertices[0] | Planes[j].Normal;

                // we only have one distance, and its not that one, add it.
                if( Planes[j].DistCount == 1 && !AreEqual(Dist, Planes[j].PlaneDist[0]) )
                {
                    Planes[j].PlaneDist[1] = Dist;
                    Planes[j].DistCount = 2;
                }
                // if we have a second distance, and its not that either, something is wrong.
                else if( Planes[j].DistCount == 2 && !AreEqual(Dist, Planes[j].PlaneDist[1]) )
                {
                    UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Found more than 2 planes with different distances."), ObjName);
                    return;
                }

                bFoundPlane = true;
            }
        }

        // If this triangle does not match an existing plane, add to list.
        if(!bFoundPlane)
        {
            check( Planes.Num() < Tris.Num() );

            FPlaneInfo NewPlane;
            NewPlane.Normal = Tris[i].Normal;
            NewPlane.DistCount = 1;
            NewPlane.PlaneDist[0] = Tris[i].Vertices[0] | NewPlane.Normal;

            Planes.Add(NewPlane);
        }
    }

    // Now we have our candidate planes, see if there are any problems

    // Wrong number of planes.
    if(Planes.Num() != 3)
    {
        UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Not very box-like (need 3 sets of planes)."), ObjName);
        return;
    }

    // If we don't have 3 pairs, we can't carry on.
    if((Planes[0].DistCount != 2) || (Planes[1].DistCount != 2) || (Planes[2].DistCount != 2))
    {
        UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Incomplete set of planes (need 2 per axis)."), ObjName);
        return;
    }

    FMatrix BoxTM = FMatrix::Identity;

    BoxTM.SetAxis(0, Planes[0].Normal);
    BoxTM.SetAxis(1, Planes[1].Normal);

    // ensure valid TM by cross-product
    FVector ZAxis = Planes[0].Normal ^ Planes[1].Normal;

    if( !AreParallel(ZAxis, Planes[2].Normal) )
    {
        UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Box axes are not perpendicular."), ObjName);
        return;
    }

    BoxTM.SetAxis(2, ZAxis);

    // OBB centre == AABB centre.
    FBox Box(0);
    for(int32 i=0; i<Tris.Num(); i++)
    {
        Box += Tris[i].Vertices[0];
        Box += Tris[i].Vertices[1];
        Box += Tris[i].Vertices[2];
    }

    BoxTM.SetOrigin( Box.GetCenter() );

    // Allocate box in array
    FKBoxElem BoxElem;
    BoxElem.SetTransform( FTransform( BoxTM ) );
    // distance between parallel planes is box edge lengths.
    BoxElem.X = FMath::Abs(Planes[0].PlaneDist[0] - Planes[0].PlaneDist[1]);
    BoxElem.Y = FMath::Abs(Planes[1].PlaneDist[0] - Planes[1].PlaneDist[1]);
    BoxElem.Z = FMath::Abs(Planes[2].PlaneDist[0] - Planes[2].PlaneDist[1]);
    AggGeom->BoxElems.Add(BoxElem);
}