2017-12-09 16:33:11 +00:00
// Copyright 2017 Mike Fricker. All Rights Reserved.
# pragma once
# include "StreetMap.generated.h"
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapCollisionSettings
{
GENERATED_USTRUCT_BODY ( )
public :
/** Uses triangle mesh data for collision data. (Cannot be used for physics simulation). */
UPROPERTY ( EditAnywhere , Category = " StreetMap " )
uint32 bGenerateCollision : 1 ;
/**
* If true , the physics triangle mesh will use double sided faces when doing scene queries .
* This is useful for planes and single sided meshes that need traces to work on both sides .
*/
UPROPERTY ( EditAnywhere , Category = " StreetMap " , meta = ( editcondition = " bGenerateCollision " ) )
uint32 bAllowDoubleSidedGeometry : 1 ;
FStreetMapCollisionSettings ( ) :
bGenerateCollision ( false ) ,
bAllowDoubleSidedGeometry ( false )
{
}
} ;
/** Mesh generation settings */
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapMeshBuildSettings
{
GENERATED_USTRUCT_BODY ( )
public :
/** Roads base vertical offset */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) , DisplayName = " Road Vertical Offset " )
float RoadOffesetZ ;
/** if true buildings mesh will be 3D instead of flat representation. */
UPROPERTY ( Category = StreetMap , EditAnywhere , DisplayName = " Create 3D Buildings " )
uint32 bWant3DBuildings : 1 ;
/** building level floor conversion factor in centimeters
@ todo : harmonize with OSMToCentimetersScaleFactor refactoring
*/
UPROPERTY ( Category = StreetMap , EditAnywhere , DisplayName = " Building Level Floor Factor " )
float BuildingLevelFloorFactor = 300.0f ;
/**
* If true , buildings mesh will receive light information .
* Lit buildings can ' t share vertices beyond quads ( all quads have their own face normals ) , so this uses a lot more geometry .
*/
UPROPERTY ( Category = StreetMap , EditAnywhere , DisplayName = " Lit buildings " )
uint32 bWantLitBuildings : 1 ;
/** Streets thickness */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) )
float StreetThickness ;
/** Street vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor StreetColor ;
/** Major road thickness */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) )
float MajorRoadThickness ;
/** Major road vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor MajorRoadColor ;
/** Highway thickness */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) )
float HighwayThickness ;
/** Highway vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor HighwayColor ;
/** Streets Thickness */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) )
float BuildingBorderThickness ;
/** Building border vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor BuildingBorderLinearColor ;
/** Buildings border vertical offset */
UPROPERTY ( Category = StreetMap , EditAnywhere , meta = ( ClampMin = " 0 " , UIMin = " 0 " ) )
float BuildingBorderZ ;
2017-12-11 19:33:54 +00:00
/** Water vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor WaterColor ;
/** Park vertex color */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FLinearColor ParkColor ;
2017-12-09 16:33:11 +00:00
2017-12-11 19:33:54 +00:00
2017-12-09 16:33:11 +00:00
FStreetMapMeshBuildSettings ( ) :
RoadOffesetZ ( 0.0f ) ,
bWant3DBuildings ( true ) ,
bWantLitBuildings ( true ) ,
StreetThickness ( 800.0f ) ,
2017-12-11 19:33:54 +00:00
StreetColor ( 0.93f , 0.90f , 0.87f ) ,
2017-12-09 16:33:11 +00:00
MajorRoadThickness ( 1000.0f ) ,
2017-12-11 19:33:54 +00:00
MajorRoadColor ( 0.92f , 0.91f , 0.88f ) ,
2017-12-09 16:33:11 +00:00
HighwayThickness ( 1400.0f ) ,
2017-12-11 19:33:54 +00:00
HighwayColor ( FLinearColor ( 0.91f , 0.90f , 0.87f ) ) ,
2017-12-09 16:33:11 +00:00
BuildingBorderThickness ( 20.0f ) ,
BuildingBorderLinearColor ( 0.85f , 0.85f , 0.85f ) ,
2017-12-11 19:33:54 +00:00
BuildingBorderZ ( 10.0f ) ,
ParkColor ( 0.00f , 0.55f , 0.27f ) ,
WaterColor ( 0.26f , 0.75f , 0.98f )
2017-12-09 16:33:11 +00:00
{
}
} ;
/** Types of roads */
UENUM ( BlueprintType )
enum EStreetMapRoadType
{
/** Small road or residential street */
Street ,
/** Major road or minor state highway */
MajorRoad ,
/** Highway */
Highway ,
/** Other (path, bus route, etc) */
Other ,
} ;
/** A road */
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapRoad
{
GENERATED_USTRUCT_BODY ( )
/** Name of the road */
2018-04-13 20:32:41 +00:00
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
2017-12-09 16:33:11 +00:00
FString RoadName ;
/** Type of road */
UPROPERTY ( Category = StreetMap , EditAnywhere )
TEnumAsByte < EStreetMapRoadType > RoadType ;
/** Nodes along this road, one at each point in the RoadPoints list */
UPROPERTY ( Category = StreetMap , EditAnywhere )
TArray < int32 > NodeIndices ;
/** List of all of the points on this road, one for each node in the NodeIndices list */
2018-04-13 20:32:41 +00:00
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
2017-12-09 16:33:11 +00:00
TArray < FVector2D > RoadPoints ;
// @todo: Performance: Bounding information could be computed at load time if we want to avoid the memory cost of storing it
/** 2D bounds (min) of this road's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMin ;
/** 2D bounds (max) of this road's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMax ;
/** True if this node is a one way. One way nodes are only traversable in the order the nodes are listed in the above array. */
UPROPERTY ( Category = StreetMap , EditAnywhere )
uint8 bIsOneWay : 1 ;
/** Returns this node's index */
inline int32 GetRoadIndex ( const class UStreetMap & StreetMap ) const ;
/** Gets the node for the specified point, or the node that came before that if the specified point doesn't have a node */
inline const struct FStreetMapNode & GetNodeAtPointIndexOrEarlier ( const class UStreetMap & StreetMap , const int32 PointIndex , int32 & OutNodeAtPointIndex ) const ;
/** Gets the node for the specified point, or the node that comes next after that if the specified point doesn't have a node */
inline const struct FStreetMapNode & GetNodeAtPointIndexOrLater ( const class UStreetMap & StreetMap , const int32 PointIndex , int32 & OutNodeAtPointIndex ) const ;
/** Computes the total length of this road by following along all of it's points */
float ComputeLengthOfRoad ( const class UStreetMap & StreetMap ) const ;
/** Computes the distance along the road between two points on the road. Be careful! The same node can appear on a road twice. */
float ComputeDistanceBetweenNodesOnRoad ( const class UStreetMap & StreetMap , const int32 NodePointIndexA , const int32 NodePointIndexB ) const ;
/** Given a position along the road, finds the nodes that come earlier and later on that road */
void FindEarlierAndLaterNodesForPositionAlongRoad ( const class UStreetMap & StreetMap , const float PositionAlongRoad , const FStreetMapNode * & OutEarlierNode , float & OutEarlierNodePositionAlongRoad , const FStreetMapNode * & OutLaterNode , float & OutLaterNodePositionAlongRoad ) const ;
/** Given a node that exists at a point index on this road, finds the nodes that are immediately earlier and later to it (adjacent.) Will set a nullptr if there are no earlier or later nodes */
void FindEarlierAndLaterNodes ( const class UStreetMap & StreetMap , const int32 RoadPointIndex , const FStreetMapNode * & OutEarlierNode , float & OutEarlierNodePositionAlongRoad , const FStreetMapNode * & OutLaterNode , float & OutLaterNodePositionAlongRoad ) const ;
// NOTE: You may notice there is no "FindPointIndexForNode()" method in this class. This is because
// the same node may appear more than once on any single road, so it's never safe to ask for
// a single point index on a road.
/** Given a node that exists on this road, computes the position along this road of that node */
float FindPositionAlongRoadForNode ( const class UStreetMap & StreetMap , const int32 PointIndexForNode ) const ;
/** Computes the location of a point along this road, given a distance along this road from the road's beginning */
FVector2D MakeLocationAlongRoad ( const class UStreetMap & StreetMap , const float PositionAlongRoad ) const ;
/** @return True if this is a one way road */
inline bool IsOneWay ( ) const
{
return bIsOneWay = = 1 ? true : false ;
}
} ;
/** Nodes have a list of road refs, one for each road that intersects this node. Each road ref references a road and also the
point along that road where this node exists . */
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapRoadRef
{
GENERATED_USTRUCT_BODY ( )
/** Index of road in the list of all roads in this street map */
UPROPERTY ( Category = StreetMap , EditAnywhere )
int32 RoadIndex ;
/** Index of the point along road where this node exists */
UPROPERTY ( Category = StreetMap , EditAnywhere )
int32 RoadPointIndex ;
} ;
/** Describes a node on a road. Nodes usually connect at least two roads together, but they might also exist at the end of a dead-end street. They are sort of like an "intersection". */
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapNode
{
GENERATED_USTRUCT_BODY ( )
/** All of the roads that intersect this node. We have references to each of these roads, as well as the point along each
road where this node exists */
UPROPERTY ( Category = StreetMap , EditAnywhere )
TArray < FStreetMapRoadRef > RoadRefs ;
/** Returns this node's index */
inline int32 GetNodeIndex ( const UStreetMap & StreetMap ) const ;
/** Gets the location of this node */
inline FVector2D GetLocation ( const UStreetMap & StreetMap ) const ;
///
/// Utility functions which may be useful for pathfinding algorithms (not used internally.)
///
/** Pathfinding: Given a node that is known to connect to this node via some road, searches for the road and returns it */
inline const FStreetMapRoad & GetShortestCostRoadToNode ( UStreetMap & StreetMap , const FStreetMapNode & OtherNode , const bool bIsTravelingForward , int32 & OutPointIndexOnRoad ) const ;
/** Pathfinding: Returns true if this node is the end point on a road with no connections */
inline bool IsDeadEnd ( const UStreetMap & StreetMap ) const ;
/** Pathfinding: Returns the number of connections between this node and other roads, taking into account the direction of travel */
inline int32 GetConnectionCount ( const class UStreetMap & StreetMap , const bool bIsTravelingForward ) const ;
/** Pathfinding: Returns a connected node by index (between 0 and GetConnectionCount() - 1 ), taking into account the direction of travel. Also returns the connecting road and wherabouts on the road the connection occurs */
inline const FStreetMapNode * GetConnection ( const class UStreetMap & StreetMap , const int32 ConnectionIndex , const bool bIsTravelingForward , const struct FStreetMapRoad * * OutConnectingRoad = nullptr , int32 * OutPointIndexOnRoad = nullptr , int32 * OutConnectedNodePointIndexOnRoad = nullptr ) const ;
/** Pathfinding: Estimates the 'cost' of the specified connected by index (between 0 and GetConnectionCount() - 1) */
inline float GetConnectionCost ( const class UStreetMap & StreetMap , const int32 ConnectionIndex , const bool bIsTravelingForward ) const ;
} ;
/** A building */
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapBuilding
{
GENERATED_USTRUCT_BODY ( )
/** Name of the building */
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
FString BuildingName ;
/** Polygon points that define the perimeter of the building */
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
TArray < FVector2D > BuildingPoints ;
/** Height of the building in meters (if known, otherwise zero) */
UPROPERTY ( Category = StreetMap , EditAnywhere )
float Height ;
/** Levels of the building (if known, otherwise zero) */
UPROPERTY ( Category = StreetMap , EditAnywhere )
int BuildingLevels ;
/** The amenity of the building */
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
FString Amenity ;
/** The building type */
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
FString Building ;
// @todo: Performance: Bounding information could be computed at load time if we want to avoid the memory cost of storing it
/** 2D bounds (min) of this building's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMin ;
/** 2D bounds (max) of this building's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMax ;
} ;
2017-12-11 19:33:54 +00:00
// Vinfamy added
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapWater
{
GENERATED_USTRUCT_BODY ( )
/** Name of the water */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FString WaterName ;
/** Polygon points that define the perimeter of the water */
UPROPERTY ( Category = StreetMap , EditAnywhere )
TArray < FVector2D > WaterPoints ;
// @todo: Performance: Bounding information could be computed at load time if we want to avoid the memory cost of storing it
/** 2D bounds (min) of this water's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMin ;
/** 2D bounds (max) of this water's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMax ;
} ;
USTRUCT ( BlueprintType )
struct STREETMAPRUNTIME_API FStreetMapPark
{
GENERATED_USTRUCT_BODY ( )
/** Name of the park */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FString ParkName ;
/** Polygon points that define the perimeter of the park */
UPROPERTY ( Category = StreetMap , EditAnywhere )
TArray < FVector2D > ParkPoints ;
// @todo: Performance: Bounding information could be computed at load time if we want to avoid the memory cost of storing it
/** 2D bounds (min) of this water's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMin ;
/** 2D bounds (max) of this water's points */
UPROPERTY ( Category = StreetMap , EditAnywhere )
FVector2D BoundsMax ;
} ;
2017-12-09 16:33:11 +00:00
/** A loaded street map */
UCLASS ( Blueprintable , BlueprintType )
class STREETMAPRUNTIME_API UStreetMap : public UObject
{
GENERATED_BODY ( )
public :
/** Default constructor for UStreetMap */
UStreetMap ( ) ;
// UObject overrides
virtual void GetAssetRegistryTags ( TArray < FAssetRegistryTag > & OutTags ) const override ;
/** Gets the roads in this street map (read only) */
const TArray < FStreetMapRoad > & GetRoads ( ) const
{
return Roads ;
}
/** Gets the roads in this street map */
TArray < FStreetMapRoad > & GetRoads ( )
{
return Roads ;
}
/** Gets the nodes on the map (read only.) Nodes describe intersections between roads */
const TArray < FStreetMapNode > & GetNodes ( ) const
{
return Nodes ;
}
/** Gets the nodes on the map. Nodes describe intersections between roads */
TArray < FStreetMapNode > & GetNodes ( )
{
return Nodes ;
}
/** Gets all of the buildings (read only) */
const TArray < FStreetMapBuilding > & GetBuildings ( ) const
{
return Buildings ;
}
/** Gets all of the buildings */
TArray < FStreetMapBuilding > & GetBuildings ( )
{
return Buildings ;
}
2017-12-11 19:33:54 +00:00
/** vinfamy Gets all of the waters (read only) */
const TArray < FStreetMapWater > & GetWaters ( ) const
{
return Waters ;
}
/** Gets all of the waters */
TArray < FStreetMapWater > & GetWaters ( )
{
return Waters ;
}
/** vinfamy Gets all of the parks (read only) */
const TArray < FStreetMapPark > & GetParks ( ) const
{
return Parks ;
}
/** Gets all of the parks */
TArray < FStreetMapPark > & GetParks ( )
{
return Parks ;
}
2017-12-09 16:33:11 +00:00
/** Gets the bounding box of the map */
FVector2D GetBoundsMin ( ) const
{
return BoundsMin ;
}
FVector2D GetBoundsMax ( ) const
{
return BoundsMax ;
}
/** Vinfamy added */
UFUNCTION ( BlueprintCallable )
2018-04-16 09:30:11 +00:00
TArray < FStreetMapBuilding > FindBuilding ( const FVector2D Point ) ;
2018-04-13 20:32:41 +00:00
UFUNCTION ( BlueprintCallable )
FStreetMapRoad FindRoad ( const FVector2D Point ) ;
2018-03-31 21:38:37 +00:00
UFUNCTION ( BlueprintCallable )
FStreetMapBuilding FindBuildingFromArray ( const FVector2D Point , TArray < FStreetMapBuilding > SmallBuildings ) ;
2017-12-12 20:52:18 +00:00
UFUNCTION ( BlueprintCallable )
TArray < FStreetMapBuilding > SearchByBuildingName ( FString SearchTerm , FString Amenity ) const ;
2018-03-31 21:38:37 +00:00
UFUNCTION ( BlueprintCallable )
TArray < FStreetMapBuilding > SearchByBuildingNameFromArray ( FString SearchTerm , FString Amenity , TArray < FStreetMapBuilding > SmallBuildings ) const ;
2017-12-09 16:33:11 +00:00
2017-12-12 20:52:18 +00:00
UFUNCTION ( BlueprintCallable )
TArray < FStreetMapBuilding > SearchByAmenity ( FString Amenity ) const ;
TArray < FStreetMapBuilding > FilterByAmenity ( FString Amenity , TArray < FStreetMapBuilding > SmallBuildings ) const ;
2017-12-09 16:33:11 +00:00
protected :
/** List of roads */
2018-04-13 20:32:41 +00:00
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
2017-12-09 16:33:11 +00:00
TArray < FStreetMapRoad > Roads ;
/** List of nodes on this map. Nodes describe interesting points along roads, usually where roads intersect or at the end of a dead-end street */
UPROPERTY ( Category = StreetMap , VisibleAnywhere )
TArray < FStreetMapNode > Nodes ;
2017-12-11 19:33:54 +00:00
/** Vinfamy added - List of waters (natural = water) */
UPROPERTY ( Category = StreetMap , VisibleAnywhere )
TArray < FStreetMapWater > Waters ;
/** List of parks (leisure = park) */
UPROPERTY ( Category = StreetMap , VisibleAnywhere )
TArray < FStreetMapPark > Parks ;
2017-12-09 16:33:11 +00:00
/** List of all buildings on the street map */
UPROPERTY ( Category = StreetMap , EditAnywhere , BlueprintReadWrite )
TArray < FStreetMapBuilding > Buildings ;
/** 2D bounds (min) of this map's roads and buildings */
UPROPERTY ( Category = StreetMap , VisibleAnywhere )
FVector2D BoundsMin ;
/** 2D bounds (max) of this map's roads and buildings */
UPROPERTY ( Category = StreetMap , VisibleAnywhere )
FVector2D BoundsMax ;
# if WITH_EDITORONLY_DATA
/** Importing data and options used for this mesh */
UPROPERTY ( VisibleAnywhere , Instanced , Category = ImportSettings )
class UAssetImportData * AssetImportData ;
friend class UStreetMapFactory ;
friend class UStreetMapReimportFactory ;
friend class FStreetMapAssetTypeActions ;
# endif // WITH_EDITORONLY_DATA
} ;
inline int32 FStreetMapRoad : : GetRoadIndex ( const UStreetMap & StreetMap ) const
{
// Pointer arithmetic based on array start
const int32 RoadIndex = this - StreetMap . GetRoads ( ) . GetData ( ) ;
return RoadIndex ;
}
inline const FStreetMapNode & FStreetMapRoad : : GetNodeAtPointIndexOrEarlier ( const UStreetMap & StreetMap , const int32 PointIndex , int32 & OutNodeAtPointIndex ) const
{
const FStreetMapNode * CurrentOrEarlierPointNode = nullptr ;
for ( int32 NodePointIndex = PointIndex ; NodePointIndex > = 0 ; - - NodePointIndex )
{
if ( NodeIndices [ NodePointIndex ] ! = INDEX_NONE )
{
CurrentOrEarlierPointNode = & StreetMap . GetNodes ( ) [ NodeIndices [ NodePointIndex ] ] ;
OutNodeAtPointIndex = NodePointIndex ;
break ;
}
}
return * CurrentOrEarlierPointNode ;
}
inline const FStreetMapNode & FStreetMapRoad : : GetNodeAtPointIndexOrLater ( const UStreetMap & StreetMap , const int32 PointIndex , int32 & OutNodeAtPointIndex ) const
{
const FStreetMapNode * NextOrUpcomingNode = nullptr ;
for ( int32 NodePointIndex = PointIndex ; NodePointIndex < RoadPoints . Num ( ) ; + + NodePointIndex )
{
if ( NodeIndices [ NodePointIndex ] ! = INDEX_NONE )
{
NextOrUpcomingNode = & StreetMap . GetNodes ( ) [ NodeIndices [ NodePointIndex ] ] ;
OutNodeAtPointIndex = NodePointIndex ;
break ;
}
}
return * NextOrUpcomingNode ;
}
inline float FStreetMapRoad : : ComputeLengthOfRoad ( const class UStreetMap & StreetMap ) const
{
// @todo: Performance: We could cache the road's total length at load time to avoid having to compute it,
// or we could save it right into the asset file
return ComputeDistanceBetweenNodesOnRoad ( StreetMap , 0 , this - > NodeIndices . Num ( ) - 1 ) ;
}
inline float FStreetMapRoad : : ComputeDistanceBetweenNodesOnRoad ( const class UStreetMap & StreetMap , const int32 NodePointIndexA , const int32 NodePointIndexB ) const
{
float TotalDistanceSoFar = 0.0f ;
// NOTE: It is very important that we use the actual road point indices here and not nodes directly, because the same node can appear
// more than once on a single road!
// @todo: Performance: We can cache distances between connected nodes rather than computing them every time. This
// can be computed at load time or at import time (and stored in the asset). Most of the other functions
// in this class that perform Size() computations could be changed to use cached distances also!
const int32 SmallerPointIndex = FMath : : Max ( 0 , FMath : : Min ( NodePointIndexA , NodePointIndexB ) ) ;
const int32 LargerPointIndex = FMath : : Min ( RoadPoints . Num ( ) - 1 , FMath : : Max ( NodePointIndexA , NodePointIndexB ) ) ;
for ( int32 PointIndex = SmallerPointIndex ; PointIndex < LargerPointIndex ; + + PointIndex )
{
const FVector2D PointLocation = RoadPoints [ PointIndex ] ;
const FVector2D NextPointLocation = RoadPoints [ PointIndex + 1 ] ;
const float DistanceBetweenPoints = ( NextPointLocation - PointLocation ) . Size ( ) ;
TotalDistanceSoFar + = DistanceBetweenPoints ;
}
// @todo: Malformed data can cause this assertion to trigger. This could be a single road with at least two adjacent nodes
// at the exact same location. We need to filter this out at load time probably.
// check( TotalDistanceSoFar > 0.0f );
return TotalDistanceSoFar ;
}
inline void FStreetMapRoad : : FindEarlierAndLaterNodesForPositionAlongRoad ( const class UStreetMap & StreetMap , const float PositionAlongRoad , const FStreetMapNode * & OutEarlierNode , float & OutEarlierNodePositionAlongRoad , const FStreetMapNode * & OutLaterNode , float & OutLaterNodePositionAlongRoad ) const
{
float CurrentPointPositionAlongRoad = 0.0f ;
const FStreetMapNode * EarlierStreetMapNode = nullptr ;
const FStreetMapNode * LaterStreetMapNode = nullptr ;
const int32 NumPoints = RoadPoints . Num ( ) ;
for ( int32 CurrentPointIndex = 0 ; CurrentPointIndex < NumPoints - 1 ; + + CurrentPointIndex )
{
if ( this - > NodeIndices [ CurrentPointIndex ] ! = INDEX_NONE )
{
EarlierStreetMapNode = & StreetMap . GetNodes ( ) [ this - > NodeIndices [ CurrentPointIndex ] ] ;
OutEarlierNodePositionAlongRoad = CurrentPointPositionAlongRoad ;
}
const int32 NextPointIndex = CurrentPointIndex + 1 ;
const FVector2D CurrentPointLocation = RoadPoints [ CurrentPointIndex ] ;
const FVector2D NextPointLocation = RoadPoints [ NextPointIndex ] ;
const float DistanceBetweenPoints = ( NextPointLocation - CurrentPointLocation ) . Size ( ) ;
const float NextPointPositionAlongRoad = CurrentPointPositionAlongRoad + DistanceBetweenPoints ;
if ( NextPointPositionAlongRoad > = PositionAlongRoad )
{
if ( NodeIndices [ NextPointIndex ] ! = INDEX_NONE )
{
LaterStreetMapNode = & StreetMap . GetNodes ( ) [ this - > NodeIndices [ NextPointIndex ] ] ;
OutLaterNodePositionAlongRoad = NextPointPositionAlongRoad ;
break ;
}
}
CurrentPointPositionAlongRoad = NextPointPositionAlongRoad ;
}
check ( EarlierStreetMapNode ! = nullptr & & LaterStreetMapNode ! = nullptr ) ;
OutEarlierNode = EarlierStreetMapNode ;
OutLaterNode = LaterStreetMapNode ;
}
inline void FStreetMapRoad : : FindEarlierAndLaterNodes ( const class UStreetMap & StreetMap , const int32 RoadPointIndex , const FStreetMapNode * & OutEarlierNode , float & OutEarlierNodePositionAlongRoad , const FStreetMapNode * & OutLaterNode , float & OutLaterNodePositionAlongRoad ) const
{
OutEarlierNode = nullptr ;
OutEarlierNodePositionAlongRoad = - 1.0f ;
OutLaterNode = nullptr ;
OutLaterNodePositionAlongRoad = - 1.0f ;
for ( int32 EarlierPointIndex = RoadPointIndex - 1 ; EarlierPointIndex > = 0 ; - - EarlierPointIndex )
{
if ( this - > NodeIndices [ EarlierPointIndex ] ! = INDEX_NONE )
{
OutEarlierNode = & StreetMap . GetNodes ( ) [ this - > NodeIndices [ EarlierPointIndex ] ] ;
OutEarlierNodePositionAlongRoad = FindPositionAlongRoadForNode ( StreetMap , EarlierPointIndex ) ;
break ;
}
}
for ( int32 LaterPointIndex = RoadPointIndex + 1 ; LaterPointIndex < this - > RoadPoints . Num ( ) ; + + LaterPointIndex )
{
if ( this - > NodeIndices [ LaterPointIndex ] ! = INDEX_NONE )
{
OutLaterNode = & StreetMap . GetNodes ( ) [ this - > NodeIndices [ LaterPointIndex ] ] ;
OutLaterNodePositionAlongRoad = FindPositionAlongRoadForNode ( StreetMap , LaterPointIndex ) ;
break ;
}
}
}
inline float FStreetMapRoad : : FindPositionAlongRoadForNode ( const class UStreetMap & StreetMap , const int32 PointIndexForNode ) const
{
float CurrentPointPositionAlongRoad = 0.0f ;
bool bFoundLocation = false ;
const int32 NumPoints = RoadPoints . Num ( ) ;
for ( int32 CurrentPointIndex = 0 ; CurrentPointIndex < PointIndexForNode ; + + CurrentPointIndex )
{
const FVector2D CurrentPointLocation = RoadPoints [ CurrentPointIndex ] ;
const FVector2D NextPointLocation = RoadPoints [ CurrentPointIndex + 1 ] ;
const float DistanceBetweenPoints = ( NextPointLocation - CurrentPointLocation ) . Size ( ) ;
const float NextPointPositionAlongRoad = CurrentPointPositionAlongRoad + DistanceBetweenPoints ;
CurrentPointPositionAlongRoad = NextPointPositionAlongRoad ;
}
return CurrentPointPositionAlongRoad ;
}
inline FVector2D FStreetMapRoad : : MakeLocationAlongRoad ( const class UStreetMap & StreetMap , const float PositionAlongRoad ) const
{
FVector2D LocationAlongRoad = FVector2D : : ZeroVector ;
float CurrentPointPositionAlongRoad = 0.0f ;
bool bFoundLocation = false ;
const int32 NumPoints = RoadPoints . Num ( ) ;
for ( int32 CurrentPointIndex = 0 ; CurrentPointIndex < NumPoints - 1 ; + + CurrentPointIndex )
{
const FVector2D CurrentPointLocation = RoadPoints [ CurrentPointIndex ] ;
const FVector2D NextPointLocation = RoadPoints [ CurrentPointIndex + 1 ] ;
const float DistanceBetweenPoints = ( NextPointLocation - CurrentPointLocation ) . Size ( ) ;
const float NextPointPositionAlongRoad = CurrentPointPositionAlongRoad + DistanceBetweenPoints ;
if ( NextPointPositionAlongRoad > = PositionAlongRoad )
{
const float LerpAlpha = ( PositionAlongRoad - CurrentPointPositionAlongRoad ) / DistanceBetweenPoints ;
LocationAlongRoad = FMath : : Lerp ( CurrentPointLocation , NextPointLocation , LerpAlpha ) ;
bFoundLocation = true ;
break ;
}
CurrentPointPositionAlongRoad = NextPointPositionAlongRoad ;
}
check ( bFoundLocation = = true ) ;
return LocationAlongRoad ;
}
inline int32 FStreetMapNode : : GetNodeIndex ( const UStreetMap & StreetMap ) const
{
// Pointer arithmetic based on array start
const int32 NodeIndex = this - StreetMap . GetNodes ( ) . GetData ( ) ;
return NodeIndex ;
}
inline bool FStreetMapNode : : IsDeadEnd ( const UStreetMap & StreetMap ) const
{
if ( RoadRefs . Num ( ) = = 1 )
{
// @todo: If this road only connects to dead end roads that oppose the direction, we need to treat this road
// as a dead end. This case should be extremely uncommon, though!
const FStreetMapRoadRef & SoleRoadRef = RoadRefs [ 0 ] ;
const FStreetMapRoad & SoleRoad = StreetMap . GetRoads ( ) [ SoleRoadRef . RoadIndex ] ;
if ( SoleRoadRef . RoadPointIndex = = 0 | | SoleRoadRef . RoadPointIndex = = ( SoleRoad . NodeIndices . Num ( ) - 1 ) )
{
// The node is attached to only one road, and the node is at the very end of one of the ends of the road
return true ;
}
}
return false ;
}
inline FVector2D FStreetMapNode : : GetLocation ( const UStreetMap & StreetMap ) const
{
const FStreetMapRoadRef & MyFirstRoadRef = RoadRefs [ 0 ] ;
const FVector2D Location = StreetMap . GetRoads ( ) [ MyFirstRoadRef . RoadIndex ] . RoadPoints [ MyFirstRoadRef . RoadPointIndex ] ;
return Location ;
}
inline const FStreetMapRoad & FStreetMapNode : : GetShortestCostRoadToNode ( UStreetMap & StreetMap , const FStreetMapNode & OtherNode , const bool bIsTravelingForward , int32 & OutPointIndexOnRoad ) const
{
const FStreetMapRoad * ConnectingRoad = nullptr ;
float BestConnectionCost = TNumericLimits < float > : : Max ( ) ;
int32 BestConnectionIndex = INDEX_NONE ;
int32 BestConnectionPointIndex = INDEX_NONE ;
const int32 ConnectionCount = GetConnectionCount ( StreetMap , bIsTravelingForward ) ;
for ( int32 ConnectionIndex = 0 ; ConnectionIndex < ConnectionCount ; + + ConnectionIndex )
{
int32 MyPointIndexOnRoad ;
int32 ConnectedNodePointIndexOnRoad ;
const FStreetMapRoad * CurrentConnectingRoad ;
const FStreetMapNode * ConnectedNode = GetConnection ( StreetMap , ConnectionIndex , bIsTravelingForward , /* Out */ & CurrentConnectingRoad , /* Out */ & MyPointIndexOnRoad , /* Out */ & ConnectedNodePointIndexOnRoad ) ;
if ( ConnectedNode = = & OtherNode )
{
if ( BestConnectionIndex ! = INDEX_NONE )
{
// We evaluate cost lazily, only in the unusual case of the two nodes being connected by multiple roads
BestConnectionCost = GetConnectionCost ( StreetMap , BestConnectionIndex , bIsTravelingForward ) ;
if ( GetConnectionCost ( StreetMap , ConnectionIndex , bIsTravelingForward ) < BestConnectionCost )
{
ConnectingRoad = CurrentConnectingRoad ;
BestConnectionIndex = ConnectionIndex ;
BestConnectionPointIndex = MyPointIndexOnRoad ;
}
}
else
{
ConnectingRoad = CurrentConnectingRoad ;
BestConnectionIndex = ConnectionIndex ;
BestConnectionPointIndex = MyPointIndexOnRoad ;
}
}
}
check ( ConnectingRoad ! = nullptr ) ;
OutPointIndexOnRoad = BestConnectionPointIndex ;
return * ConnectingRoad ;
}
inline int32 FStreetMapNode : : GetConnectionCount ( const UStreetMap & StreetMap , const bool bIsTravelingForward ) const
{
// NOTE: We're iterating here in the exact same order as in the GetConnection() function below! That's critically important!
int32 TotalConnections = 0 ;
for ( const FStreetMapRoadRef & RoadRef : RoadRefs )
{
const FStreetMapRoad & Road = StreetMap . GetRoads ( ) [ RoadRef . RoadIndex ] ;
if ( RoadRef . RoadPointIndex > 0 & & ( ! bIsTravelingForward | | ! Road . IsOneWay ( ) ) )
{
// We connect to a node earlier up this road
+ + TotalConnections ;
}
if ( RoadRef . RoadPointIndex < ( Road . NodeIndices . Num ( ) - 1 ) & & ( bIsTravelingForward | | ! Road . IsOneWay ( ) ) )
{
// We connect to a node further down this road
+ + TotalConnections ;
}
}
return TotalConnections ;
}
inline const FStreetMapNode * FStreetMapNode : : GetConnection ( const UStreetMap & StreetMap , const int32 ConnectionIndex , const bool bIsTravelingForward , const FStreetMapRoad * * OutConnectingRoad , int32 * OutPointIndexOnRoad , int32 * OutConnectedNodePointIndexOnRoad ) const
{
if ( OutConnectingRoad ! = nullptr )
{
* OutConnectingRoad = nullptr ;
}
const FStreetMapNode * ConnectedNode = nullptr ;
// @todo: Performance: We can improve performance by caching additional connectivity information right on
// the node itself. This function would be a hot spot for any sort of pathfinding computation
// NOTE: We're iterating here in the exact same order as in the GetConnectionCount() function above! That's critically important!
int32 CurrentConnectionIndex = 0 ;
for ( const FStreetMapRoadRef & RoadRef : RoadRefs )
{
const FStreetMapRoad & Road = StreetMap . GetRoads ( ) [ RoadRef . RoadIndex ] ;
// @todo: Performance: We could avoid the "while" loops below by not storing INDEX_NONEs in the NodeIndices array,
// but instead mapping them to points by going through the node itself, then back to a road
if ( RoadRef . RoadPointIndex > 0 & & ( ! bIsTravelingForward | | ! Road . IsOneWay ( ) ) )
{
// We connect to an earlier node up this road
if ( CurrentConnectionIndex = = ConnectionIndex )
{
int32 EarlierNodeRoadPointIndex = RoadRef . RoadPointIndex - 1 ;
while ( Road . NodeIndices [ EarlierNodeRoadPointIndex ] = = INDEX_NONE )
{
- - EarlierNodeRoadPointIndex ;
}
const int32 EarlierNodeIndex = Road . NodeIndices [ EarlierNodeRoadPointIndex ] ;
const FStreetMapNode & EarlierNode = StreetMap . GetNodes ( ) [ EarlierNodeIndex ] ;
ConnectedNode = & EarlierNode ;
if ( OutConnectingRoad ! = nullptr )
{
* OutConnectingRoad = & Road ;
}
if ( OutPointIndexOnRoad ! = nullptr )
{
* OutPointIndexOnRoad = RoadRef . RoadPointIndex ;
}
if ( OutConnectedNodePointIndexOnRoad ! = nullptr )
{
* OutConnectedNodePointIndexOnRoad = EarlierNodeRoadPointIndex ;
}
// Got it!
break ;
}
+ + CurrentConnectionIndex ;
}
if ( RoadRef . RoadPointIndex < ( Road . NodeIndices . Num ( ) - 1 ) & & ( bIsTravelingForward | | ! Road . IsOneWay ( ) ) )
{
// We connect to node further down this road
if ( CurrentConnectionIndex = = ConnectionIndex )
{
int32 LaterNodeRoadPointIndex = RoadRef . RoadPointIndex + 1 ;
while ( Road . NodeIndices [ LaterNodeRoadPointIndex ] = = INDEX_NONE )
{
+ + LaterNodeRoadPointIndex ;
}
const int32 LaterNodeIndex = Road . NodeIndices [ LaterNodeRoadPointIndex ] ;
const FStreetMapNode & LaterNode = StreetMap . GetNodes ( ) [ LaterNodeIndex ] ;
ConnectedNode = & LaterNode ;
if ( OutConnectingRoad ! = nullptr )
{
* OutConnectingRoad = & Road ;
}
if ( OutPointIndexOnRoad ! = nullptr )
{
* OutPointIndexOnRoad = RoadRef . RoadPointIndex ;
}
if ( OutConnectedNodePointIndexOnRoad ! = nullptr )
{
* OutConnectedNodePointIndexOnRoad = LaterNodeRoadPointIndex ;
}
// Got it!
break ;
}
+ + CurrentConnectionIndex ;
}
}
check ( ConnectedNode ! = nullptr ) ;
return ConnectedNode ;
}
inline float FStreetMapNode : : GetConnectionCost ( const UStreetMap & StreetMap , const int32 ConnectionIndex , const bool bIsTravelingForward ) const
{
/////////////////////////////////////////////////////////
// Tweakables for connection cost estimation
//
const float MaxSpeedLimit = 120.0f ; // 120 Km/hr
const float HighwaySpeed = 110.0f ;
const float HighwayTrafficFactor = 0.0 ;
const float MajorRoadSpeed = 70.0f ;
const float MajorRoadTrafficFactor = 0.2f ;
const float StreetSpeed = 40.0f ;
const float StreetTrafficFactor = 1.0f ;
/////////////////////////////////////////////////////////
// @todo: Street map pathfinding is a grand art in itself, and estimating cost of connections is
// a very complicated problem. We're only doing some basic estimates for now, but in the
// future we could consider taking into account the cost of different types of turns and
// intersections, lane counts, actual speed limits, etc.
int32 MyPointIndexOnRoad ;
int32 ConnectedNodePointIndexOnRoad ;
const FStreetMapRoad * ConnectingRoad = nullptr ;
const FStreetMapNode & ConnectedNode = * GetConnection ( StreetMap , ConnectionIndex , bIsTravelingForward , /* Out */ & ConnectingRoad , /* Out */ & MyPointIndexOnRoad , /* Out */ & ConnectedNodePointIndexOnRoad ) ;
const float DistanceBetweenNodes = ConnectingRoad - > ComputeDistanceBetweenNodesOnRoad ( StreetMap , MyPointIndexOnRoad , ConnectedNodePointIndexOnRoad ) ;
float TotalCost = DistanceBetweenNodes ;
// Apply some scaling to the cost of traveling between these nodes
{
float SpeedLimit = 0.0f ;
float TrafficFactor = 0.0f ;
switch ( ConnectingRoad - > RoadType )
{
case EStreetMapRoadType : : Highway :
SpeedLimit = HighwaySpeed ;
TrafficFactor = HighwayTrafficFactor ;
break ;
case EStreetMapRoadType : : MajorRoad :
SpeedLimit = MajorRoadSpeed ;
TrafficFactor = MajorRoadTrafficFactor ;
break ;
case EStreetMapRoadType : : Street :
case EStreetMapRoadType : : Other :
SpeedLimit = StreetSpeed ;
TrafficFactor = StreetTrafficFactor ;
break ;
default :
check ( 0 ) ;
break ;
}
const float RoadSpeedCostScale = ( 1.0f - ( SpeedLimit / MaxSpeedLimit ) ) ;
TotalCost * = 1.0f + RoadSpeedCostScale * 15.0f * ( 0.5f + TrafficFactor * 0.5f ) ;
}
return TotalCost ;
}