Port code for road generation

This commit is contained in:
nsubiron 2017-03-07 13:46:58 +00:00
parent a79a62a354
commit 778d5cda20
21 changed files with 1831 additions and 3 deletions

View File

@ -4,6 +4,8 @@
#define LOCTEXT_NAMESPACE "FCarlaModule" #define LOCTEXT_NAMESPACE "FCarlaModule"
DEFINE_LOG_CATEGORY(LogCarla);
void FCarlaModule::StartupModule() void FCarlaModule::StartupModule()
{ {
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
@ -16,5 +18,5 @@ void FCarlaModule::ShutdownModule()
} }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FCarlaModule, Carla) IMPLEMENT_MODULE(FCarlaModule, Carla)

View File

@ -1,9 +1,15 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
// This file is included before any other file in every compile unit within the
// plugin.
#pragma once #pragma once
#include "ModuleManager.h" #include "ModuleManager.h"
#include "../Common/NonCopyable.h"
DECLARE_LOG_CATEGORY_EXTERN(LogCarla, Log, All);
class FCarlaModule : public IModuleInterface class FCarlaModule : public IModuleInterface
{ {
public: public:
@ -11,4 +17,4 @@ public:
/** IModuleInterface implementation */ /** IModuleInterface implementation */
virtual void StartupModule() override; virtual void StartupModule() override;
virtual void ShutdownModule() override; virtual void ShutdownModule() override;
}; };

View File

@ -0,0 +1,169 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "CityMapGenerator.h"
#include "MapGen/GraphGenerator.h"
#include <algorithm>
#ifdef WITH_EDITOR
#include <sstream>
#endif
// =============================================================================
// -- Constructor and destructor -----------------------------------------------
// =============================================================================
ACityMapGenerator::ACityMapGenerator(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
UpdateMap();
}
ACityMapGenerator::~ACityMapGenerator() {}
// =============================================================================
// -- Private methods ----------------------------------------------------------
// =============================================================================
#if WITH_EDITOR
void ACityMapGenerator::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property) {
UpdateMap();
}
}
#endif
void ACityMapGenerator::UpdateMap() {
UpdateSeeds();
GenerateGraph();
if (bGenerateRoads) {
GenerateRoads();
}
}
void ACityMapGenerator::UpdateSeeds() {
if (!bUseFixedSeed) {
bUseMultipleFixedSeeds = false;
FRandomStream randomStream;
randomStream.GenerateNewSeed();
Seed = randomStream.GetCurrentSeed();
}
if (!bUseMultipleFixedSeeds) {
RoadPlanningSeed = Seed;
BuildingGenerationSeed = Seed;
}
}
void ACityMapGenerator::GenerateGraph() {
if ((MapSizeX < 5u) || (MapSizeY < 5u)) {
MapSizeX = 5u;
MapSizeY = 5u;
UE_LOG(LogCarla, Warning, TEXT("Map size changed, was too small"));
}
#ifdef WITH_EDITOR
// Delete the dcel before the new one is created so indices are restored.
Dcel.Reset(nullptr);
#endif // WITH_EDITOR
Dcel = MapGen::GraphGenerator::Generate(MapSizeX, MapSizeY, RoadPlanningSeed);
UE_LOG(LogCarla, Log,
TEXT("Generated DCEL with: { %d vertices, %d half-edges, %d faces }"),
Dcel->CountNodes(),
Dcel->CountHalfEdges(),
Dcel->CountFaces());
DcelParser = MakeUnique<MapGen::GraphParser>(*Dcel);
#ifdef WITH_EDITOR
{ // print the results of the parser.
std::wstringstream sout;
sout << "\nGenerated " << DcelParser->CityAreaCount() << " city areas: ";
for (auto i = 0u; i < DcelParser->CityAreaCount(); ++i) {
sout << "{ ";
auto &cityArea = DcelParser->GetCityAreaAt(i);
for (size_t j = 0u; j < cityArea.NodeCount(); ++j) {
sout << cityArea.GetNodeAt(j) << " ";
}
sout << "} ";
}
sout << "\nGenerated " << DcelParser->RoadSegmentCount() << " road segments: ";
for (auto i = 0u; i < DcelParser->RoadSegmentCount(); ++i) {
sout << "{ ";
auto &roadSegment = DcelParser->GetRoadSegmentAt(i);
for (size_t j = 0u; j < roadSegment.Size(); ++j) {
sout << roadSegment[j] << " ";
}
sout << "} ";
}
UE_LOG(LogCarla, Log, TEXT("\n%s"), sout.str().c_str());
}
#endif // WITH_EDITOR
}
void ACityMapGenerator::GenerateRoads() {
constexpr auto basicRoadTag = ECityMapMeshTag::RoadTwoLanes;
constexpr auto basicIntersectionTag = ECityMapMeshTag::RoadXIntersection;
// Rotation for vertical roads.
const FQuat rotation(FVector(0.0f, 0.0f, 1.0f), HALF_PI);
check(Dcel != nullptr);
using Graph = MapGen::DoublyConnectedEdgeList;
const Graph &graph = *Dcel;
const uint32 margin = CityMapMeshTag::GetRoadIntersectionSize() / 2u;
// For each edge add road segment.
for (auto &edge : graph.GetHalfEdges()) {
auto source = Graph::GetSource(edge).GetPosition();
auto target = Graph::GetTarget(edge).GetPosition();
if (source.x == target.x) {
// vertical
auto y = 1u + margin + std::min(source.y, target.y);
auto end = std::max(source.y, target.y) - margin;
for (; y < end; ++y) {
AddInstance(basicRoadTag, source.x, y, HALF_PI);
}
} else if (source.y == target.y) {
// horizontal
auto x = 1u + margin + std::min(source.x, target.x);
auto end = std::max(source.x, target.x) - margin;
for (; x < end; ++x) {
AddInstance(basicRoadTag, x, source.y);
}
} else {
UE_LOG(LogCarla, Warning, TEXT("Diagonal edge ignored"));
}
}
// For each node add the intersection.
for (auto &node : graph.GetNodes()) {
const auto coords = node.GetPosition();
ECityMapMeshTag tag = basicIntersectionTag;
switch (node.IntersectionType) {
case MapGen::EIntersectionType::Turn90Deg:
tag = ECityMapMeshTag::Road90DegTurn;
break;
case MapGen::EIntersectionType::TIntersection:
tag = ECityMapMeshTag::RoadTIntersection;
break;
case MapGen::EIntersectionType::XIntersection:
tag = ECityMapMeshTag::RoadXIntersection;
break;
default:
UE_LOG(LogCarla, Warning, TEXT("Intersection type not implemented"));
}
FString tagStr = CityMapMeshTag::ToString(tag);
std::wstringstream sout;
for (float a : node.Rots)
sout << a << " ";
UE_LOG(
LogCarla,
Log,
TEXT("Add instance \"%s\" at {%d, %d} with rotation %f, { %s }"),
*tagStr, coords.x, coords.y, node.Rotation, sout.str().c_str());
AddInstance(tag, coords.x, coords.y, node.Rotation);
}
}

View File

@ -0,0 +1,76 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "MapGen/CityMapMeshHolder.h"
#include "MapGen/DoublyConnectedEdgeList.h"
#include "MapGen/GraphParser.h"
#include "CityMapGenerator.generated.h"
UCLASS(HideCategories=(Rendering, Input))
class CARLA_API ACityMapGenerator : public ACityMapMeshHolder
{
GENERATED_BODY()
public:
ACityMapGenerator(const FObjectInitializer& ObjectInitializer);
~ACityMapGenerator();
private:
#if WITH_EDITOR
/// Called after a property change in editor.
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
/// Update the map based on the current settings.
void UpdateMap();
/// Update the random seeds. Generate random if no fixed seed is used.
void UpdateSeeds();
/// Regenerate the DCEL.
void GenerateGraph();
/// Add the road meshes to the scene based on the current DCEL.
void GenerateRoads();
/// @name Map Generation
/// @{
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (ClampMin = "10", ClampMax = "200"))
uint32 MapSizeX = 20u;
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (ClampMin = "10", ClampMax = "200"))
uint32 MapSizeY = 20u;
UPROPERTY(Category = "Map Generation", EditAnywhere)
bool bGenerateRoads = true;
UPROPERTY(Category = "Map Generation", EditAnywhere)
bool bUseFixedSeed = true;
UPROPERTY(Category = "Map Generation", EditAnywhere, meta = (EditCondition = bUseFixedSeed))
int32 Seed = 123456789;
/// @}
/// @name Map Generation - Advance Display
/// @{
UPROPERTY(Category = "Map Generation", EditAnywhere, AdvancedDisplay, meta = (EditCondition = bUseFixedSeed))
bool bUseMultipleFixedSeeds = false;
UPROPERTY(Category = "Map Generation", EditAnywhere, AdvancedDisplay, meta = (EditCondition = bUseMultipleFixedSeeds))
int32 RoadPlanningSeed;
UPROPERTY(Category = "Map Generation", EditAnywhere, AdvancedDisplay, meta = (EditCondition = bUseMultipleFixedSeeds))
int32 BuildingGenerationSeed;
/// @}
/// @name Other private members
/// @{
TUniquePtr<MapGen::DoublyConnectedEdgeList> Dcel;
TUniquePtr<MapGen::GraphParser> DcelParser;
/// @}
};

View File

@ -0,0 +1,40 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GraphTypes.h"
#include <vector>
namespace MapGen {
class CARLA_API CityAreaDescription : private NonCopyable
{
public:
explicit CityAreaDescription(const GraphFace &Face) : _face(&Face) {}
void Add(const GraphNode &Node) {
_nodes.emplace_back(&Node);
}
const GraphFace &GetFace() const {
return *_face;
}
const GraphNode &GetNodeAt(size_t i) const {
return *_nodes[i];
}
size_t NodeCount() const {
return _nodes.size();
}
private:
const GraphFace *_face;
std::vector<const GraphNode *> _nodes;
};
} // namespace MapGen

View File

@ -0,0 +1,13 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
namespace MapGen {
enum class EIntersectionType {
Turn90Deg,
TIntersection,
XIntersection
};
} // namespace MapGen

View File

@ -0,0 +1,127 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "CityMapMeshHolder.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include <vector>
using tag_size_t = std::underlying_type<ECityMapMeshTag>::type;
constexpr static tag_size_t NUMBER_OF_TAGS = CityMapMeshTag::GetNumberOfTags();
// =============================================================================
// -- Constructor --------------------------------------------------------------
// =============================================================================
ACityMapMeshHolder::ACityMapMeshHolder(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
SceneRootComponent =
ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneComponent"));
SceneRootComponent->SetMobility(EComponentMobility::Static);
RootComponent = SceneRootComponent;
for (tag_size_t i = 0u; i < NUMBER_OF_TAGS; ++i) {
// Add static mesh holder.
StaticMeshes.Add(CityMapMeshTag::FromUInt(i));
// Create an instantiator for each mesh.
const FString name = CityMapMeshTag::ToString(i) + "Instantiator";
auto instantiator = CreateDefaultSubobject<UInstancedStaticMeshComponent>(*name);
instantiator->SetMobility(EComponentMobility::Static);
instantiator->SetupAttachment(SceneRootComponent);
MeshInstatiators.Add(instantiator);
instantiator->RegisterComponent();
}
}
// =============================================================================
// -- Public methods -----------------------------------------------------------
// =============================================================================
FVector ACityMapMeshHolder::GetTileLocation(uint32 X, uint32 Y) const
{
return {X * MapScale, Y * MapScale, 0.0f};
}
// =============================================================================
// -- Protected methods --------------------------------------------------------
// =============================================================================
#if WITH_EDITOR
void ACityMapMeshHolder::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property) {
ResetInstantiators();
UpdateMapScale();
}
}
#endif
const UStaticMesh *ACityMapMeshHolder::GetStaticMesh(ECityMapMeshTag Tag) const
{
return StaticMeshes[Tag];
}
void ACityMapMeshHolder::AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y)
{
AddInstance(Tag, FTransform(GetTileLocation(X, Y)));
}
void ACityMapMeshHolder::AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y, float Angle)
{
const FQuat rotation(FVector(0.0f, 0.0f, 1.0f), Angle);
const FVector location = GetTileLocation(X, Y);
AddInstance(Tag, FTransform(rotation, location));
}
void ACityMapMeshHolder::AddInstance(ECityMapMeshTag Tag, FTransform Transform)
{
auto instantiator = MeshInstatiators[CityMapMeshTag::ToUInt(Tag)];
check(instantiator != nullptr);
instantiator->AddInstance(Transform);
}
// =============================================================================
// -- Private methods ----------------------------------------------------------
// =============================================================================
void ACityMapMeshHolder::ResetInstantiators()
{
for (tag_size_t i = 0u; i < NUMBER_OF_TAGS; ++i) {
UInstancedStaticMeshComponent *instantiator = MeshInstatiators[i];
check(instantiator != nullptr);
instantiator->ClearInstances();
instantiator->SetStaticMesh(StaticMeshes[CityMapMeshTag::FromUInt(i)]);
}
}
void ACityMapMeshHolder::UpdateMapScale()
{
auto Tag = CityMapMeshTag::GetBaseMeshTag();
auto *mesh = GetStaticMesh(Tag);
if (mesh == nullptr) {
UE_LOG(
LogCarla,
Error,
TEXT("Cannot find mesh \"%s\" for computing tile size"),
*CityMapMeshTag::ToString(Tag));
MapScale = 1.0f;
} else {
FVector size = mesh->GetBoundingBox().GetSize();
if (size.X != size.Y) {
UE_LOG(
LogCarla,
Warning,
TEXT("Base mesh \"%s\" is not squared"),
*CityMapMeshTag::ToString(Tag));
}
MapScale = size.X;
}
}

View File

@ -0,0 +1,73 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GameFramework/Actor.h"
#include "CityMapMeshTag.h"
#include "CityMapMeshHolder.generated.h"
class UInstancedStaticMeshComponent;
/// Holds the static meshes and instances necessary for building the city map.
UCLASS(Abstract)
class CARLA_API ACityMapMeshHolder : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACityMapMeshHolder(const FObjectInitializer& ObjectInitializer);
protected:
#if WITH_EDITOR
/// Called after a property change in editor.
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
/// Return the 3D world location (relative to this actor) of the given 2D
/// tile.
FVector GetTileLocation(uint32 X, uint32 Y) const;
/// Return the static mesh corresponding to @a Tag.
const UStaticMesh *GetStaticMesh(ECityMapMeshTag Tag) const;
/// Add an instance of a mesh with a given tile location.
/// @param Tag The mesh' tag
/// @param X Tile coordinate X
/// @param Y Tile coordinate Y
void AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y);
/// Add an instance of a mesh with a given tile location and rotation.
/// @param Tag The mesh' tag
/// @param X Tile coordinate X
/// @param Y Tile coordinate Y
/// @param Angle Rotation around Z axis
void AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y, float Angle);
/// Add an instance of a mesh with a given transform.
/// @param Tag The mesh' tag
/// @param Transform Transform that will be applied to the mesh
void AddInstance(ECityMapMeshTag Tag, FTransform Transform);
private:
/// Clear all instances in the instantiators and update the static meshes.
void ResetInstantiators();
/// Set the scale to the dimensions of the base mesh.
void UpdateMapScale();
UPROPERTY()
USceneComponent *SceneRootComponent;
UPROPERTY(Category = "Meshes", EditAnywhere)
TMap<ECityMapMeshTag, UStaticMesh *> StaticMeshes;
UPROPERTY(Category = "Meshes|Debug", VisibleAnywhere)
float MapScale;
UPROPERTY(Category = "Meshes|Debug", VisibleAnywhere)
TArray<UInstancedStaticMeshComponent *> MeshInstatiators;
};

View File

@ -0,0 +1,26 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "CityMapMeshTag.h"
#include "Package.h"
#include <array>
ECityMapMeshTag CityMapMeshTag::GetBaseMeshTag()
{
return ECityMapMeshTag::RoadTwoLanes;
}
uint32 CityMapMeshTag::GetRoadIntersectionSize()
{
return 5u;
}
FString CityMapMeshTag::ToString(ECityMapMeshTag Tag)
{
const UEnum* ptr = FindObject<UEnum>(ANY_PACKAGE, TEXT("ECityMapMeshTag"), true);
if(!ptr)
return FString("Invalid");
return ptr->GetEnumName(static_cast<int32>(Tag));
}

View File

@ -0,0 +1,61 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
/// Tag to identify the meshes used by the ProceduralMapGenerator.
///
/// It will work as long as we have less than 255 meshes, currently blueprint
/// type enums support uint8 only.
UENUM(BlueprintType)
enum class ECityMapMeshTag : uint8
{
RoadTwoLanes UMETA(DisplayName = "Road: Two Lanes"),
RoadFourLanes UMETA(DisplayName = "Road: Four Lanes"),
Road90DegTurn UMETA(DisplayName = "Road: 90 Degree Turn"),
RoadTIntersection UMETA(DisplayName = "Road: T-Intersection"),
RoadXIntersection UMETA(DisplayName = "Road: X-Intersection"),
NUMBER_OF_TAGS UMETA(Hidden)
};
/// Helper class for working with ECityMapMeshTag.
class CARLA_API CityMapMeshTag
{
public:
/// Return the number of tags.
static constexpr uint8 GetNumberOfTags() {
return ToUInt(ECityMapMeshTag::NUMBER_OF_TAGS);
}
/// Return the base mesh. The base mesh defines the unit tile for map scaling.
static ECityMapMeshTag GetBaseMeshTag();
/// Get the size in tiles of a road intersection side. I.e., return N such NxN
/// is the size of a road intersection piece.
static uint32 GetRoadIntersectionSize();
/// @name Tag conversions
/// @{
/// Convert @a Tag to an unsigned integer type.
static constexpr uint8 ToUInt(ECityMapMeshTag Tag) {
return static_cast<uint8>(Tag);
}
/// Convert an unsigned integer to a ECityMapMeshTag.
static ECityMapMeshTag FromUInt(uint8 Value) {
check(Value < GetNumberOfTags());
return static_cast<ECityMapMeshTag>(Value);
}
/// Get @a Tag name as FString.
static FString ToString(ECityMapMeshTag Tag);
/// Convert @a Value to ECityMapMeshTag and get its name as FString.
static FString ToString(uint8 Value) {
return ToString(FromUInt(Value));
}
/// @}
};

View File

@ -0,0 +1,315 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "DoublyConnectedEdgeList.h"
#include <map>
#ifdef WITH_EDITOR
#include <sstream>
#endif // WITH_EDITOR
namespace MapGen {
// ===========================================================================
// -- Local static methods ---------------------------------------------------
// ===========================================================================
#ifdef WITH_EDITOR
static void ResetIndices() {
GraphNode::ResetIndex();
GraphHalfEdge::ResetIndex();
GraphFace::ResetIndex();
}
#endif // WITH_EDITOR
/// Return the pair {prev, next}, where prev/next is the previous/next edge
/// counterclockwise around edge's source node. I.e., edge's position is in
/// between prev and next.
///
/// Note: Always returns the half-edge pointing out from node.
///
/// The time complexity is O(n*log(n)) where n is the number of edges of
/// edge's source.
static std::pair<DoublyConnectedEdgeList::HalfEdge *, DoublyConnectedEdgeList::HalfEdge *>
FindPositionInNode(DoublyConnectedEdgeList::HalfEdge &halfEdge)
{
using Dcel = DoublyConnectedEdgeList;
// from [-pi, pi] to [0, 1].
auto normalize = [](auto a) {
constexpr decltype(a) twoPi = 2.0 * 3.14159265359;
a /= twoPi;
while (a >= 1.0) a -= 1.0;
while (a < 0.0) a += 1.0;
return a;
};
auto angle = Dcel::GetAngle(halfEdge);
std::map<decltype(angle), Dcel::HalfEdge *> edgeMap;
// Iterate every half-edge in the source node.
auto &firstHalfEdge = Dcel::GetLeavingHalfEdge(Dcel::GetSource(halfEdge));
auto *edge = &firstHalfEdge;
do {
if (edge != &halfEdge) {
auto alpha = DoublyConnectedEdgeList::GetAngle(*edge);
auto a = normalize(alpha - angle);
edgeMap.insert({a, edge});
}
edge = &Dcel::GetNextInNode(*edge);
} while (edge != &firstHalfEdge);
check(!edgeMap.empty());
return {edgeMap.rbegin()->second, edgeMap.begin()->second};
}
// ===========================================================================
// -- Constructors and destructor --------------------------------------------
// ===========================================================================
DoublyConnectedEdgeList::DoublyConnectedEdgeList(
const Position &Position0,
const Position &Position1) :
Nodes(),
HalfEdges(2u),
Faces(1u)
{
Nodes.emplace_back(Position0);
Nodes.emplace_back(Position1);
Faces.front().HalfEdge = &HalfEdges.front();
HalfEdges.front().Source = &Nodes.front();
HalfEdges.front().Target = &Nodes.back();
HalfEdges.front().Next = &HalfEdges.back();
HalfEdges.front().Pair = &HalfEdges.back();
HalfEdges.front().Face = &Faces.front();
HalfEdges.back().Source = &Nodes.back();
HalfEdges.back().Target = &Nodes.front();
HalfEdges.back().Next = &HalfEdges.front();
HalfEdges.back().Pair = &HalfEdges.front();
HalfEdges.back().Face = &Faces.front();
Nodes.front().LeavingHalfEdge = &HalfEdges.front();
Nodes.back().LeavingHalfEdge = &HalfEdges.back();
}
DoublyConnectedEdgeList::~DoublyConnectedEdgeList()
{
#ifdef WITH_EDITOR
ResetIndices();
#endif // WITH_EDITOR
}
// ===========================================================================
// -- Adding elements to the graph -------------------------------------------
// ===========================================================================
DoublyConnectedEdgeList::Node &DoublyConnectedEdgeList::AddNode(
const Position &NodePosition,
Node &OtherNode)
{
Nodes.emplace_back(NodePosition);
auto &newNode = Nodes.back();
HalfEdges.emplace_back();
auto &edge0 = HalfEdges.back();
HalfEdges.emplace_back();
auto &edge1 = HalfEdges.back();
edge0.Source = &newNode;
edge0.Target = &OtherNode;
edge0.Pair = &edge1;
edge1.Source = &OtherNode;
edge1.Target = &newNode;
edge1.Pair = &edge0;
HalfEdge *prev;
HalfEdge *next;
std::tie(prev, next) = FindPositionInNode(edge1);
edge0.Next = next;
edge0.Face = next->Face;
edge1.Next = &edge0;
edge1.Face = next->Face;
prev->Pair->Next = &edge1;
newNode.LeavingHalfEdge = &edge0;
return newNode;
}
DoublyConnectedEdgeList::Node &DoublyConnectedEdgeList::SplitEdge(
const Position &Position,
HalfEdge &edge)
{
HalfEdges.emplace_back();
auto &edge0 = HalfEdges.back();
HalfEdges.emplace_back();
auto &edge1 = HalfEdges.back();
Nodes.emplace_back(Position);
auto &newNode = Nodes.back();
auto &node0 = *edge.Source;
// Opposite direction of edge.
edge0.Source = &newNode;
edge0.Target = &node0;
edge0.Pair = &edge1;
edge0.Face = edge.Pair->Face;
// Same direction as edge.
edge1.Source = &node0;
edge1.Target = &newNode;
edge1.Pair = &edge0;
edge1.Next = &edge;
edge1.Face = edge.Face;
// Fix connections to node0.
HalfEdge *prev;
HalfEdge *next;
std::tie(prev, next) = FindPositionInNode(edge);
edge0.Next = next;
prev->Pair->Next = &edge1;
// Fix the pair that was split.
edge.Source = &newNode;
edge.Pair->Target = &newNode;
edge.Pair->Next = &edge0;
// Fix the node's edges.
node0.LeavingHalfEdge = &edge1;
newNode.LeavingHalfEdge = &edge0;
return newNode;
}
DoublyConnectedEdgeList::Face &DoublyConnectedEdgeList::ConnectNodes(
Node &Node0,
Node &Node1)
{
Faces.emplace_back();
auto &newFace = Faces.back();
HalfEdges.emplace_back();
auto &edge0 = HalfEdges.back();
HalfEdges.emplace_back();
auto &edge1 = HalfEdges.back();
edge0.Source = &Node0;
edge0.Target = &Node1;
edge0.Pair = &edge1;
edge1.Source = &Node1;
edge1.Target = &Node0;
edge1.Pair = &edge0;
// Connect edges to node0.
HalfEdge *prev0;
HalfEdge *next0;
std::tie(prev0, next0) = FindPositionInNode(edge0);
edge1.Next = next0;
prev0->Pair->Next = &edge0;
// Connect edges to node1.
HalfEdge *prev1;
HalfEdge *next1;
std::tie(prev1, next1) = FindPositionInNode(edge1);
edge0.Next = next1;
prev1->Pair->Next = &edge1;
// Attach faces to the newly created edges.
auto &oldFace = *next1->Face;
oldFace.HalfEdge = &edge0;
newFace.HalfEdge = &edge1;
// Iterate over the edges of each face and correct pointers.
auto fixFace = [](Face &face) {
auto &firstEdge = GetHalfEdge(face);
auto *edge = &firstEdge;
do {
edge->Face = &face;
edge = &GetNextInFace(*edge);
} while (edge != &firstEdge);
};
fixFace(oldFace);
fixFace(newFace);
return newFace;
}
// ===========================================================================
// -- Other member functions -------------------------------------------------
// ===========================================================================
float DoublyConnectedEdgeList::GetAngle(const HalfEdge &halfEdge) {
auto src = GetSource(halfEdge).GetPosition();
auto trg = GetTarget(halfEdge).GetPosition();
auto dir = trg - src; // @todo normalize?
return std::atan2(static_cast<double>(dir.y), static_cast<double>(dir.x));
}
#ifdef WITH_EDITOR
void DoublyConnectedEdgeList::PrintToLog() const
{
std::wstringstream sout;
{
sout << "iterate all nodes: ";
for (auto &node : GetNodes()) {
auto p = node.GetPosition();
sout << node << "{" << p.x << "," << p.y << "} ";
}
sout << "\n";
}
{
sout << "iterate all faces: ";
for (auto &face : GetFaces()) {
sout << face << " ";
}
sout << "\n";
}
{
sout << "iterate all edges: ";
for (auto &edge : GetHalfEdges()) {
auto &src = GetSource(edge);
auto &trg = GetTarget(edge);
auto &face = GetFace(edge);
sout << edge << "{" << src << "->" << trg << "," << face << "} ";
}
sout << "\n";
}
{
sout << "iterate nodes in face: ";
for (auto &face : GetFaces()) {
sout << face << "{ ";
auto &firstEdge = GetHalfEdge(face);
const auto *edge = &firstEdge;
do {
sout << GetSource(*edge) << " ";
edge = &GetNextInFace(*edge);
} while (edge != &firstEdge);
sout << "} ";
}
sout << "\n";
}
{
sout << "iterate edges in node: ";
for (auto &node : GetNodes()) {
sout << node << "{ ";
auto &firstEdge = GetLeavingHalfEdge(node);
const auto *edge = &firstEdge;
do {
sout << GetTarget(*edge) << " ";
edge = &GetNextInNode(*edge);
} while (edge != &firstEdge);
sout << "} ";
}
sout << "\n";
}
UE_LOG(LogCarla, Log, TEXT("\n%s"), sout.str().c_str());
}
#endif // WITH_EDITOR
} // namespace MapGen

View File

@ -0,0 +1,332 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GraphTypes.h"
#include "ListView.h"
#include "Position.h"
#include <array>
#include <list>
namespace MapGen {
/// Simple doubly-connected edge list structure. It only allows adding
/// elements, not removing them.
class CARLA_API DoublyConnectedEdgeList : private NonCopyable
{
// =========================================================================
// -- DCEL types -----------------------------------------------------------
// =========================================================================
public:
using Position = MapGen::Position<int32>;
struct Node;
struct HalfEdge;
struct Face;
struct Node : public GraphNode
{
friend DoublyConnectedEdgeList;
Node(const Position &Pos) : Position(Pos) {}
Node &operator=(const Node &) = delete;
const DoublyConnectedEdgeList::Position &GetPosition() const
{
return Position;
}
private:
DoublyConnectedEdgeList::Position Position;
HalfEdge *LeavingHalfEdge = nullptr;
};
struct HalfEdge : public GraphHalfEdge
{
friend DoublyConnectedEdgeList;
HalfEdge &operator=(const HalfEdge &) = delete;
private:
Node *Source = nullptr;
Node *Target = nullptr;
HalfEdge *Next = nullptr;
HalfEdge *Pair = nullptr;
Face *Face = nullptr;
};
struct Face : public GraphFace
{
friend DoublyConnectedEdgeList;
Face &operator=(const Face &) = delete;
private:
HalfEdge *HalfEdge = nullptr;
};
using NodeContainer = std::list<Node>;
using NodeIterator = typename NodeContainer::iterator;
using ConstNodeIterator = typename NodeContainer::const_iterator;
using HalfEdgeContainer = std::list<HalfEdge>;
using HalfEdgeIterator = typename HalfEdgeContainer::iterator;
using ConstHalfEdgeIterator = typename HalfEdgeContainer::const_iterator;
using FaceContainer = std::list<Face>;
using FaceIterator = typename FaceContainer::iterator;
using ConstFaceIterator = typename FaceContainer::const_iterator;
// =========================================================================
// -- Constructors and destructor ------------------------------------------
// =========================================================================
public:
/// Create a DoublyConnectedEdgeList with two nodes, two edges and one face.
explicit DoublyConnectedEdgeList(const Position &Position0, const Position &Position1);
/// Create a DoublyConnectedEdgeList consisting of a cycle of N nodes.
template <size_t N>
explicit DoublyConnectedEdgeList(const std::array<Position, N> &Cycle)
: DoublyConnectedEdgeList(Cycle[0u], Cycle[1u])
{
static_assert(N > 2u, "Not enough nodes to make a cycle!");
for (auto i = 2u; i < Cycle.size(); ++i) {
AddNode(Cycle[i], Nodes.back());
}
ConnectNodes(Nodes.front(), Nodes.back());
}
~DoublyConnectedEdgeList();
// =========================================================================
/// @name Adding elements to the graph -------------------------------------
// =========================================================================
/// {
public:
/// Add a node at @a NodePosition and attach it to @a OtherNode.
///
/// The time complexity is O(n*log(n)) where n is the number of edges
/// leaving @a OtherNode.
///
/// @return The newly generated node.
Node &AddNode(const Position &NodePosition, Node &OtherNode);
/// Split @a HalfEdge (and its pair) at @a Position.
///
/// The time complexity is O(n*log(n)) where n is the number of edges
/// leaving @a HalfEdge's source.
///
/// @return The newly generated node.
Node &SplitEdge(const Position &Position, HalfEdge &HalfEdge);
/// Connect two nodes by a pair of edges.
///
/// It is assumed that both nodes are connected by the same face.
///
/// The time complexity is O(n0*log(n0) + n1*log(n1) + nf) where n0 and n1
/// are the number of edges leaving @a Node0 and @a Node1 respectively, and
/// nf is the number of edges in the face containing both nodes.
///
/// @return The newly generated face.
Face &ConnectNodes(Node &Node0, Node &Node1);
/// @}
// =========================================================================
/// @name Counting graph elements ------------------------------------------
// =========================================================================
/// @{
public:
size_t CountNodes() const
{
return Nodes.size();
}
size_t CountHalfEdges() const
{
return HalfEdges.size();
}
size_t CountFaces() const
{
return Faces.size();
}
/// @}
// =========================================================================
/// @name Accessing graph elements -----------------------------------------
// =========================================================================
/// @{
public:
ListView<NodeIterator> GetNodes()
{
return ListView<NodeIterator>(Nodes);
}
ListView<ConstNodeIterator> GetNodes() const
{
return ListView<ConstNodeIterator>(Nodes);
}
ListView<HalfEdgeIterator> GetHalfEdges()
{
return ListView<HalfEdgeIterator>(HalfEdges);
}
ListView<ConstHalfEdgeIterator> GetHalfEdges() const
{
return ListView<ConstHalfEdgeIterator>(HalfEdges);
}
ListView<FaceIterator> GetFaces()
{
return ListView<FaceIterator>(Faces);
}
ListView<ConstFaceIterator> GetFaces() const
{
return ListView<ConstFaceIterator>(Faces);
}
/// @}
// =========================================================================
/// @name Accessing graph pointers -----------------------------------------
// =========================================================================
/// @{
public:
// -- Primary pointers -----------------------------------------------------
static Node &GetSource(HalfEdge &halfEdge)
{
check(halfEdge.Source != nullptr);
return *halfEdge.Source;
}
static const Node &GetSource(const HalfEdge &halfEdge)
{
check(halfEdge.Source != nullptr);
return *halfEdge.Source;
}
static Node &GetTarget(HalfEdge &halfEdge)
{
check(halfEdge.Target != nullptr);
return *halfEdge.Target;
}
static const Node &GetTarget(const HalfEdge &halfEdge)
{
check(halfEdge.Target != nullptr);
return *halfEdge.Target;
}
static HalfEdge &GetPair(HalfEdge &halfEdge)
{
check(halfEdge.Pair != nullptr);
return *halfEdge.Pair;
}
static const HalfEdge &GetPair(const HalfEdge &halfEdge)
{
check(halfEdge.Pair != nullptr);
return *halfEdge.Pair;
}
static Face &GetFace(HalfEdge &halfEdge)
{
check(halfEdge.Face != nullptr);
return *halfEdge.Face;
}
static const Face &GetFace(const HalfEdge &halfEdge)
{
check(halfEdge.Face != nullptr);
return *halfEdge.Face;
}
static HalfEdge &GetLeavingHalfEdge(Node &node)
{
check(node.LeavingHalfEdge != nullptr);
return *node.LeavingHalfEdge;
}
static const HalfEdge &GetLeavingHalfEdge(const Node &node)
{
check(node.LeavingHalfEdge != nullptr);
return *node.LeavingHalfEdge;
}
static HalfEdge &GetHalfEdge(Face &face)
{
check(face.HalfEdge != nullptr);
return *face.HalfEdge;
}
static const HalfEdge &GetHalfEdge(const Face &face)
{
check(face.HalfEdge != nullptr);
return *face.HalfEdge;
}
// -- Secondary pointers ---------------------------------------------------
static HalfEdge &GetNextInFace(HalfEdge &halfEdge)
{
check(halfEdge.Next != nullptr);
return *halfEdge.Next;
}
static const HalfEdge &GetNextInFace(const HalfEdge &halfEdge)
{
check(halfEdge.Next != nullptr);
return *halfEdge.Next;
}
static HalfEdge &GetNextInNode(HalfEdge &halfEdge)
{
return GetNextInFace(GetPair(halfEdge));
}
static const HalfEdge &GetNextInNode(const HalfEdge &halfEdge)
{
return GetNextInFace(GetPair(halfEdge));
}
/// @}
// =========================================================================
/// @name Other member functions -------------------------------------------
// =========================================================================
/// @{
public:
/// Return the angle [-pi, pi] of the half-edge.
static float GetAngle(const HalfEdge &halfEdge);
#ifdef WITH_EDITOR
void PrintToLog() const;
#endif
/// @}
// =========================================================================
// -- Private members ------------------------------------------------------
// =========================================================================
private:
NodeContainer Nodes;
HalfEdgeContainer HalfEdges;
FaceContainer Faces;
};
} // namespace MapGen

View File

@ -0,0 +1,109 @@
//// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
//
#include "Carla.h"
#include "GraphGenerator.h"
#include <vector>
namespace MapGen {
using Graph = DoublyConnectedEdgeList;
constexpr static int32 MARGIN = 6;
// ===========================================================================
// -- Static local methods ---------------------------------------------------
// ===========================================================================
static int32 signOf(int32 val) {
return (0 < val) - (val < 0);
}
static const Graph::Position &getSourcePosition(const Graph::HalfEdge &edge) {
return Graph::GetSource(edge).GetPosition();
}
static const Graph::Position &getTargetPosition(const Graph::HalfEdge &edge) {
return Graph::GetTarget(edge).GetPosition();
}
static Graph::Position getDirection(const Graph::HalfEdge &edge) {
return getTargetPosition(edge) - getSourcePosition(edge);
}
static std::pair<Graph::HalfEdge *, Graph::HalfEdge *> getRandomOpposingEdges(
Graph::Face &face,
FRandomStream &random) {
// Get all the edges in the face.
std::vector<Graph::HalfEdge *> edges;
edges.reserve(4u);
auto &firstEdge = Graph::GetHalfEdge(face);
auto *edge = &firstEdge;
do {
edges.emplace_back(edge);
edge = &Graph::GetNextInFace(*edge);
} while (edge != &firstEdge);
check(edges.size() == 4u);
auto randomIndex = random.RandRange(0, edges.size() - 1);
return {edges[randomIndex], edges[(randomIndex + 2u) % edges.size()]};
}
static Graph::Face *splitFace(Graph &graph, Graph::Face &face, FRandomStream &random) {
auto edgePair = getRandomOpposingEdges(face, random);
auto dir = getDirection(*edgePair.first);
// Assumes both edges are opposing faces on a rectangle.
auto otherDir = getDirection(*edgePair.second);
check((dir.x == -1 * otherDir.x) && (dir.y == -1 * otherDir.y));
// If the rectangle is not big enough do not split it.
if ((std::abs(dir.x) < 2*MARGIN+1) && (std::abs(dir.y) < 2*MARGIN+1))
return nullptr;
// Get a random point along the edges.
auto randX = (dir.x != 0 ? signOf(dir.x) * random.RandRange(MARGIN, std::abs(dir.x) - MARGIN) : 0);
auto randY = (dir.y != 0 ? signOf(dir.y) * random.RandRange(MARGIN, std::abs(dir.y) - MARGIN) : 0);
auto position0 = getSourcePosition(*edgePair.first) + Graph::Position{randX, randY};
auto position1 = getTargetPosition(*edgePair.second) + Graph::Position{randX, randY};
// Split the edges and connect.
Graph::Node &node0 = graph.SplitEdge(position0, *edgePair.first);
Graph::Node &node1 = graph.SplitEdge(position1, *edgePair.second);
return &graph.ConnectNodes(node0, node1);
}
static void randomize(Graph &graph, const int32 seed)
{
check(graph.CountNodes() == 4u);
check(graph.CountHalfEdges() == 8u);
check(graph.CountFaces() == 2u);
FRandomStream random(seed);
/// @todo We skip first face because is the surrounding face. But this won't
/// be always the case, if the graph is generated differently it might be a
/// different one.
Graph::Face *face = &*(++graph.GetFaces().begin());
do {
face = splitFace(graph, *face, random);
#ifdef WITH_EDITOR
graph.PrintToLog();
#endif // WITH_EDITOR
} while (face != nullptr);
}
// =============================================================================
// -- GraphGenerator -----------------------------------------------------------
// =============================================================================
TUniquePtr<DoublyConnectedEdgeList> GraphGenerator::Generate(
const uint32 SizeX,
const uint32 SizeY,
const int32 Seed)
{
using Position = typename DoublyConnectedEdgeList::Position;
std::array<Position, 4u> box = {
Position(0, 0),
Position(0, SizeY),
Position(SizeX, SizeY),
Position(SizeX, 0)};
auto Dcel = MakeUnique<DoublyConnectedEdgeList>(box);
randomize(*Dcel, Seed);
return Dcel;
}
} // namespace MapGen

View File

@ -0,0 +1,19 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "DoublyConnectedEdgeList.h"
namespace MapGen {
/// Random DoublyConnectedEdgeList generator.
class CARLA_API GraphGenerator : private NonCopyable
{
public:
/// Create a squared DoublyConnectedEdgeList of size @a SizeX times @a SizeY
/// and generate random connections inside using fixed @a Seed.
static TUniquePtr<DoublyConnectedEdgeList> Generate(uint32 SizeX, uint32 SizeY, int32 Seed);
};
} // namespace MapGen

View File

@ -0,0 +1,168 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "GraphParser.h"
#include "DoublyConnectedEdgeList.h"
#include <type_traits>
#include <unordered_set>
namespace MapGen {
using Graph = DoublyConnectedEdgeList;
// ===========================================================================
// -- Local static methods ---------------------------------------------------
// ===========================================================================
static int getQuadrant(float angle) {
return static_cast<int>(std::round(angle/HALF_PI));
}
// Assumes angles are separated by half pi approx.
static float getRotation(float angle0, float angle1) {
const int min = getQuadrant(std::min(angle0, angle1));
const int max = getQuadrant(std::max(angle0, angle1));
return HALF_PI * std::min(min, min * max);
}
// Assumes angles are separated by half pi approx.
static float getRotation(float angle0, float angle1, float angle2) {
/// @todo There has to be a better way.
switch (getQuadrant(angle0) + getQuadrant(angle1) + getQuadrant(angle2)) {
case 0:
return HALF_PI;
case 1:
return 0.0;
case 2:
return -1.0 * HALF_PI;
case 3:
return PI;
default:
UE_LOG(LogCarla, Error, TEXT("Wrong quadrants"));
return 0.0;
}
}
/// @todo This can probably be done at graph creation.
static void fixGraphData(Graph &graph) {
// Set the edge count for each node in the graph.
for (auto &node : graph.GetNodes()) {
std::vector<float> angles;
angles.reserve(4u);
// Iterate every half-edge in this node.
auto &firstEdge = Graph::GetLeavingHalfEdge(node);
auto *edge = &firstEdge;
do {
edge->Angle = Graph::GetAngle(*edge);
angles.emplace_back(edge->Angle);
edge = &Graph::GetNextInNode(*edge);
} while (edge != &firstEdge);
check(!angles.empty());
node.EdgeCount = angles.size();
node.bIsIntersection = true;
switch (node.EdgeCount) {
case 2:
node.Rotation = getRotation(angles[0u], angles[1u]);
node.IntersectionType = EIntersectionType::Turn90Deg;
break;
case 3:
node.Rotation = getRotation(angles[0u], angles[1u], angles[2u]);
node.IntersectionType = EIntersectionType::TIntersection;
break;
case 4:
default:
node.Rotation = 0.0;
node.IntersectionType = EIntersectionType::XIntersection;
break;
}
node.Rots.swap(angles);
}
}
// ===========================================================================
// -- RoadSegmentBuilder -----------------------------------------------------
// ===========================================================================
class RoadSegmentBuilder {
public:
std::vector<TUniquePtr<RoadSegmentDescription>> Segments;
explicit RoadSegmentBuilder(const Graph &graph) : _graph(graph) {}
void Add(Graph::HalfEdge &edge) {
if (!insert(edge))
return;
if (Graph::GetSource(edge).bIsIntersection) {
Segments.emplace_back(MakeUnique<RoadSegmentDescription>());
_handlingInitial = false;
}
if (_handlingInitial) {
_initial.emplace_back(&edge);
} else {
Segments.back()->Add(edge);
}
}
void Close() {
for (auto edge : _initial) {
Segments.back()->Add(*edge);
}
_handlingInitial = true;
}
private:
/// Insert both half-edges only if they haven't been visited yet.
bool insert(Graph::HalfEdge &edge) {
return _visitedEdges.insert(&edge).second &&
_visitedEdges.insert(&Graph::GetPair(edge)).second;
}
const Graph &_graph;
std::unordered_set<const Graph::HalfEdge *> _visitedEdges;
bool _handlingInitial = true;
std::vector<const Graph::HalfEdge *> _initial;
};
// ===========================================================================
// -- GraphParser ------------------------------------------------------------
// ===========================================================================
GraphParser::GraphParser(DoublyConnectedEdgeList &graph) {
check(graph.CountNodes() >= 4u);
check(graph.CountHalfEdges() >= 8u);
check(graph.CountFaces() >= 2u);
fixGraphData(graph);
CityAreas.reserve(graph.CountFaces() - 1);
RoadSegmentBuilder rsb(graph);
auto faceList = graph.GetFaces();
auto it = faceList.begin();
++it; // Ignore first face (unbounded).
for (; it != faceList.end(); ++it) {
CityAreas.emplace_back(MakeUnique<CityAreaDescription>(*it));
CityAreaDescription &cityArea = *CityAreas.back();
// Iterate every half-edge in this face.
auto &firstEdge = Graph::GetHalfEdge(*it);
for (auto *edge = &Graph::GetNextInFace(firstEdge);
edge != &firstEdge;
edge = &Graph::GetNextInFace(*edge)) {
cityArea.Add(Graph::GetSource(*edge));
rsb.Add(*edge);
}
rsb.Close();
}
RoadSegments.swap(rsb.Segments);
}
} // namespace MapGen

View File

@ -0,0 +1,68 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "CityAreaDescription.h"
#include "GraphTypes.h"
#include "RoadSegmentDescription.h"
#include <vector>
namespace MapGen {
class DoublyConnectedEdgeList;
class CARLA_API GraphParser : private NonCopyable
{
public:
explicit GraphParser(DoublyConnectedEdgeList &Dcel);
bool HasRoadSegments() const {
return !RoadSegments.empty();
}
bool HasCityAreas() const {
return !CityAreas.empty();
}
size_t RoadSegmentCount() const {
return RoadSegments.size();
}
size_t CityAreaCount() const {
return CityAreas.size();
}
const RoadSegmentDescription &GetRoadSegmentAt(size_t i) const {
return *RoadSegments[i];
}
const CityAreaDescription &GetCityAreaAt(size_t i) const {
return *CityAreas[i];
}
TUniquePtr<RoadSegmentDescription> PopRoadSegment() {
TUniquePtr<RoadSegmentDescription> ptr{RoadSegments.back().Release()};
RoadSegments.pop_back();
return ptr;
}
TUniquePtr<CityAreaDescription> PopCityArea() {
TUniquePtr<CityAreaDescription> ptr{CityAreas.back().Release()};
CityAreas.pop_back();
return ptr;
}
private:
using RoadSegmentList = std::vector<TUniquePtr<RoadSegmentDescription>>;
using CityAreaList = std::vector<TUniquePtr<CityAreaDescription>>;
RoadSegmentList RoadSegments;
CityAreaList CityAreas;
};
} // namespace MapGen

View File

@ -0,0 +1,16 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "GraphTypes.h"
namespace MapGen {
#ifdef WITH_EDITOR
template <> uint32 DataIndex<'n'>::NEXT_INDEX = 0u;
template <> uint32 DataIndex<'e'>::NEXT_INDEX = 0u;
template <> uint32 DataIndex<'f'>::NEXT_INDEX = 0u;
#endif // WITH_EDITOR
} // namespace MapGen

View File

@ -0,0 +1,62 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "CityMapDefinitions.h"
#include <vector>
namespace MapGen {
#ifdef WITH_EDITOR
/// For debug only purposes.
template <char C>
struct DataIndex : private NonCopyable
{
DataIndex() : index(++NEXT_INDEX) {}
static void ResetIndex() {
NEXT_INDEX = 0u;
}
template <typename OSTREAM>
friend OSTREAM &operator<<(OSTREAM &os, const DataIndex &d) {
os << C << d.index;
return os;
}
// private:
uint32 index = 0u;
static uint32 NEXT_INDEX;
};
# define INHERIT_GRAPH_TYPE_BASE_CLASS(c) : public DataIndex<c>
#else
# define INHERIT_GRAPH_TYPE_BASE_CLASS(c) : private NonCopyable
#endif // WITH_EDITOR
struct GraphNode INHERIT_GRAPH_TYPE_BASE_CLASS('n')
{
uint32 EdgeCount;
bool bIsIntersection = true; // at this point every node is an intersection.
EIntersectionType IntersectionType;
float Rotation;
std::vector<float> Rots;
};
struct GraphHalfEdge INHERIT_GRAPH_TYPE_BASE_CLASS('e')
{
float Angle;
};
struct GraphFace INHERIT_GRAPH_TYPE_BASE_CLASS('f')
{
};
#undef INHERIT_GRAPH_TYPE_BASE_CLASS
} // namespace MapGen

View File

@ -0,0 +1,43 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
namespace MapGen {
template<typename IT>
class CARLA_API ListView
{
public:
using iterator = IT;
explicit ListView(iterator begin, iterator end) : Begin(begin), End(end) {}
template <typename STL_CONTAINER>
explicit ListView(STL_CONTAINER &StlContainer) :
Begin(iterator(StlContainer.begin())),
End(iterator(StlContainer.end())) {}
ListView(const ListView &) = default;
ListView &operator=(const ListView &) = delete;
iterator begin() const {
return Begin;
}
iterator end() const {
return End;
}
bool empty() const {
return Begin == End;
}
private:
const iterator Begin;
const iterator End;
};
} // namespace MapGen

View File

@ -0,0 +1,55 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include <type_traits>
namespace MapGen {
template<typename T>
class CARLA_API Position {
public:
using number_type = T;
static_assert(
std::is_arithmetic<number_type>::value &&
!std::is_same<number_type, bool>::value, "not a valid number type");
number_type x;
number_type y;
constexpr Position(T X, T Y) : x(X), y(Y) {}
constexpr bool operator==(const Position &rhs) const {
return (x == rhs.x) && (y == rhs.y);
}
constexpr bool operator!=(const Position &rhs) const {
return !(*this == rhs);
}
Position &operator+=(const Position &rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
friend Position operator+(Position lhs, const Position &rhs) {
lhs += rhs;
return lhs;
}
Position &operator-=(const Position &rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend Position operator-(Position lhs, const Position &rhs) {
lhs -= rhs;
return lhs;
}
};
} // namespace MapGen

View File

@ -0,0 +1,48 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "GraphTypes.h"
#include <vector>
namespace MapGen {
class CARLA_API RoadSegmentDescription : private NonCopyable
{
public:
void Add(const GraphHalfEdge &Edge) {
if (Angle == nullptr) {
Angle = MakeUnique<float>(Edge.Angle);
} else if (*Angle != Edge.Angle) { /// @todo Use a scale.
Angle = nullptr;
}
_vect.emplace_back(&Edge);
}
const GraphHalfEdge &operator[](size_t i) const {
return *_vect[i];
}
size_t Size() const {
return _vect.size();
}
bool IsStraight() const {
return Angle.IsValid();
}
/// @return nullptr if the road segment is not straight.
const float *GetAngle() const {
return Angle.Get();
}
private:
TUniquePtr<float> Angle = nullptr;
std::vector<const GraphHalfEdge *> _vect;
};
} // namespace MapGen