11 Dec work

This commit is contained in:
vinfamy 2017-12-12 02:33:54 +07:00
parent 10131860d4
commit 199a62edfe
11 changed files with 494 additions and 5 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -335,6 +335,72 @@ bool FOSMFile::ProcessAttribute( const TCHAR* AttributeName, const TCHAR* Attrib
// Other type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building
}
}
/**
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "natural" ) ) )
{
if( !FCString::Stricmp( AttributeValue, TEXT( "water" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
else
{
// Other natural type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building
}
}
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "waterway" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "water" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "landuse" ) ) )
{
if( !FCString::Stricmp( AttributeValue, TEXT( "basin" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
else if( !FCString::Stricmp( AttributeValue, TEXT( "reservoir" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
else if( !FCString::Stricmp( AttributeValue, TEXT( "pond" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Water;
}
}
*/
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "leisure" ) ) )
{
if( !FCString::Stricmp( AttributeValue, TEXT( "park" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Park;
}
else if( !FCString::Stricmp( AttributeValue, TEXT( "fitness_centre" ) ) )
{
CurrentWayInfo->WayType = EOSMWayType::Building;
CurrentWayInfo->Amenity = AttributeValue;
}
else
{
// Other natural type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building
}
}
else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "height" ) ) )
{
// Check to see if there is a space character in the height value. For now, we're looking

View file

@ -129,6 +129,12 @@ public:
/** Default type of building. A general catch-all. */
Building,
/** Default type of building. A general catch-all. */
Water,
/** Default type of building. A general catch-all. */
Park,
///

View file

@ -292,7 +292,7 @@ bool UStreetMapFactory::LoadFromOpenStreetMapXMLFile( UStreetMap* StreetMap, FSt
// Vinfamy added
if ( NewBuilding.BuildingLevels == 0 )
{
NewBuilding.BuildingLevels = rand() % 5 + 1;
NewBuilding.BuildingLevels = rand() % 10 + 3;
}
NewBuilding.Amenity = OSMWay.Amenity;
NewBuilding.Building = OSMWay.Building;
@ -316,6 +316,207 @@ bool UStreetMapFactory::LoadFromOpenStreetMapXMLFile( UStreetMap* StreetMap, FSt
return false;
};
// Adds a water to the street map using the OpenStreetMap data, flattening the road's coordinates into our map's space
auto AddWaterForWay = [ConvertLatLongToMetersRelative, OSMToCentimetersScaleFactor](
const FOSMFile& OSMFile,
UStreetMap& StreetMapRef,
const FOSMFile::FOSMWayInfo& OSMWay ) -> bool
{
if( OSMWay.WayType == FOSMFile::EOSMWayType::Water )
{
// Require at least three points so that we don't have degenerate polygon!
if( OSMWay.Nodes.Num() > 2 )
{
// Create a Water for this way
FStreetMapWater& NewWater = *new( StreetMapRef.Waters )FStreetMapWater();
FVector2D BoundsMin( TNumericLimits<float>::Max(), TNumericLimits<float>::Max() );
FVector2D BoundsMax( TNumericLimits<float>::Lowest(), TNumericLimits<float>::Lowest() );
NewWater.WaterPoints.AddUninitialized( OSMWay.Nodes.Num() );
int32 CurWaterPoint = 0;
for( const FOSMFile::FOSMNodeInfo* OSMNodePtr : OSMWay.Nodes )
{
const FOSMFile::FOSMNodeInfo& OSMNode = *OSMNodePtr;
// Transform all points relative to the center of the latitude/longitude bounds, so that
// we get as much precision as possible.
const double RelativeToLatitude = OSMFile.AverageLatitude;
const double RelativeToLongitude = OSMFile.AverageLongitude;
const FVector2D NodePos = ConvertLatLongToMetersRelative(
OSMNode.Latitude,
OSMNode.Longitude,
RelativeToLatitude,
RelativeToLongitude ) * OSMToCentimetersScaleFactor;
// Update bounding box
{
if( NodePos.X < BoundsMin.X )
{
BoundsMin.X = NodePos.X;
}
if( NodePos.Y < BoundsMin.Y )
{
BoundsMin.Y = NodePos.Y;
}
if( NodePos.X > BoundsMax.X )
{
BoundsMax.X = NodePos.X;
}
if( NodePos.Y > BoundsMax.Y )
{
BoundsMax.Y = NodePos.Y;
}
}
// Fill in the points
NewWater.WaterPoints[ CurWaterPoint++ ] = NodePos;
}
// Make sure the Water ended up with a closed polygon, then remove the final (redundant) point
const bool bIsClosed = NewWater.WaterPoints[ 0 ].Equals( NewWater.WaterPoints[ NewWater.WaterPoints.Num() - 1 ], KINDA_SMALL_NUMBER );
if( bIsClosed )
{
// Remove the final redundant point
NewWater.WaterPoints.Pop();
}
else
{
// Wasn't expecting to have an unclosed shape. Our tolerances might be off, or the data was malformed.
// Either way, it shouldn't be a problem as we'll close the shape ourselves below.
// @todo: Log this for the user as an import warning
}
NewWater.WaterName = OSMWay.Name;
if( NewWater.WaterName.IsEmpty() )
{
NewWater.WaterName = OSMWay.Ref;
}
NewWater.BoundsMin = BoundsMin;
NewWater.BoundsMax = BoundsMax;
StreetMapRef.BoundsMin.X = FMath::Min( StreetMapRef.BoundsMin.X, BoundsMin.X );
StreetMapRef.BoundsMin.Y = FMath::Min( StreetMapRef.BoundsMin.Y, BoundsMin.Y );
StreetMapRef.BoundsMax.X = FMath::Max( StreetMapRef.BoundsMax.X, BoundsMax.X );
StreetMapRef.BoundsMax.Y = FMath::Max( StreetMapRef.BoundsMax.Y, BoundsMax.Y );
return true;
}
else
{
// NOTE: Skipped adding Water for way because it has less than 3 points
// @todo: Log this for the user as an import warning
}
}
return false;
};
// Adds a Park to the street map using the OpenStreetMap data, flattening the road's coordinates into our map's space
auto AddParkForWay = [ConvertLatLongToMetersRelative, OSMToCentimetersScaleFactor](
const FOSMFile& OSMFile,
UStreetMap& StreetMapRef,
const FOSMFile::FOSMWayInfo& OSMWay ) -> bool
{
if( OSMWay.WayType == FOSMFile::EOSMWayType::Park )
{
// Require at least three points so that we don't have degenerate polygon!
if( OSMWay.Nodes.Num() > 2 )
{
// Create a Park for this way
FStreetMapPark& NewPark = *new( StreetMapRef.Parks )FStreetMapPark();
FVector2D BoundsMin( TNumericLimits<float>::Max(), TNumericLimits<float>::Max() );
FVector2D BoundsMax( TNumericLimits<float>::Lowest(), TNumericLimits<float>::Lowest() );
NewPark.ParkPoints.AddUninitialized( OSMWay.Nodes.Num() );
int32 CurParkPoint = 0;
for( const FOSMFile::FOSMNodeInfo* OSMNodePtr : OSMWay.Nodes )
{
const FOSMFile::FOSMNodeInfo& OSMNode = *OSMNodePtr;
// Transform all points relative to the center of the latitude/longitude bounds, so that
// we get as much precision as possible.
const double RelativeToLatitude = OSMFile.AverageLatitude;
const double RelativeToLongitude = OSMFile.AverageLongitude;
const FVector2D NodePos = ConvertLatLongToMetersRelative(
OSMNode.Latitude,
OSMNode.Longitude,
RelativeToLatitude,
RelativeToLongitude ) * OSMToCentimetersScaleFactor;
// Update bounding box
{
if( NodePos.X < BoundsMin.X )
{
BoundsMin.X = NodePos.X;
}
if( NodePos.Y < BoundsMin.Y )
{
BoundsMin.Y = NodePos.Y;
}
if( NodePos.X > BoundsMax.X )
{
BoundsMax.X = NodePos.X;
}
if( NodePos.Y > BoundsMax.Y )
{
BoundsMax.Y = NodePos.Y;
}
}
// Fill in the points
NewPark.ParkPoints[ CurParkPoint++ ] = NodePos;
}
// Make sure the Park ended up with a closed polygon, then remove the final (redundant) point
const bool bIsClosed = NewPark.ParkPoints[ 0 ].Equals( NewPark.ParkPoints[ NewPark.ParkPoints.Num() - 1 ], KINDA_SMALL_NUMBER );
if( bIsClosed )
{
// Remove the final redundant point
NewPark.ParkPoints.Pop();
}
else
{
// Wasn't expecting to have an unclosed shape. Our tolerances might be off, or the data was malformed.
// Either way, it shouldn't be a problem as we'll close the shape ourselves below.
// @todo: Log this for the user as an import warning
}
NewPark.ParkName = OSMWay.Name;
if( NewPark.ParkName.IsEmpty() )
{
NewPark.ParkName = OSMWay.Ref;
}
NewPark.BoundsMin = BoundsMin;
NewPark.BoundsMax = BoundsMax;
StreetMapRef.BoundsMin.X = FMath::Min( StreetMapRef.BoundsMin.X, BoundsMin.X );
StreetMapRef.BoundsMin.Y = FMath::Min( StreetMapRef.BoundsMin.Y, BoundsMin.Y );
StreetMapRef.BoundsMax.X = FMath::Max( StreetMapRef.BoundsMax.X, BoundsMax.X );
StreetMapRef.BoundsMax.Y = FMath::Max( StreetMapRef.BoundsMax.Y, BoundsMax.Y );
return true;
}
else
{
// NOTE: Skipped adding Park for way because it has less than 3 points
// @todo: Log this for the user as an import warning
}
}
return false;
};
// Load up the OSM file. It's in XML format.
@ -349,6 +550,26 @@ bool UStreetMapFactory::LoadFromOpenStreetMapXMLFile( UStreetMap* StreetMap, FSt
// ...
}
}
else if( OSMWay->WayType == FOSMFile::EOSMWayType::Water )
{
if( AddWaterForWay( OSMFile, *StreetMap, *OSMWay ) )
{
// ...
}
}
else if( OSMWay->WayType == FOSMFile::EOSMWayType::Park )
{
if( AddParkForWay( OSMFile, *StreetMap, *OSMWay ) )
{
// ...
}
}
else
{
int32 RoadIndex = INDEX_NONE;

View file

@ -99,20 +99,32 @@ public:
/** Buildings border vertical offset */
UPROPERTY(Category = StreetMap, EditAnywhere, meta = (ClampMin = "0", UIMin = "0"))
float BuildingBorderZ;
/** Water vertex color */
UPROPERTY(Category = StreetMap, EditAnywhere)
FLinearColor WaterColor;
/** Park vertex color */
UPROPERTY(Category = StreetMap, EditAnywhere)
FLinearColor ParkColor;
FStreetMapMeshBuildSettings() :
RoadOffesetZ(0.0f),
bWant3DBuildings(true),
bWantLitBuildings(true),
StreetThickness(800.0f),
StreetColor(0.05f, 0.75f, 0.05f),
StreetColor(0.93f, 0.90f, 0.87f),
MajorRoadThickness(1000.0f),
MajorRoadColor(0.15f, 0.85f, 0.15f),
MajorRoadColor(0.92f, 0.91f, 0.88f),
HighwayThickness(1400.0f),
HighwayColor(FLinearColor(0.25f, 0.95f, 0.25f)),
HighwayColor(FLinearColor(0.91f, 0.90f, 0.87f)),
BuildingBorderThickness(20.0f),
BuildingBorderLinearColor(0.85f, 0.85f, 0.85f),
BuildingBorderZ(10.0f)
BuildingBorderZ(10.0f),
ParkColor(0.00f, 0.55f, 0.27f),
WaterColor(0.26f, 0.75f, 0.98f)
{
}
@ -312,6 +324,57 @@ struct STREETMAPRUNTIME_API FStreetMapBuilding
};
// 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;
};
/** A loaded street map */
UCLASS(Blueprintable, BlueprintType)
class STREETMAPRUNTIME_API UStreetMap : public UObject
@ -361,6 +424,32 @@ public:
{
return Buildings;
}
/** 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;
}
/** Gets the bounding box of the map */
FVector2D GetBoundsMin() const
@ -397,6 +486,14 @@ protected:
/** 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;
/** 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;
/** List of all buildings on the street map */
UPROPERTY( Category=StreetMap, EditAnywhere, BlueprintReadWrite)

View file

@ -232,6 +232,10 @@ void UStreetMapComponent::GenerateMesh()
const float BuildingBorderZ = MeshBuildSettings.BuildingBorderZ;
const FColor BuildingBorderColor( BuildingBorderLinearColor.ToFColor( false ) );
const FColor BuildingFillColor( FLinearColor( BuildingBorderLinearColor * 0.33f ).CopyWithNewOpacity( 1.0f ).ToFColor( false ) );
const FColor WaterColor = MeshBuildSettings.WaterColor.ToFColor( false );
const FColor ParkColor = MeshBuildSettings.ParkColor.ToFColor( false );
/////////////////////////////////////////////////////////
@ -247,6 +251,8 @@ void UStreetMapComponent::GenerateMesh()
const auto& Roads = StreetMap->GetRoads();
const auto& Nodes = StreetMap->GetNodes();
const auto& Buildings = StreetMap->GetBuildings();
const auto& Waters = StreetMap->GetWaters();
const auto& Parks = StreetMap->GetParks();
for( const auto& Road : Roads )
{
@ -430,6 +436,99 @@ void UStreetMapComponent::GenerateMesh()
}
}
}
// 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
}
CachedLocalBounds = MeshBoundingBox;
}