Merge branch 'dev' into benchmark_branch
This commit is contained in:
commit
27967e0a77
|
@ -0,0 +1,63 @@
|
|||
Contributing to CARLA
|
||||
=====================
|
||||
|
||||
> _This document is a work in progress and might be incomplete._
|
||||
|
||||
We are more than happy to accept contributions!
|
||||
|
||||
How can I contribute?
|
||||
|
||||
* Reporting bugs
|
||||
* Feature requests
|
||||
* Code contributions
|
||||
|
||||
Reporting bugs
|
||||
--------------
|
||||
|
||||
Use our [issue section](issueslink) on GitHub. Please check before that the
|
||||
issue was not already added.
|
||||
|
||||
[issueslink]: https://github.com/carla-simulator/carla/issues
|
||||
|
||||
Feature requests
|
||||
----------------
|
||||
|
||||
Please check first the list of [feature requests][frlink]. If it is not there
|
||||
and you think is a feature that might be interesting for users, please submit
|
||||
your request as a new issue.
|
||||
|
||||
[frlink]: https://github.com/carla-simulator/carla/issues?q=is%3Aissue+is%3Aopen+label%3A%22feature+request%22
|
||||
|
||||
Code contributions
|
||||
------------------
|
||||
|
||||
Before starting hands-on on coding, please check out the
|
||||
[projects page][projectslink] to see if we are already working on that. In case
|
||||
of doubt or to discuss how to proceed, please contact one of us (or send an
|
||||
email to carla.simulator@gmail.com).
|
||||
|
||||
[projectslink]: https://github.com/carla-simulator/carla/projects/1
|
||||
|
||||
#### What should I know before I get started?
|
||||
|
||||
Check out the ["CARLA Design"](carla_design.md) document to get an idea on the
|
||||
different modules that compose CARLA, and chose the most appropriate one to hold
|
||||
the new feature.
|
||||
|
||||
#### Coding style
|
||||
|
||||
Please follow the current coding style when submitting new code.
|
||||
|
||||
* Use spaces, not tabs.
|
||||
* Comments should not exceed 80 columns, code may exceed this limit a bit in rare
|
||||
occasions if it results in clearer code.
|
||||
* Python code follows [PEP8 style guide](https://www.python.org/dev/peps/pep-0008/) (use `autopep8` whenever possible).
|
||||
* Unreal C++ code, CarlaUE4 and Carla plugin, follow the [Unreal Engine's Coding Standard](https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/) with the exception of using spaces instead of tabs.
|
||||
* CarlaServer uses [Google's style guide](https://google.github.io/styleguide/cppguide.html).
|
||||
|
||||
#### Pull request
|
||||
|
||||
Once you think your contribution is ready to be added to CARLA, please submit a
|
||||
pull request and one of our team members will take a look at it.
|
||||
|
||||
Try to be as descriptive as possible when filling the pull-request description.
|
|
@ -0,0 +1,43 @@
|
|||
CARLA Design
|
||||
============
|
||||
|
||||
> _This document is a work in progress and might be incomplete._
|
||||
|
||||
CARLA is composed by the following modules
|
||||
|
||||
* Client side
|
||||
- Python client API: "PythonClient/carla"
|
||||
* Server side
|
||||
- CarlaUE4 Unreal Engine project: "Unreal/CarlaUE4"
|
||||
- Carla plugin for Unreal Engine: "Unreal/CarlaUE4/Plugins/Carla"
|
||||
- CarlaServer: "Util/CarlaServer"
|
||||
|
||||
Python client API
|
||||
-----------------
|
||||
|
||||
The client API provides a Python module for communicating with the CARLA server.
|
||||
In the folder "PythonClient", we provide several examples for scripting a CARLA
|
||||
client using the "carla" module.
|
||||
|
||||
CarlaUE4 Unreal Engine project
|
||||
------------------------------
|
||||
|
||||
The Unreal project "CarlaUE4" contains all the assets and scenes for generating
|
||||
the CARLA binary. It uses the tools provided by the Carla plugin to assemble the
|
||||
cities and behavior of the agents in the scene.
|
||||
|
||||
Carla plugin for Unreal Engine
|
||||
------------------------------
|
||||
|
||||
The Carla plugin contains all the functionality of CARLA. We tried to keep this
|
||||
functionality separated from the assets, so the functionality in this plugin can
|
||||
be used as much as possible in any Unreal project.
|
||||
|
||||
It uses "CarlaServer" library for the networking communication.
|
||||
|
||||
CarlaServer
|
||||
-----------
|
||||
|
||||
External library for the networking communications.
|
||||
|
||||
See ["CarlaServer"](carla_server.md) for implementation details.
|
Binary file not shown.
After Width: | Height: | Size: 424 KiB |
|
@ -7,7 +7,6 @@ CARLA Documentation
|
|||
* [CARLA settings](carla_settings.md)
|
||||
* [Measurements](measurements.md)
|
||||
* [Cameras and sensors](cameras_and_sensors.md)
|
||||
|
||||
* [F.A.Q.](faq.md)
|
||||
* [Troubleshooting](troubleshooting.md)
|
||||
|
||||
|
@ -17,8 +16,13 @@ CARLA Documentation
|
|||
* [How to build on Windows](how_to_build_on_windows.md)
|
||||
* [How to add Automotive Materials](how_to_add_automotive_materials.md)
|
||||
|
||||
#### Contributing
|
||||
|
||||
* [Contribution guidelines](CONTRIBUTING.md)
|
||||
|
||||
#### Development
|
||||
|
||||
* [Map customization](map_customization.md)
|
||||
* [How to add assets](how_to_add_assets.md)
|
||||
* [CARLA design](carla_design.md)
|
||||
* [CarlaServer documentation](carla_server.md)
|
||||
|
|
|
@ -116,10 +116,22 @@ If enabled, the server attaches a list of agents to the measurements package
|
|||
every frame. Each of these agents has an unique id that identifies it, and
|
||||
belongs to one of the following classes
|
||||
|
||||
* **Vehicle** Contains its transform, bounding-box, and forward speed.
|
||||
* **Pedestrian** Contains its transform, bounding-box, and forward speed. (*)
|
||||
* **Vehicle** Contains its transform, box-extent, and forward speed.
|
||||
* **Pedestrian** Contains its transform, box-extent, and forward speed. (*)
|
||||
* **Traffic light** Contains its transform and state (green, yellow, red).
|
||||
* **Speed-limit sign** Contains its transform and speed-limit.
|
||||
|
||||
(*) At this point every pedestrian is assumed to have the same bounding-box
|
||||
size.
|
||||
|
||||
###### Transform and bounding box
|
||||
|
||||
The transform defines the location and orientation of the agent. The bounding
|
||||
box is assumed to be centered at the agent's location. The box extent gives the
|
||||
radii dimensions of the bounding box of the agent.
|
||||
|
||||
![Vehicle Bounding Box](img/vehicle_bounding_box.png)
|
||||
|
||||
!!! important
|
||||
As seen in the picture, the Z coordinate of the box is not fitted to
|
||||
vehicle's height.
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB), and the INTEL Visual Computing Lab.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include "Carla.h"
|
||||
#include "RoutePlanner.h"
|
||||
|
||||
#include "CarlaWheeledVehicle.h"
|
||||
#include "Util/RandomEngine.h"
|
||||
#include "WheeledVehicleAIController.h"
|
||||
|
||||
#include "Engine/CollisionProfile.h"
|
||||
|
||||
static bool IsSplineValid(const USplineComponent *SplineComponent)
|
||||
{
|
||||
return (SplineComponent != nullptr) &&
|
||||
(SplineComponent->GetNumberOfSplinePoints() > 1);
|
||||
}
|
||||
|
||||
static AWheeledVehicleAIController *GetVehicleController(AActor *Actor)
|
||||
{
|
||||
auto *Vehicle = (Actor->IsPendingKill() ? nullptr : Cast<ACarlaWheeledVehicle>(Actor));
|
||||
return (Vehicle != nullptr ?
|
||||
Cast<AWheeledVehicleAIController>(Vehicle->GetController()) :
|
||||
nullptr);
|
||||
}
|
||||
|
||||
static const USplineComponent *PickARoute(
|
||||
URandomEngine &RandomEngine,
|
||||
const TArray<USplineComponent *> &Routes,
|
||||
const TArray<float> &Probabilities)
|
||||
{
|
||||
check(Routes.Num() > 0);
|
||||
|
||||
if (Routes.Num() == 1) {
|
||||
return Routes[0];
|
||||
}
|
||||
|
||||
auto Index = RandomEngine.GetIntWithWeight(Probabilities);
|
||||
check((Index >= 0) && (Index < Routes.Num()));
|
||||
return Routes[Index];
|
||||
}
|
||||
|
||||
ARoutePlanner::ARoutePlanner(const FObjectInitializer& ObjectInitializer) :
|
||||
Super(ObjectInitializer)
|
||||
{
|
||||
RootComponent =
|
||||
ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneRootComponent"));
|
||||
RootComponent->SetMobility(EComponentMobility::Static);
|
||||
|
||||
TriggerVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerVolume"));
|
||||
TriggerVolume->SetupAttachment(RootComponent);
|
||||
TriggerVolume->SetHiddenInGame(true);
|
||||
TriggerVolume->SetMobility(EComponentMobility::Static);
|
||||
TriggerVolume->SetCollisionProfileName(FName("OverlapAll"));
|
||||
TriggerVolume->bGenerateOverlapEvents = true;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void ARoutePlanner::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
const auto Size = Routes.Num();
|
||||
if (PropertyChangedEvent.Property && (Size != Probabilities.Num())) {
|
||||
Probabilities.Reset(Size);
|
||||
for (auto i = 0; i < Size; ++i) {
|
||||
Probabilities.Add(1.0f / static_cast<float>(Size));
|
||||
if (Routes[i] == nullptr) {
|
||||
Routes[i] = NewObject<USplineComponent>(this);
|
||||
Routes[i]->SetupAttachment(RootComponent);
|
||||
Routes[i]->SetHiddenInGame(true);
|
||||
Routes[i]->SetMobility(EComponentMobility::Static);
|
||||
Routes[i]->RegisterComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
void ARoutePlanner::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (Routes.Num() < 1) {
|
||||
UE_LOG(LogCarla, Warning, TEXT("ARoutePlanner has no route assigned."));
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &&Route : Routes) {
|
||||
if (!IsSplineValid(Route)) {
|
||||
UE_LOG(LogCarla, Error, TEXT("ARoutePlanner has a route with zero way-points."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register delegate on begin overlap.
|
||||
if (!TriggerVolume->OnComponentBeginOverlap.IsAlreadyBound(this, &ARoutePlanner::OnTriggerBeginOverlap))
|
||||
{
|
||||
TriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &ARoutePlanner::OnTriggerBeginOverlap);
|
||||
}
|
||||
}
|
||||
|
||||
void ARoutePlanner::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
// Deregister the delegate.
|
||||
if (TriggerVolume->OnComponentBeginOverlap.IsAlreadyBound(this, &ARoutePlanner::OnTriggerBeginOverlap))
|
||||
{
|
||||
TriggerVolume->OnComponentBeginOverlap.RemoveDynamic(this, &ARoutePlanner::OnTriggerBeginOverlap);
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void ARoutePlanner::OnTriggerBeginOverlap(
|
||||
UPrimitiveComponent * /*OverlappedComp*/,
|
||||
AActor *OtherActor,
|
||||
UPrimitiveComponent * /*OtherComp*/,
|
||||
int32 /*OtherBodyIndex*/,
|
||||
bool /*bFromSweep*/,
|
||||
const FHitResult & /*SweepResult*/)
|
||||
{
|
||||
auto *Controller = GetVehicleController(OtherActor);
|
||||
auto *RandomEngine = (Controller != nullptr ? Controller->GetRandomEngine() : nullptr);
|
||||
if (RandomEngine != nullptr)
|
||||
{
|
||||
auto *Route = PickARoute(*RandomEngine, Routes, Probabilities);
|
||||
|
||||
TArray<FVector> WayPoints;
|
||||
const auto Size = Route->GetNumberOfSplinePoints();
|
||||
check(Size > 1);
|
||||
WayPoints.Reserve(Size);
|
||||
for (auto i = 1; i < Size; ++i)
|
||||
{
|
||||
WayPoints.Add(Route->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::World));
|
||||
}
|
||||
|
||||
Controller->SetFixedRoute(WayPoints);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB), and the INTEL Visual Computing Lab.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/SplineComponent.h"
|
||||
#include "RoutePlanner.generated.h"
|
||||
|
||||
/// Assign a random route to every ACarlaWheeledVehicle entering the trigger
|
||||
/// volume. Routes must be added in editor after placing this actor into the
|
||||
/// world. Spline tangents are ignored, only locations are taken into account
|
||||
/// for making the route.
|
||||
UCLASS()
|
||||
class CARLA_API ARoutePlanner : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
ARoutePlanner(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
protected:
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
UFUNCTION()
|
||||
void OnTriggerBeginOverlap(
|
||||
UPrimitiveComponent* OverlappedComp,
|
||||
AActor *OtherActor,
|
||||
UPrimitiveComponent *OtherComp,
|
||||
int32 OtherBodyIndex,
|
||||
bool bFromSweep,
|
||||
const FHitResult &SweepResult);
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
UBoxComponent *TriggerVolume;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Traffic Routes", EditAnywhere)
|
||||
TArray<USplineComponent *> Routes;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Traffic Routes", EditAnywhere, EditFixedSize)
|
||||
TArray<float> Probabilities;
|
||||
};
|
|
@ -59,6 +59,13 @@ static bool IsThereAnObstacleAhead(
|
|||
RayTrace(Vehicle, StartLeft, EndLeft);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void ClearQueue(std::queue<T> &Queue)
|
||||
{
|
||||
std::queue<T> EmptyQueue;
|
||||
Queue.swap(EmptyQueue);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// -- Constructor and destructor -----------------------------------------------
|
||||
// =============================================================================
|
||||
|
@ -118,8 +125,7 @@ void AWheeledVehicleAIController::ConfigureAutopilot(const bool Enable)
|
|||
Vehicle->SetReverse(false);
|
||||
Vehicle->SetHandbrakeInput(false);
|
||||
TrafficLightState = ETrafficLightState::Green;
|
||||
decltype(TargetLocations) EmptyQueue;
|
||||
TargetLocations.swap(EmptyQueue);
|
||||
ClearQueue(TargetLocations);
|
||||
Vehicle->SetAIVehicleState(
|
||||
bAutopilotEnabled ?
|
||||
ECarlaWheeledVehicleState::FreeDriving :
|
||||
|
@ -130,8 +136,13 @@ void AWheeledVehicleAIController::ConfigureAutopilot(const bool Enable)
|
|||
// -- Traffic ------------------------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
void AWheeledVehicleAIController::SetFixedRoute(const TArray<FVector> &Locations)
|
||||
void AWheeledVehicleAIController::SetFixedRoute(
|
||||
const TArray<FVector> &Locations,
|
||||
const bool bOverwriteCurrent)
|
||||
{
|
||||
if (bOverwriteCurrent) {
|
||||
ClearQueue(TargetLocations);
|
||||
}
|
||||
for (auto &Location : Locations) {
|
||||
TargetLocations.emplace(Location);
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
|
||||
/// Set a fixed route to follow if autopilot is enabled.
|
||||
UFUNCTION(Category = "Wheeled Vehicle Controller", BlueprintCallable)
|
||||
void SetFixedRoute(const TArray<FVector> &Locations);
|
||||
void SetFixedRoute(const TArray<FVector> &Locations, bool bOverwriteCurrent=true);
|
||||
|
||||
/// @}
|
||||
// ===========================================================================
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB), and the INTEL Visual Computing Lab.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include "Carla.h"
|
||||
#include "IntersectionEntrance.h"
|
||||
|
||||
|
||||
// Sets default values
|
||||
AIntersectionEntrance::AIntersectionEntrance(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AIntersectionEntrance::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void AIntersectionEntrance::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
}
|
||||
|
||||
TArray<FVector> AIntersectionEntrance::GetRoute(int it)
|
||||
{
|
||||
TArray<AActor*> points = Routes[it].points;
|
||||
TArray<FVector> route;
|
||||
|
||||
for (int i = 0; i < points.Num(); ++i){
|
||||
route.Add(points[i]->GetActorLocation());
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
float AIntersectionEntrance::GetProbability(int it)
|
||||
{
|
||||
return Routes[it].probability;
|
||||
}
|
||||
|
||||
/*
|
||||
#if WITH_EDITOR
|
||||
|
||||
void AIntersectionEntrance::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
if (PropertyChangedEvent.Property) {
|
||||
if (bCreateRoutes && (GetWorld() != nullptr)) {
|
||||
//ClearRoutes();
|
||||
for (int i = 0; i < Routes.Num(); ++i){
|
||||
for(int e = 0; e < Routes[i].points.Num(); ++e){
|
||||
AActor* actor= GetWorld()->SpawnActor<AActor>();//USphereComponent* createdComp = NewObject<USphereComponent>(this);//CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
|
||||
USceneComponent* SphereMesh = NewObject<USceneComponent>(actor);
|
||||
SphereMesh->AttachToComponent(RootComponent,FAttachmentTransformRules::KeepWorldTransform);
|
||||
if(actor)
|
||||
{
|
||||
actor->RegisterAllComponents();
|
||||
Routes[i].points[e] = actor;
|
||||
//Routes[i].points[e].position = createdComp->GetRelativeTransform().GetLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bCreateRoutes = false;
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
*/
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB), and the INTEL Visual Computing Lab.
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "IntersectionEntrance.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FRoute {
|
||||
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category=TrafficRoutes, EditAnywhere)
|
||||
TArray < AActor *> points;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category=TrafficRoutes, EditAnywhere)
|
||||
float probability = 0.0f;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class CARLA_API AIntersectionEntrance : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
AIntersectionEntrance(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
protected:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Trigger")
|
||||
TArray<FVector> GetRoute(int route);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Trigger")
|
||||
float GetProbability(int route);
|
||||
/*
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif // WITH_EDITOR
|
||||
*/
|
||||
public:
|
||||
|
||||
UPROPERTY(Category = "Routes", EditAnywhere)
|
||||
bool bCreateRoutes = false;
|
||||
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category=TrafficRoutes, EditAnywhere)
|
||||
TArray< FRoute > Routes;
|
||||
|
||||
};
|
|
@ -15,9 +15,12 @@ pages:
|
|||
- 'How to build on Linux': 'how_to_build_on_linux.md'
|
||||
- 'How to build on Windows': 'how_to_build_on_windows.md'
|
||||
- 'How to add Automotive Materials': 'how_to_add_automotive_materials.md'
|
||||
- Contributing:
|
||||
- 'Contribution guidelines': 'CONTRIBUTING.md'
|
||||
- Development:
|
||||
- 'Map customization': 'map_customization.md'
|
||||
- 'How to add assets': 'how_to_add_assets.md'
|
||||
- 'CARLA design': 'carla_design.md'
|
||||
- 'CarlaServer documentation': 'carla_server.md'
|
||||
|
||||
markdown_extensions:
|
||||
|
|
Loading…
Reference in New Issue