LibGame  v0.4.0
The LG Game Engine - Copyright (C) 2024-2025 ETMSoftware
All Data Structures Files Functions Variables
lg_terrain.c File Reference

Functions

LG_Terrainlg_terrain_new (uint16_t width, uint16_t height, float norm_max_height, int noise_type, int seed, float frequency, LG_Texture *tex)
 
LG_Terrainlg_terrain_new_from_heightmap (float *heightmap, uint16_t width, uint16_t height, float norm_max_height, LG_Texture *tex)
 
float lg_terrain_get_elevation (LG_Terrain *terrain, float x, float y, uint16_t w, uint16_t h)
 
void lg_terrain_free (LG_Terrain *terrain)
 
float * lg_heightmap_generate (uint16_t width, uint16_t height, int noise_type, int seed, float frequency)
 
void lg_heightmaps_add (float *heightmap1, float *heightmap2, uint16_t w, uint16_t h, float k)
 
void lg_heightmap_flatten_border (float *heightmap, uint16_t w, uint16_t h, uint16_t k, int border)
 
void lg_heightmap_apply_func (float *heightmap, uint16_t w, uint16_t h, float(*func)(uint16_t, uint16_t, float))
 
LG_Meshlg_terrain_to_mesh (LG_Terrain *terrain)
 
Vertex_rgbalg_horiz_grid (uint16_t grid_width, int *n_vertices, float scaling, LG_Color_u c)
 
const Vertexlg_horiz_grid0 ()
 
const unsigned short * lg_horiz_grid0_indices ()
 
size_t lg_sizeof_horiz_grid0 ()
 
size_t lg_sizeof_horiz_grid0_indices ()
 

Detailed Description

=== Procedural terrain generation ===

Create heightmaps and terrains with Perlin noise.

We use these data structs to compute:

  • Heightmaps
    === Grid mesh (w x h) ===
    y ^
    |
    6------7------8
    | | |
    | | |
    3------4------5
    | | |
    | | |
    0------1------2---->
    x
    n_vertices = (w + 1) * (h + 1)
    n_faces = w * h * 2
    n_indices = n_faces * 3
    vertex_xy = x + y * (w + 1)
  • Vertex normals
    === Quad ===
    y ^
    |
    v4-----v3
    | |
    | |
    v1-----v2---->
    x
    1 quad = 2 triangles (v1-v3-v4, v1-v2-v3) = 2 faces = 4 vertices
  • VBOs and IBOs
    === 2D array (w x h) ===
    y ^
    |
    -------------
    | e2 | e3 |
    -------------
    | e0 | e1 |
    ----------------->
    x
    n_elements = w * h
    size = n_elements * sizeof(element)
    element_i = element_at_xy = x + y * w

NOTE:

  • Grid width = number of units along one row, should always be even - if odd, we substract one
  • Grid height = number of units along one column, should always be even - if odd, we substract one
  • Dims must be < HEIGHTMAP_MAX_W x HEIGHTMAP_MAX_H (defined in lg_terrain.h)

Function Documentation

◆ lg_terrain_new()

LG_Terrain* lg_terrain_new ( uint16_t  width,
uint16_t  height,
float  norm_max_height,
int  noise_type,
int  seed,
float  frequency,
LG_Texture tex 
)

Create a horizontally-centered, normalized LG_Terrain VBO and IBO with noise

If noise_type == PERLIN_NOISE

  • seed is ignored if == 0, and default value 1337 is used instead
  • frequency is ignored if < LG_FLOAT_EPSILON (defined in lg_mesh.h), and default value 0.01 is used instead

As we use the excellent FastNoiseLite lib, you can/should check out https://github.com/Auburn/FastNoiseLite/wiki/Documentation

If noise_type == RANDOM_NOISE, seed and frequency are irrelevant

Height is ambiguous here. So:

  • WIDTH/HEIGHT = WIDTH/HEIGHT OF THE GRID
    Heightmap grid = 2D float array filled with noise values in range [0.0, 1.0]
    float grid[width * heigh]
  • norm_max_height relates to the heightmap height values, it's the normalized max height (should be in range [0.0, 1.0])

Returned LG_Terrain must be freed when done

Parameters
widthWidth of the heightmap grid (num of units along one row), must be < HEIGHTMAP_MAX_W
heightHeight of the heightmap grid (num of units along one column), must be < HEIGHTMAP_MAX_H
norm_max_heightMax height (normalized, should be in range [0.0, 1.0])
noise_typeNoise type (lg_noise_type enum)
seedSeed, default (if ignored) = 1337
frequencyFrequency, default (if ignored) = 0.01
texA LG_Texture, may be NULL
Returns
A new LG_Terrain if OK, NULL on error

◆ lg_terrain_new_from_heightmap()

LG_Terrain* lg_terrain_new_from_heightmap ( float *  heightmap,
uint16_t  width,
uint16_t  height,
float  norm_max_height,
LG_Texture tex 
)

Create a horizontally-centered, normalized LG_Terrain VBO and IBO from a (Vertex_uv_n *) heightmap

Returned LG_Terrain must be freed when done

Parameters
heightmapA (float *) heightmap
widthWidth of the heightmap grid (num of units along one row), must be < HEIGHTMAP_MAX_W
heightHeight of the heightmap grid (num of units along one column), must be < HEIGHTMAP_MAX_H
norm_max_heightMax height (normalized, should be in range [0.0, 1.0])
texA LG_Texture, may be NULL
Returns
A new LG_Terrain if OK, NULL on error

◆ lg_terrain_get_elevation()

float lg_terrain_get_elevation ( LG_Terrain terrain,
float  x,
float  y,
uint16_t  w,
uint16_t  h 
)

Get the terrain elevation at x, y

x and y must be in range [-1.0, 1.0]

w and h are the width and height of the heightmap grid used to generate the terrain

Parameters
x
y
widthWidth of the heightmap grid (num of units along one row), must be < HEIGHTMAP_MAX_W
heightHeight of the heightmap grid (num of units along one column), must be < HEIGHTMAP_MAX_H
Returns
Elevation at x, y

◆ lg_terrain_free()

void lg_terrain_free ( LG_Terrain terrain)

Free LG_Terrain's vbo_data, ibo_data, heightmap, and instance

Parameters
terrainA LG_Terrain instance

◆ lg_heightmap_generate()

float* lg_heightmap_generate ( uint16_t  width,
uint16_t  height,
int  noise_type,
int  seed,
float  frequency 
)

Generate a (non-centered) width x height heightmap (2D float array) filled with noise values in range [0.0, 1.0]

If noise_type == PERLIN_NOISE

  • seed is ignored if == 0, and default value 1337 is used instead
  • frequency is ignored if < LG_FLOAT_EPSILON (defined in lg_mesh.h), and default value 0.01 is used instead

As we use the excellent FastNoiseLite lib, you can/should check out https://github.com/Auburn/FastNoiseLite/wiki/Documentation

If noise_type == RANDOM_NOISE, seed and frequency are irrelevant

Returned array must be freed when done

Parameters
widthGrid width (num of units along one row), must be < HEIGHTMAP_MAX_W
heightGrid height (num of units along one column), must be < HEIGHTMAP_MAX_H
noise_typeNoise type (see lg_noise_type enum in lg_terrain.h)
seedSeed, default = 1337
frequencyFrequency, default = 0.01
Returns
A pointer to the first element of a float array (of size width * height * sizeof(float)) if OK, NULL on error

◆ lg_heightmaps_add()

void lg_heightmaps_add ( float *  heightmap1,
float *  heightmap2,
uint16_t  w,
uint16_t  h,
float  k 
)

Add height values of heightmap2 to height values of heightmpap1, then multiply by k

Quite useful to experiment and create more "stunning" heightmaps

Heightmaps must have same dimensions

Parameters
heightmap1
heightmap2
wWidth of both heightmaps
hHeight of both heightmaps
k

◆ lg_heightmap_flatten_border()

void lg_heightmap_flatten_border ( float *  heightmap,
uint16_t  w,
uint16_t  h,
uint16_t  k,
int  border 
)

'Flatten' heightmap border(s) - useful to join 2 heighmaps

Linear flattening so far, should be Gaussian

border = NORTH_BORDER | SOUTH_BORDER | WEST_BORDER | EAST_BORDER mask

Parameters
heightmap
wWidth
hHeight
khoriz_margin = w / k, vert_margin = h / k, should be in range [2, 6]
borderMask of which border(s) should be flattened

◆ lg_heightmap_apply_func()

void lg_heightmap_apply_func ( float *  heightmap,
uint16_t  w,
uint16_t  h,
float(*)(uint16_t, uint16_t, float)  func 
)

Apply a user-defined func to a heightmap, doing: heightmap[x + y * w] = func(x, y, heightmap[x + y * w])

Quite useful for elevation redistribution

For example, you can get more valley-like Perlin noise with:

float func(uint16_t x, uint16_t y, float h)
{
return expf(h * 6 - 4) / 6; // range -4 -> 2
}
Parameters
heightmap
wWidth
hHeight
funcFunc pointer

◆ lg_terrain_to_mesh()

LG_Mesh* lg_terrain_to_mesh ( LG_Terrain terrain)

Create a new LG_Mesh instance from a LG_Terrain instance

lg_terrain_new() creates a horizontally-centered, normalized LG_Terrain VBO and IBO so the mesh will also be horizontally-centered and normalized

You can free the LG_Terrain instance afterwards, as new vbo_data and ibo_data are dynamically generated (with malloc3())

Parameters
terrainPointer to LG_Terrain instance
Returns
New mesh if OK, NULL on error

◆ lg_horiz_grid()

Vertex_rgba* lg_horiz_grid ( uint16_t  grid_width,
int *  n_vertices,
float  scaling,
LG_Color_u  c 
)

Horiz centered square grid

 Draw with glDrawArrays(GL_LINES, 0, n_vertices)

Must be freed when done

Example use:

// Filling a LG_SceneNode->lines3d_vb to draw a grid
typedef struct {
union {
Vertex_rgba vb_array[LG_LINES_N_VERTICES_MAX];
Vertex_rgba *vb_ptr;
};
uint32_t n_vertices;
zboolean dynamic_b;
int n_vertices;
Vertex_rgba *h_grid = lg_horiz_grid(..., &n_vertices, ...);
...
node->lines3d_vb.vb_ptr = h_grid;
node->lines3d_vb.n_vertices = n_vertices;
node->lines3d_vb.dynamic_b = TRUE;
Parameters
grid_widthGrid width, ie number of units along one row or one column, should be even - if odd, we substract one
n_verticesPointer to int with num of array elements
scalingScaling k
cColor
Returns
An array of Vertex_rgba if OK, NULL on error

◆ lg_horiz_grid0()

const Vertex* lg_horiz_grid0 ( )

DEPRECATED

Horiz centered 8 x 8 grid

Vertices = x, y, z

◆ lg_horiz_grid0_indices()

const unsigned short* lg_horiz_grid0_indices ( )

DEPRECATED

◆ lg_sizeof_horiz_grid0()

size_t lg_sizeof_horiz_grid0 ( )

DEPRECATED

◆ lg_sizeof_horiz_grid0_indices()

size_t lg_sizeof_horiz_grid0_indices ( )

DEPRECATED

lg_scenenode_new
LG_SceneNode * lg_scenenode_new(int id, const char *name, lg_scenenode_type type)
Definition: lg_scene_graph.c:31
LG_SceneNode
Definition: lg_scene_graph.h:29
Lines3D_VB
Definition: lg_3d_primitives.h:53
lg_horiz_grid
Vertex_rgba * lg_horiz_grid(uint16_t grid_width, int *n_vertices, float scaling, LG_Color_u c)
Definition: lg_terrain.c:692
Vertex_rgba
Definition: lg_vertex.h:80