|
| 1 | +# The Landscape/Terrain API |
| 2 | + |
| 3 | +Terrains in UE4 are special actors built over the 'heightmap' concept. |
| 4 | + |
| 5 | +Each Terrain is a grid of components (ULandscapeComponent). Each component is mapped to a texture holding heights data. |
| 6 | + |
| 7 | +The landscape component concept is really important as it impacts performance and quality of the result. A component is the minimal render unit of a terrain |
| 8 | +(so it is the minimal geometry that can be culled both from the rendering and collisions point of view). |
| 9 | + |
| 10 | +A brief explanaton on landscape components is available here: https://docs.unrealengine.com/latest/INT/Engine/Landscape/TechnicalGuide/#componentsections |
| 11 | + |
| 12 | +To build a new terrain (or Landscape in UE4), you need to get a heightmap. From this heightmap, the UE4 api will generate |
| 13 | +the textures mapped to components. |
| 14 | + |
| 15 | +Heightmaps are simple arrays of unsigned 16bit values (0 to 65535 with 32768 value considered the 'sea level'). |
| 16 | + |
| 17 | +In Python (for performance reason, and to simplify integration with numpy) heightsmap are represented as bytearray's (so you eventually need to recast them). |
| 18 | + |
| 19 | +# Creating a new Landscape |
| 20 | + |
| 21 | +We start by creating a heightmap with random values: |
| 22 | + |
| 23 | +```python |
| 24 | +import unreal_engine as ue |
| 25 | +import struct |
| 26 | +import random |
| 27 | + |
| 28 | +width = 1024 |
| 29 | +height = 1024 |
| 30 | +heightmap = [] |
| 31 | + |
| 32 | +# fill the heightmap with random values |
| 33 | +for y in range(0, height): |
| 34 | + for x in range(0, width): |
| 35 | + heightmap.append(random.randint(0, 65535)) |
| 36 | + |
| 37 | +data = struct.pack('{0}H'.format(width * height), *heightmap) |
| 38 | +``` |
| 39 | + |
| 40 | +Now 'data' is something we can use for the landscape api |
| 41 | + |
| 42 | +Before filling up a landscape, we need to spawn it: |
| 43 | + |
| 44 | +```python |
| 45 | +from unreal_engine.classes import Landscape |
| 46 | + |
| 47 | +new_landscape = ue.get_editor_world().actor_spawn(Landscape) |
| 48 | +``` |
| 49 | + |
| 50 | +Note: do not run the previous script, as the editor does not like uninitialized terrains. (read: it will brutally crash) |
| 51 | + |
| 52 | +Now it is time to fill the terrain with the heightmap data we created before. We need to choose how many components we need (the grid resolution)and how many quads are required for each component |
| 53 | +(each component geometry is formed by simple quads primitives). |
| 54 | + |
| 55 | +Once we know how big the terrain will be, we can expand/adapt the heightmap accordingly using a special UE4 api function: |
| 56 | + |
| 57 | +``` |
| 58 | +unreal_engine.heightmap_expand(data, original_width, original_height, terrain_width, terrain_height) |
| 59 | +``` |
| 60 | + |
| 61 | +This function will generate a new heightmap with the optimal dimension for the landscape. |
| 62 | + |
| 63 | + |
| 64 | +```python |
| 65 | +import unreal_engine as ue |
| 66 | +import struct |
| 67 | +import random |
| 68 | +from unreal_engine.classes import Landscape |
| 69 | + |
| 70 | +width = 1024 |
| 71 | +height = 1024 |
| 72 | +heightmap = [] |
| 73 | + |
| 74 | +for y in range(0, height): |
| 75 | + for x in range(0, width): |
| 76 | + heightmap.append(random.randint(0, 65535)) |
| 77 | + |
| 78 | +data = struct.pack('{0}H'.format(width * height), *heightmap) |
| 79 | + |
| 80 | +quads_per_section = 63 |
| 81 | +number_of_sections = 1 |
| 82 | +components_x = 8 |
| 83 | +components_y = 8 |
| 84 | + |
| 85 | +fixed_data = ue.heightmap_expand(data, width, height, quads_per_section * number_of_sections * components_x + 1, quads_per_section * number_of_sections * components_y + 1) |
| 86 | + |
| 87 | +landscape = ue.get_editor_world().actor_spawn(Landscape) |
| 88 | +landscape.landscape_import(quads_per_section, number_of_sections, components_x, components_y, fixed_data) |
| 89 | +landscape.set_actor_scale(1,1,1) |
| 90 | +``` |
| 91 | + |
| 92 | +You should have noted that instead specifying the quads per component we are using the 'section' concept. |
| 93 | + |
| 94 | +The truth is that UE4 allows another level of subdivision for giving better control over optimizations (LOD, mipmapping...). More details here: |
| 95 | + |
| 96 | +https://docs.unrealengine.com/latest/INT/Engine/Landscape/TechnicalGuide/#componentsections |
| 97 | + |
| 98 | +You can have 1 section (1x1 quad) or 2 (2x2 quads). Other values are not supported. |
| 99 | + |
| 100 | +Even the number of quads is related to textures size, so valid values are: 7x7, 15x15, 31x31, 63x63, 127x127, 255x255 (note the off-by-one weirdness, as all of the terrain tools works with max value on not the size) |
| 101 | + |
| 102 | +Note that you need to carefully choose the size of the terrain as well as the heightmap: |
| 103 | + |
| 104 | +https://docs.unrealengine.com/latest/INT/Engine/Landscape/TechnicalGuide/index.html#calculatingheightmapdimensions |
| 105 | + |
| 106 | + |
0 commit comments