2017-12-09 16:33:11 +00:00
// Copyright 2017 Mike Fricker. All Rights Reserved.
# include "StreetMapRuntime.h"
# include "StreetMapComponent.h"
# include "StreetMapSceneProxy.h"
# include "Runtime/Engine/Classes/Engine/StaticMesh.h"
# include "Runtime/Engine/Public/StaticMeshResources.h"
# include "PolygonTools.h"
# include "PhysicsEngine/BodySetup.h"
# if WITH_EDITOR
# include "ModuleManager.h"
# include "PropertyEditorModule.h"
# endif //WITH_EDITOR
UStreetMapComponent : : UStreetMapComponent ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer ) ,
StreetMap ( nullptr ) ,
CachedLocalBounds ( ForceInit )
{
// We make sure our mesh collision profile name is set to NoCollisionProfileName at initialization.
// Because we don't have collision data yet!
SetCollisionProfileName ( UCollisionProfile : : NoCollision_ProfileName ) ;
// We don't currently need to be ticked. This can be overridden in a derived class though.
PrimaryComponentTick . bCanEverTick = false ;
this - > bAutoActivate = false ; // NOTE: Components instantiated through C++ are not automatically active, so they'll only tick once and then go to sleep!
// We don't currently need InitializeComponent() to be called on us. This can be overridden in a
// derived class though.
bWantsInitializeComponent = false ;
// Turn on shadows. It looks better.
CastShadow = true ;
// Our mesh is too complicated to be a useful occluder.
bUseAsOccluder = false ;
// Our mesh can influence navigation.
bCanEverAffectNavigation = true ;
static ConstructorHelpers : : FObjectFinder < UMaterialInterface > DefaultMaterialAsset ( TEXT ( " /StreetMap/StreetMapDefaultMaterial " ) ) ;
StreetMapDefaultMaterial = DefaultMaterialAsset . Object ;
}
FPrimitiveSceneProxy * UStreetMapComponent : : CreateSceneProxy ( )
{
FStreetMapSceneProxy * StreetMapSceneProxy = nullptr ;
if ( HasValidMesh ( ) )
{
StreetMapSceneProxy = new FStreetMapSceneProxy ( this ) ;
StreetMapSceneProxy - > Init ( this , Vertices , Indices ) ;
}
return StreetMapSceneProxy ;
}
int32 UStreetMapComponent : : GetNumMaterials ( ) const
{
// NOTE: This is a bit of a weird thing about Unreal that we need to deal with when defining a component that
// can have materials assigned. UPrimitiveComponent::GetNumMaterials() will return 0, so we need to override it
// to return the number of overridden materials, which are the actual materials assigned to the component.
return HasValidMesh ( ) ? GetNumMeshSections ( ) : GetNumOverrideMaterials ( ) ;
}
void UStreetMapComponent : : SetStreetMap ( class UStreetMap * NewStreetMap , bool bClearPreviousMeshIfAny /*= false*/ , bool bRebuildMesh /*= false */ )
{
if ( StreetMap ! = NewStreetMap )
{
StreetMap = NewStreetMap ;
if ( bClearPreviousMeshIfAny )
InvalidateMesh ( ) ;
if ( bRebuildMesh )
BuildMesh ( ) ;
}
}
bool UStreetMapComponent : : GetPhysicsTriMeshData ( struct FTriMeshCollisionData * CollisionData , bool InUseAllTriData )
{
if ( ! CollisionSettings . bGenerateCollision | | ! HasValidMesh ( ) )
{
return false ;
}
// Copy vertices data
const int32 NumVertices = Vertices . Num ( ) ;
CollisionData - > Vertices . Empty ( ) ;
CollisionData - > Vertices . AddUninitialized ( NumVertices ) ;
for ( int32 VertexIndex = 0 ; VertexIndex < NumVertices ; VertexIndex + + )
{
CollisionData - > Vertices [ VertexIndex ] = Vertices [ VertexIndex ] . Position ;
}
// Copy indices data
const int32 NumTriangles = Indices . Num ( ) / 3 ;
FTriIndices TempTriangle ;
for ( int32 TriangleIndex = 0 ; TriangleIndex < NumTriangles * 3 ; TriangleIndex + = 3 )
{
TempTriangle . v0 = Indices [ TriangleIndex + 0 ] ;
TempTriangle . v1 = Indices [ TriangleIndex + 1 ] ;
TempTriangle . v2 = Indices [ TriangleIndex + 2 ] ;
CollisionData - > Indices . Add ( TempTriangle ) ;
CollisionData - > MaterialIndices . Add ( 0 ) ;
}
CollisionData - > bFlipNormals = true ;
CollisionData - > bDeformableMesh = true ;
return HasValidMesh ( ) ;
}
bool UStreetMapComponent : : ContainsPhysicsTriMeshData ( bool InUseAllTriData ) const
{
return HasValidMesh ( ) & & CollisionSettings . bGenerateCollision ;
}
bool UStreetMapComponent : : WantsNegXTriMesh ( )
{
return false ;
}
void UStreetMapComponent : : CreateBodySetupIfNeeded ( bool bForceCreation /*= false*/ )
{
if ( StreetMapBodySetup = = nullptr | | bForceCreation = = true )
{
// Creating new BodySetup Object.
StreetMapBodySetup = NewObject < UBodySetup > ( this ) ;
StreetMapBodySetup - > BodySetupGuid = FGuid : : NewGuid ( ) ;
StreetMapBodySetup - > bDoubleSidedGeometry = CollisionSettings . bAllowDoubleSidedGeometry ;
// shapes per poly shape for collision (Not working in simulation mode).
StreetMapBodySetup - > CollisionTraceFlag = CTF_UseComplexAsSimple ;
}
}
void UStreetMapComponent : : GenerateCollision ( )
{
if ( ! CollisionSettings . bGenerateCollision | | ! HasValidMesh ( ) )
{
return ;
}
// create a new body setup
CreateBodySetupIfNeeded ( true ) ;
if ( GetCollisionProfileName ( ) = = UCollisionProfile : : NoCollision_ProfileName )
{
SetCollisionProfileName ( UCollisionProfile : : BlockAll_ProfileName ) ;
}
// Rebuild the body setup
# if WITH_EDITOR || WITH_RUNTIME_PHYSICS_COOKING
StreetMapBodySetup - > InvalidatePhysicsData ( ) ;
# endif
StreetMapBodySetup - > CreatePhysicsMeshes ( ) ;
UpdateNavigationIfNeeded ( ) ;
}
void UStreetMapComponent : : ClearCollision ( )
{
if ( StreetMapBodySetup ! = nullptr )
{
StreetMapBodySetup - > InvalidatePhysicsData ( ) ;
StreetMapBodySetup = nullptr ;
}
if ( GetCollisionProfileName ( ) ! = UCollisionProfile : : NoCollision_ProfileName )
{
SetCollisionProfileName ( UCollisionProfile : : NoCollision_ProfileName ) ;
}
UpdateNavigationIfNeeded ( ) ;
}
class UBodySetup * UStreetMapComponent : : GetBodySetup ( )
{
if ( CollisionSettings . bGenerateCollision = = true )
{
// checking if we have a valid body setup.
// A new one is created only if a valid body setup is not found.
CreateBodySetupIfNeeded ( ) ;
return StreetMapBodySetup ;
}
if ( StreetMapBodySetup ! = nullptr ) StreetMapBodySetup = nullptr ;
return nullptr ;
}
void UStreetMapComponent : : GenerateMesh ( )
{
/////////////////////////////////////////////////////////
// Visual tweakables for generated Street Map mesh
//
const float RoadZ = MeshBuildSettings . RoadOffesetZ ;
const bool bWant3DBuildings = MeshBuildSettings . bWant3DBuildings ;
const float BuildingLevelFloorFactor = MeshBuildSettings . BuildingLevelFloorFactor ;
const bool bWantLitBuildings = MeshBuildSettings . bWantLitBuildings ;
const bool bWantBuildingBorderOnGround = ! bWant3DBuildings ;
const float StreetThickness = MeshBuildSettings . StreetThickness ;
const FColor StreetColor = MeshBuildSettings . StreetColor . ToFColor ( false ) ;
const float MajorRoadThickness = MeshBuildSettings . MajorRoadThickness ;
const FColor MajorRoadColor = MeshBuildSettings . MajorRoadColor . ToFColor ( false ) ;
const float HighwayThickness = MeshBuildSettings . HighwayThickness ;
const FColor HighwayColor = MeshBuildSettings . HighwayColor . ToFColor ( false ) ;
const float BuildingBorderThickness = MeshBuildSettings . BuildingBorderThickness ;
FLinearColor BuildingBorderLinearColor = MeshBuildSettings . BuildingBorderLinearColor ;
const float BuildingBorderZ = MeshBuildSettings . BuildingBorderZ ;
const FColor BuildingBorderColor ( BuildingBorderLinearColor . ToFColor ( false ) ) ;
const FColor BuildingFillColor ( FLinearColor ( BuildingBorderLinearColor * 0.33f ) . CopyWithNewOpacity ( 1.0f ) . ToFColor ( false ) ) ;
2017-12-11 19:33:54 +00:00
const FColor WaterColor = MeshBuildSettings . WaterColor . ToFColor ( false ) ;
const FColor ParkColor = MeshBuildSettings . ParkColor . ToFColor ( false ) ;
2017-12-09 16:33:11 +00:00
/////////////////////////////////////////////////////////
CachedLocalBounds = FBox ( ForceInit ) ;
Vertices . Reset ( ) ;
Indices . Reset ( ) ;
if ( StreetMap ! = nullptr )
{
FBox MeshBoundingBox ;
MeshBoundingBox . Init ( ) ;
const auto & Roads = StreetMap - > GetRoads ( ) ;
const auto & Nodes = StreetMap - > GetNodes ( ) ;
const auto & Buildings = StreetMap - > GetBuildings ( ) ;
2017-12-11 19:33:54 +00:00
const auto & Waters = StreetMap - > GetWaters ( ) ;
const auto & Parks = StreetMap - > GetParks ( ) ;
2017-12-09 16:33:11 +00:00
for ( const auto & Road : Roads )
{
float RoadThickness = StreetThickness ;
FColor RoadColor = StreetColor ;
switch ( Road . RoadType )
{
case EStreetMapRoadType : : Highway :
RoadThickness = HighwayThickness ;
RoadColor = HighwayColor ;
break ;
case EStreetMapRoadType : : MajorRoad :
RoadThickness = MajorRoadThickness ;
RoadColor = MajorRoadColor ;
break ;
case EStreetMapRoadType : : Street :
case EStreetMapRoadType : : Other :
break ;
default :
check ( 0 ) ;
break ;
}
for ( int32 PointIndex = 0 ; PointIndex < Road . RoadPoints . Num ( ) - 1 ; + + PointIndex )
{
AddThick2DLine (
Road . RoadPoints [ PointIndex ] ,
Road . RoadPoints [ PointIndex + 1 ] ,
RoadZ ,
RoadThickness ,
RoadColor ,
RoadColor ,
MeshBoundingBox ) ;
}
}
TArray < int32 > TempIndices ;
TArray < int32 > TriangulatedVertexIndices ;
TArray < FVector > TempPoints ;
for ( int32 BuildingIndex = 0 ; BuildingIndex < Buildings . Num ( ) ; + + BuildingIndex )
{
const auto & Building = Buildings [ BuildingIndex ] ;
// Building mesh (or filled area, if the building has no height)
// Triangulate this building
// @todo: Performance: Triangulating lots of building polygons is quite slow. We could easily do this
// as part of the import process and store tessellated geometry instead of doing this at load time.
bool WindsClockwise ;
if ( FPolygonTools : : TriangulatePolygon ( Building . BuildingPoints , TempIndices , /* Out */ TriangulatedVertexIndices , /* Out */ WindsClockwise ) )
{
// @todo: Performance: We could preprocess the building shapes so that the points always wind
// in a consistent direction, so we can skip determining the winding above.
const int32 FirstTopVertexIndex = this - > Vertices . Num ( ) ;
// calculate fill Z for buildings
// either use the defined height or extrapolate from building level count
float BuildingFillZ = 0.0f ;
if ( bWant3DBuildings ) {
if ( Building . Height > 0 ) {
BuildingFillZ = Building . Height ;
}
else if ( Building . BuildingLevels > 0 ) {
BuildingFillZ = ( float ) Building . BuildingLevels * BuildingLevelFloorFactor ;
}
}
// Top of building
{
TempPoints . SetNum ( Building . BuildingPoints . Num ( ) , false ) ;
for ( int32 PointIndex = 0 ; PointIndex < Building . BuildingPoints . Num ( ) ; + + PointIndex )
{
TempPoints [ PointIndex ] = FVector ( Building . BuildingPoints [ ( Building . BuildingPoints . Num ( ) - PointIndex ) - 1 ] , BuildingFillZ ) ;
}
AddTriangles ( TempPoints , TriangulatedVertexIndices , FVector : : ForwardVector , FVector : : UpVector , BuildingFillColor , MeshBoundingBox ) ;
}
if ( bWant3DBuildings & & ( Building . Height > KINDA_SMALL_NUMBER | | Building . BuildingLevels > 0 ) )
{
// NOTE: Lit buildings can't share vertices beyond quads (all quads have their own face normals), so this uses a lot more geometry!
if ( bWantLitBuildings )
{
// Create edges for the walls of the 3D buildings
for ( int32 LeftPointIndex = 0 ; LeftPointIndex < Building . BuildingPoints . Num ( ) ; + + LeftPointIndex )
{
const int32 RightPointIndex = ( LeftPointIndex + 1 ) % Building . BuildingPoints . Num ( ) ;
TempPoints . SetNum ( 4 , false ) ;
const int32 TopLeftVertexIndex = 0 ;
TempPoints [ TopLeftVertexIndex ] = FVector ( Building . BuildingPoints [ WindsClockwise ? RightPointIndex : LeftPointIndex ] , BuildingFillZ ) ;
const int32 TopRightVertexIndex = 1 ;
TempPoints [ TopRightVertexIndex ] = FVector ( Building . BuildingPoints [ WindsClockwise ? LeftPointIndex : RightPointIndex ] , BuildingFillZ ) ;
const int32 BottomRightVertexIndex = 2 ;
TempPoints [ BottomRightVertexIndex ] = FVector ( Building . BuildingPoints [ WindsClockwise ? LeftPointIndex : RightPointIndex ] , 0.0f ) ;
const int32 BottomLeftVertexIndex = 3 ;
TempPoints [ BottomLeftVertexIndex ] = FVector ( Building . BuildingPoints [ WindsClockwise ? RightPointIndex : LeftPointIndex ] , 0.0f ) ;
TempIndices . SetNum ( 6 , false ) ;
TempIndices [ 0 ] = BottomLeftVertexIndex ;
TempIndices [ 1 ] = TopLeftVertexIndex ;
TempIndices [ 2 ] = BottomRightVertexIndex ;
TempIndices [ 3 ] = BottomRightVertexIndex ;
TempIndices [ 4 ] = TopLeftVertexIndex ;
TempIndices [ 5 ] = TopRightVertexIndex ;
const FVector FaceNormal = FVector : : CrossProduct ( ( TempPoints [ 0 ] - TempPoints [ 2 ] ) . GetSafeNormal ( ) , ( TempPoints [ 0 ] - TempPoints [ 1 ] ) . GetSafeNormal ( ) ) ;
const FVector ForwardVector = FVector : : UpVector ;
const FVector UpVector = FaceNormal ;
AddTriangles ( TempPoints , TempIndices , ForwardVector , UpVector , BuildingFillColor , MeshBoundingBox ) ;
}
}
else
{
// Create vertices for the bottom
const int32 FirstBottomVertexIndex = this - > Vertices . Num ( ) ;
for ( int32 PointIndex = 0 ; PointIndex < Building . BuildingPoints . Num ( ) ; + + PointIndex )
{
const FVector2D Point = Building . BuildingPoints [ PointIndex ] ;
FStreetMapVertex & NewVertex = * new ( this - > Vertices ) FStreetMapVertex ( ) ;
NewVertex . Position = FVector ( Point , 0.0f ) ;
NewVertex . TextureCoordinate = FVector2D ( 0.0f , 0.0f ) ; // NOTE: We're not using texture coordinates for anything yet
NewVertex . TangentX = FVector : : ForwardVector ; // NOTE: Tangents aren't important for these unlit buildings
NewVertex . TangentZ = FVector : : UpVector ;
NewVertex . Color = BuildingFillColor ;
MeshBoundingBox + = NewVertex . Position ;
}
// Create edges for the walls of the 3D buildings
for ( int32 LeftPointIndex = 0 ; LeftPointIndex < Building . BuildingPoints . Num ( ) ; + + LeftPointIndex )
{
const int32 RightPointIndex = ( LeftPointIndex + 1 ) % Building . BuildingPoints . Num ( ) ;
const int32 BottomLeftVertexIndex = FirstBottomVertexIndex + LeftPointIndex ;
const int32 BottomRightVertexIndex = FirstBottomVertexIndex + RightPointIndex ;
const int32 TopRightVertexIndex = FirstTopVertexIndex + RightPointIndex ;
const int32 TopLeftVertexIndex = FirstTopVertexIndex + LeftPointIndex ;
this - > Indices . Add ( BottomLeftVertexIndex ) ;
this - > Indices . Add ( TopLeftVertexIndex ) ;
this - > Indices . Add ( BottomRightVertexIndex ) ;
this - > Indices . Add ( BottomRightVertexIndex ) ;
this - > Indices . Add ( TopLeftVertexIndex ) ;
this - > Indices . Add ( TopRightVertexIndex ) ;
}
}
}
}
else
{
// @todo: Triangulation failed for some reason, possibly due to degenerate polygons. We can
// probably improve the algorithm to avoid this happening.
}
// Building border
if ( bWantBuildingBorderOnGround )
{
for ( int32 PointIndex = 0 ; PointIndex < Building . BuildingPoints . Num ( ) ; + + PointIndex )
{
AddThick2DLine (
Building . BuildingPoints [ PointIndex ] ,
Building . BuildingPoints [ ( PointIndex + 1 ) % Building . BuildingPoints . Num ( ) ] ,
BuildingBorderZ ,
BuildingBorderThickness , // Thickness
BuildingBorderColor ,
BuildingBorderColor ,
MeshBoundingBox ) ;
}
}
}
2017-12-11 19:33:54 +00:00
// Vinfamy added
TArray < int32 > TempIndicesW ;
TArray < int32 > TriangulatedVertexIndicesW ;
TArray < FVector > TempPointsW ;
for ( int32 WaterIndex = 0 ; WaterIndex < Waters . Num ( ) ; + + WaterIndex )
{
const auto & Water = Waters [ WaterIndex ] ;
// Water mesh (or filled area, if the Water has no height)
// Triangulate this Water
// @todo: Performance: Triangulating lots of Water polygons is quite slow. We could easily do this
// as part of the import process and store tessellated geometry instead of doing this at load time.
bool WindsClockwise ;
if ( FPolygonTools : : TriangulatePolygon ( Water . WaterPoints , TempIndicesW , /* Out */ TriangulatedVertexIndicesW , /* Out */ WindsClockwise ) )
{
// @todo: Performance: We could preprocess the Water shapes so that the points always wind
// in a consistent direction, so we can skip determining the winding above.
const int32 FirstTopVertexIndex = this - > Vertices . Num ( ) ;
// calculate fill Z for Waters
// either use the defined height or extrapolate from Water level count
float WaterFillZ = 0.0f ;
// Top of Water
{
TempPointsW . SetNum ( Water . WaterPoints . Num ( ) , false ) ;
for ( int32 PointIndex = 0 ; PointIndex < Water . WaterPoints . Num ( ) ; + + PointIndex )
{
TempPointsW [ PointIndex ] = FVector ( Water . WaterPoints [ ( Water . WaterPoints . Num ( ) - PointIndex ) - 1 ] , WaterFillZ ) ;
}
AddTriangles ( TempPointsW , TriangulatedVertexIndicesW , FVector : : ForwardVector , FVector : : UpVector , WaterColor , MeshBoundingBox ) ;
}
}
else
{
// @todo: Triangulation failed for some reason, possibly due to degenerate polygons. We can
// probably improve the algorithm to avoid this happening.
}
// Water border
}
TArray < int32 > TempIndicesP ;
TArray < int32 > TriangulatedVertexIndicesP ;
TArray < FVector > TempPointsP ;
for ( int32 ParkIndex = 0 ; ParkIndex < Parks . Num ( ) ; + + ParkIndex )
{
const auto & Park = Parks [ ParkIndex ] ;
// Park mesh (or filled area, if the Park has no height)
// Triangulate this Park
// @todo: Performance: Triangulating lots of Park polygons is quite slow. We could easily do this
// as part of the import process and store tessellated geometry instead of doing this at load time.
bool WindsClockwise ;
if ( FPolygonTools : : TriangulatePolygon ( Park . ParkPoints , TempIndicesP , /* Out */ TriangulatedVertexIndicesP , /* Out */ WindsClockwise ) )
{
// @todo: Performance: We could preprocess the Park shapes so that the points always wind
// in a consistent direction, so we can skip determining the winding above.
const int32 FirstTopVertexIndex = this - > Vertices . Num ( ) ;
// calculate fill Z for Parks
// either use the defined height or extrapolate from Park level count
float ParkFillZ = 0.0f ;
// Top of Park
{
TempPointsP . SetNum ( Park . ParkPoints . Num ( ) , false ) ;
for ( int32 PointIndex = 0 ; PointIndex < Park . ParkPoints . Num ( ) ; + + PointIndex )
{
TempPointsP [ PointIndex ] = FVector ( Park . ParkPoints [ ( Park . ParkPoints . Num ( ) - PointIndex ) - 1 ] , ParkFillZ ) ;
}
AddTriangles ( TempPointsP , TriangulatedVertexIndicesP , FVector : : ForwardVector , FVector : : UpVector , ParkColor , MeshBoundingBox ) ;
}
}
else
{
// @todo: Triangulation failed for some reason, possibly due to degenerate polygons. We can
// probably improve the algorithm to avoid this happening.
}
// Park border
}
2017-12-09 16:33:11 +00:00
CachedLocalBounds = MeshBoundingBox ;
}
}
# if WITH_EDITOR
void UStreetMapComponent : : PostEditChangeProperty ( FPropertyChangedEvent & PropertyChangedEvent )
{
bool bNeedRefreshCustomizationModule = false ;
// Check to see if the "StreetMap" property changed.
if ( PropertyChangedEvent . Property ! = nullptr )
{
const FName PropertyName ( PropertyChangedEvent . Property - > GetFName ( ) ) ;
if ( PropertyName = = GET_MEMBER_NAME_CHECKED ( UStreetMapComponent , StreetMap ) )
{
bNeedRefreshCustomizationModule = true ;
}
else if ( IsCollisionProperty ( PropertyName ) ) // For some unknown reason , GET_MEMBER_NAME_CHECKED(UStreetMapComponent, CollisionSettings) is not working ??? "TO CHECK LATER"
{
if ( CollisionSettings . bGenerateCollision = = true )
{
GenerateCollision ( ) ;
}
else
{
ClearCollision ( ) ;
}
bNeedRefreshCustomizationModule = true ;
}
}
if ( bNeedRefreshCustomizationModule )
{
FPropertyEditorModule & PropertyModule = FModuleManager : : GetModuleChecked < FPropertyEditorModule > ( " PropertyEditor " ) ;
PropertyModule . NotifyCustomizationModuleChanged ( ) ;
}
// Call the parent implementation of this function
Super : : PostEditChangeProperty ( PropertyChangedEvent ) ;
}
# endif // WITH_EDITOR
void UStreetMapComponent : : BuildMesh ( )
{
// Wipes out our cached mesh data. Maybe unnecessary in case GenerateMesh is clearing cached mesh data and creating a new SceneProxy !
InvalidateMesh ( ) ;
GenerateMesh ( ) ;
if ( HasValidMesh ( ) )
{
// We have a new bounding box
UpdateBounds ( ) ;
}
else
{
// No mesh was generated
}
GenerateCollision ( ) ;
// Mark our render state dirty so that CreateSceneProxy can refresh it on demand
MarkRenderStateDirty ( ) ;
AssignDefaultMaterialIfNeeded ( ) ;
Modify ( ) ;
}
void UStreetMapComponent : : AssignDefaultMaterialIfNeeded ( )
{
if ( this - > GetNumMaterials ( ) = = 0 | | this - > GetMaterial ( 0 ) = = nullptr )
{
if ( ! HasValidMesh ( ) | | GetDefaultMaterial ( ) = = nullptr )
return ;
this - > SetMaterial ( 0 , GetDefaultMaterial ( ) ) ;
}
}
void UStreetMapComponent : : UpdateNavigationIfNeeded ( )
{
if ( bCanEverAffectNavigation | | bNavigationRelevant )
{
UNavigationSystem : : UpdateComponentInNavOctree ( * this ) ;
}
}
void UStreetMapComponent : : InvalidateMesh ( )
{
Vertices . Reset ( ) ;
Indices . Reset ( ) ;
CachedLocalBounds = FBoxSphereBounds ( FBox ( ForceInit ) ) ;
ClearCollision ( ) ;
// Mark our render state dirty so that CreateSceneProxy can refresh it on demand
MarkRenderStateDirty ( ) ;
Modify ( ) ;
}
FBoxSphereBounds UStreetMapComponent : : CalcBounds ( const FTransform & LocalToWorld ) const
{
if ( HasValidMesh ( ) )
{
FBoxSphereBounds WorldSpaceBounds = CachedLocalBounds . TransformBy ( LocalToWorld ) ;
WorldSpaceBounds . BoxExtent * = BoundsScale ;
WorldSpaceBounds . SphereRadius * = BoundsScale ;
return WorldSpaceBounds ;
}
else
{
return FBoxSphereBounds ( LocalToWorld . GetLocation ( ) , FVector : : ZeroVector , 0.0f ) ;
}
}
void UStreetMapComponent : : AddThick2DLine ( const FVector2D Start , const FVector2D End , const float Z , const float Thickness , const FColor & StartColor , const FColor & EndColor , FBox & MeshBoundingBox )
{
const float HalfThickness = Thickness * 0.5f ;
const FVector2D LineDirection = ( End - Start ) . GetSafeNormal ( ) ;
const FVector2D RightVector ( - LineDirection . Y , LineDirection . X ) ;
const int32 BottomLeftVertexIndex = Vertices . Num ( ) ;
FStreetMapVertex & BottomLeftVertex = * new ( Vertices ) FStreetMapVertex ( ) ;
BottomLeftVertex . Position = FVector ( Start - RightVector * HalfThickness , Z ) ;
BottomLeftVertex . TextureCoordinate = FVector2D ( 0.0f , 0.0f ) ;
BottomLeftVertex . TangentX = FVector ( LineDirection , 0.0f ) ;
BottomLeftVertex . TangentZ = FVector : : UpVector ;
BottomLeftVertex . Color = StartColor ;
MeshBoundingBox + = BottomLeftVertex . Position ;
const int32 BottomRightVertexIndex = Vertices . Num ( ) ;
FStreetMapVertex & BottomRightVertex = * new ( Vertices ) FStreetMapVertex ( ) ;
BottomRightVertex . Position = FVector ( Start + RightVector * HalfThickness , Z ) ;
BottomRightVertex . TextureCoordinate = FVector2D ( 1.0f , 0.0f ) ;
BottomRightVertex . TangentX = FVector ( LineDirection , 0.0f ) ;
BottomRightVertex . TangentZ = FVector : : UpVector ;
BottomRightVertex . Color = StartColor ;
MeshBoundingBox + = BottomRightVertex . Position ;
const int32 TopRightVertexIndex = Vertices . Num ( ) ;
FStreetMapVertex & TopRightVertex = * new ( Vertices ) FStreetMapVertex ( ) ;
TopRightVertex . Position = FVector ( End + RightVector * HalfThickness , Z ) ;
TopRightVertex . TextureCoordinate = FVector2D ( 1.0f , 1.0f ) ;
TopRightVertex . TangentX = FVector ( LineDirection , 0.0f ) ;
TopRightVertex . TangentZ = FVector : : UpVector ;
TopRightVertex . Color = EndColor ;
MeshBoundingBox + = TopRightVertex . Position ;
const int32 TopLeftVertexIndex = Vertices . Num ( ) ;
FStreetMapVertex & TopLeftVertex = * new ( Vertices ) FStreetMapVertex ( ) ;
TopLeftVertex . Position = FVector ( End - RightVector * HalfThickness , Z ) ;
TopLeftVertex . TextureCoordinate = FVector2D ( 0.0f , 1.0f ) ;
TopLeftVertex . TangentX = FVector ( LineDirection , 0.0f ) ;
TopLeftVertex . TangentZ = FVector : : UpVector ;
TopLeftVertex . Color = EndColor ;
MeshBoundingBox + = TopLeftVertex . Position ;
Indices . Add ( BottomLeftVertexIndex ) ;
Indices . Add ( BottomRightVertexIndex ) ;
Indices . Add ( TopRightVertexIndex ) ;
Indices . Add ( BottomLeftVertexIndex ) ;
Indices . Add ( TopRightVertexIndex ) ;
Indices . Add ( TopLeftVertexIndex ) ;
} ;
void UStreetMapComponent : : AddTriangles ( const TArray < FVector > & Points , const TArray < int32 > & PointIndices , const FVector & ForwardVector , const FVector & UpVector , const FColor & Color , FBox & MeshBoundingBox )
{
const int32 FirstVertexIndex = Vertices . Num ( ) ;
for ( FVector Point : Points )
{
FStreetMapVertex & NewVertex = * new ( Vertices ) FStreetMapVertex ( ) ;
NewVertex . Position = Point ;
NewVertex . TextureCoordinate = FVector2D ( 0.0f , 0.0f ) ; // NOTE: We're not using texture coordinates for anything yet
NewVertex . TangentX = ForwardVector ;
NewVertex . TangentZ = UpVector ;
NewVertex . Color = Color ;
MeshBoundingBox + = NewVertex . Position ;
}
for ( int32 PointIndex : PointIndices )
{
Indices . Add ( FirstVertexIndex + PointIndex ) ;
}
} ;
FString UStreetMapComponent : : GetStreetMapAssetName ( ) const
{
return StreetMap ! = nullptr ? StreetMap - > GetName ( ) : FString ( TEXT ( " NONE " ) ) ;
}