Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/images/tiles/tile_actors_overview.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ development/use_prebuilt.md
development/use_source.md
client_setup.md
use_plugin.md
tile_actors.md
development/dev_setup_linux.md
development/dev_setup_win.md
development/vscode_user_settings.md
Expand Down Expand Up @@ -97,4 +98,4 @@ ros/ros.md
:caption: Python Client API Reference

Python Client API Reference <https://iamaisim.github.io/ProjectAirSim/client_api/index.html>
```
```
96 changes: 96 additions & 0 deletions docs/tile_actors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Tile Camera and HTTP Tile Server Actors

This page explains how to use the tile actors added to the `ProjectAirSim` Unreal plugin:

- `ATileCameraActor` - maps XYZ tile coordinates to a camera world position and sets orthographic width.
- `ATileHttpServerActor` - serves tile PNG files over HTTP from disk and can render missing tiles on demand through `ATileCameraActor`.
- `UTileMercatorLibrary` - helper library used by both actors to convert tile coordinates to Unreal world coordinates.

The source files are located in:

- `unreal/Blocks/Plugins/ProjectAirSim/Source/ProjectAirSim/Private/Tiles/TileCameraActor.*`
- `unreal/Blocks/Plugins/ProjectAirSim/Source/ProjectAirSim/Private/Tiles/TileHttpServerActor.*`
- `unreal/Blocks/Plugins/ProjectAirSim/Source/ProjectAirSim/Private/Tiles/TileMercatorLibrary.*`

![Tile actors overview (replace with your own screenshot)](images/tiles/tile_actors_overview.jpg)

## Prerequisites

1. Use a build that includes the tile actor commit.
2. Ensure the plugin compiles with `HTTPServer` enabled in module dependencies.
3. Open your Unreal level and place the actors from the ProjectAirSim plugin classes.

## How to use `ATileCameraActor`

`ATileCameraActor` is responsible for computing camera transform and ortho width for one map tile.

1. Place a `TileCameraActor` in the level.
2. Configure these key properties in Details panel:
- `Zoom`, `TileX`, `TileY`
- `OriginLat`, `OriginLon` (reference lat/lon mapped to Unreal world origin Same as setted in Jsonc)
- `AltitudeMeters`
- `bTileYIsTMS` if your Y index uses TMS format
- `bUseMercatorScale` to match standard WebMercator tile size behavior (I use it False, True case was not working very well)
3. Enable `bUpdateOnBeginPlay` for one-time initialization or `bUpdateEveryTick` for continuous updates.
4. If you need immediate capture output, enable `bCaptureAfterUpdate`.

Notes:

- The actor uses a `SceneCaptureComponent2D` and defaults to orthographic projection.
- `bNorthIsX` controls axis convention:
- `false`: X=East, Y=North
- `true`: X=North, Y=East

## How to use `ATileHttpServerActor`

`ATileHttpServerActor` exposes tiles over HTTP with route:

- `GET /tiles/:z/:x/:y`

### Basic file-server mode (cache only)

1. Place a `TileHttpServerActor` in the level.
2. Set:
- `Port` (default `8080`)
- `RoutePrefix` (default `/tiles`)
- `TileRootDir` (default `Saved/Tiles` when relative)
- `MinZoom`, `MaxZoom`
3. Keep `bRenderMissingTiles = false`.
4. Start play. Existing files at `TileRootDir/z/x/y.png` are served directly.

### On-demand render mode (render missing tiles)

1. Place both `TileCameraActor` and `TileHttpServerActor` in the level.
2. In `TileHttpServerActor`, set:
- `bRenderMissingTiles = true`
- `TileCamera = <your TileCameraActor instance>`
3. Optionally set `TileSize` (default `256`) and `bTileYIsTMS`.
4. When a tile is missing on disk, the server renders it, returns PNG bytes, and saves to cache.

## Request examples

With default settings (`Port=8080`, `RoutePrefix=/tiles`):

```bash
curl -o tile.png http://127.0.0.1:8080/tiles/20/0/0
```

PNG extension in URL is also accepted:

```bash
curl -o tile.png http://127.0.0.1:8080/tiles/20/0/0.png
```

## Tile directory layout

Rendered or pre-generated tiles are expected/saved as:

```text
<TileRootDir>/<z>/<x>/<y>.png
```

If `TileRootDir` is relative (for example, `Tiles`), absolute location becomes:

```text
<ProjectSavedDir>/Tiles
```
4 changes: 4 additions & 0 deletions docs/use_plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ bCookAll=True

You can also add more drones to the scene. See **[Multiple Robots in a Simulation](multiple_robots.md)** for more details.

## How to use tile camera and tile HTTP server actors

See **[Tile Camera and HTTP Tile Server Actors](tile_actors.md)** for setup and request examples.

## How to modify the drone

See **[How to Modify a Drone's Physical Geometry](modify_drone_physical.md)** for more details about how a drone's physical geometry can be modified through the **[Robot Configuration Settings](config_robot.md)**.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "Tiles/TileCameraActor.h"

#include "Components/SceneCaptureComponent2D.h"
#include "Components/SceneComponent.h"
#include "Engine/SceneCapture2D.h"
#include "Tiles/TileMercatorLibrary.h"

ATileCameraActor::ATileCameraActor() {
PrimaryActorTick.bCanEverTick = true;

USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);

CaptureComponent =
CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("CaptureComponent"));
CaptureComponent->SetupAttachment(RootComponent);
CaptureComponent->ProjectionType = ECameraProjectionMode::Orthographic;
CaptureComponent->bCaptureEveryFrame = false;
CaptureComponent->bCaptureOnMovement = false;
CaptureComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;

SetActorRotation(FRotator(-90.0f, 0.0f, 0.0f));
}

void ATileCameraActor::BeginPlay() {
Super::BeginPlay();

if (bUpdateOnBeginPlay) {
UpdateFromTile();
}
}

void ATileCameraActor::Tick(float DeltaTime) {
Super::Tick(DeltaTime);

if (bUpdateEveryTick) {
UpdateFromTile();
}
}

void ATileCameraActor::UpdateFromTile() {
int32 YForCalc = TileY;
if (bTileYIsTMS && Zoom >= 0) {
const int32 MaxIndex = (1 << Zoom) - 1;
YForCalc = MaxIndex - TileY;
}

FVector LocationCm = FVector::ZeroVector;
double OrthoWidthCm = 0.0;
UTileMercatorLibrary::ComputeTileCamera(
Zoom, TileX, YForCalc, OriginLat, OriginLon, AltitudeMeters, bNorthIsX,
bUseMercatorScale, LocationCm, OrthoWidthCm);

SetActorLocation(LocationCm + LocalOffsetCm);

if (CaptureComponent && bApplyOrthoWidthToCapture) {
CaptureComponent->ProjectionType = ECameraProjectionMode::Orthographic;
CaptureComponent->OrthoWidth = OrthoWidthCm;
}

if (CaptureComponent && bCaptureAfterUpdate) {
CaptureComponent->CaptureScene();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TileCameraActor.generated.h"

class USceneCaptureComponent2D;

UCLASS()
class PROJECTAIRSIM_API ATileCameraActor : public AActor {
GENERATED_BODY()

public:
ATileCameraActor();

protected:
virtual void BeginPlay() override;

public:
virtual void Tick(float DeltaTime) override;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
int32 Zoom = 20;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
int32 TileX = 0;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
int32 TileY = 0;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
double OriginLat = 47.641468;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
double OriginLon = -122.140165;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
double AltitudeMeters = 122.0;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bNorthIsX = false;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bUseMercatorScale = true;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
FVector LocalOffsetCm = FVector::ZeroVector;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bTileYIsTMS = false;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bUpdateOnBeginPlay = true;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bUpdateEveryTick = false;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bApplyOrthoWidthToCapture = true;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tiles")
bool bCaptureAfterUpdate = false;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tiles")
USceneCaptureComponent2D* CaptureComponent = nullptr;

UFUNCTION(BlueprintCallable, Category = "Tiles")
void UpdateFromTile();
};
Loading