Skip to content

Commit

Permalink
Add a utility function that generates reasonable planetary terrain.
Browse files Browse the repository at this point in the history
  • Loading branch information
Philip Rideout committed Aug 16, 2015
1 parent d46fc32 commit b42794a
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: c
script: scons test earth sdf && scons lib --double
script: scons test earth sdf planet && scons lib --double
addons:
apt:
sources:
Expand Down
7 changes: 4 additions & 3 deletions SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ Alias('lib', heman)

env = env.Clone(LIBS=['m', heman])

env.Program('test_heman', source=['test/test_heman.c'])
env.Program('test_earth', source=['test/test_earth.c'])
env.Program('test_sdf', source=['test/test_sdf.c'])
for test in TESTS:
name = 'test_' + test
path = 'test/' + name + '.c'
env.Program(name, source=[path])

Default('test_heman')
26 changes: 13 additions & 13 deletions SConstruct
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
BUILD_DIR = 'build'
import os.path

TESTS = Split('heman earth sdf planet')

def add_test(target, filename):
import os.path
binpath = os.path.join(BUILD_DIR, filename)
Command(target, binpath, binpath)
AlwaysBuild(target)
BUILD_DIR = 'build'

AddOption('--double',
dest='double',
action='store_true',
help='use 64-bit floats')

# Building Targets
# Targets that build code.

Export('TESTS')
SConscript('SConscript', variant_dir=BUILD_DIR, src_dir='.', duplicate=0)

# Executing Tests
# Targets that run code.

add_test('test', 'test_heman')
add_test('earth', 'test_earth')
add_test('sdf', 'test_sdf')
for test in TESTS:
filename = 'test_' + test
binpath = os.path.join(BUILD_DIR, filename)
Command(test, binpath, binpath)
AlwaysBuild(test)

# Code Formatting
# Targets that format code.

additions = ['include/heman.h'] + Glob('test/*.c')
exclusions = Glob('src/noise.*')
Expand All @@ -32,7 +32,7 @@ Command('format', cfiles, 'clang-format-3.6 -i $SOURCES && ' +
'uncrustify -c uncrustify.cfg --no-backup $SOURCES')
AlwaysBuild('format')

# Sphinx Docs
# Targets that generate documentation.

Command('docs', Glob('docs/*.rst'), 'cd docs ; rm -rf _build ; make html')
AlwaysBuild('docs')
Binary file added docs/_static/planet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions docs/generate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ Islands
.. image:: _static/island.png
:width: 256px
Planets
=======
.. c:function:: heman_image* heman_generate_planet_heightmap(int width, int height, int seed)
High-level function that sums up several octaves of `OpenSimplex <https://en.wikipedia.org/wiki/OpenSimplex_noise>`_ noise over a 3D domain to generate an interesting lat-long height map. Clients should specify a **width** that is twice the value of **height**.
.. image:: _static/planet.png
:width: 512px
2 changes: 1 addition & 1 deletion docs/importexport.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Import / Export
###############

Heman only knows how to work with in-memory floating-point images. It doesn't know how to read and write image files, although its test suite uses `stb <https://github.com/nothings/stb>`_ for handling image files. See the heman utility header (`hut.h <https://github.com/prideout/heman/blob/master/test/hut.h>`_) for an example of this.
Heman only knows how to work with in-memory floating-point images. It doesn't know how to read and write image files, although the test suite uses `stb <https://github.com/nothings/stb>`_ for handling image files. See the heman utility header (`hut.h <https://github.com/prideout/heman/blob/master/test/hut.h>`_) for an example of this.

Heman can, however, convert floating-point to unsigned bytes, or vice versa, using one of the following functions.

Expand Down
4 changes: 4 additions & 0 deletions include/heman.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ heman_image* heman_color_apply_gradient(heman_image* heightmap,
// distance field to generate an interesting height map.
heman_image* heman_generate_island_heightmap(int width, int height, int seed);

// High-level function that uses several octaves of OpenSimplex noise over a 3D
// domain to generate an interesting lat-long height map.
heman_image* heman_generate_planet_heightmap(int width, int height, int seed);

// High-level function that sums up a number of noise octaves, also known as
// Fractional Brownian Motion. Taken alone, Perlin / Simplex noise are not
// fractals; this makes them more fractal-like. A good starting point is to use
Expand Down
63 changes: 54 additions & 9 deletions src/generate.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@
#include "noise.h"
#include <math.h>
#include <memory.h>
#include <kazmath/vec3.h>

static const HEMAN_FLOAT SEALEVEL = 0.5f;

#define NOISE(U, V) open_simplex_noise2(ctx, U, V)
#define MAX(a, b) (a > b ? a : b)
#define MIN(a, b) (a > b ? b : a)
#define NOISE3(p) open_simplex_noise3(ctx, p.x, p.y, p.z)

static heman_image* generate_island_noise(int width, int height, int seed)
{
struct osn_context* ctx;
open_simplex_noise(seed, &ctx);
heman_image* img = heman_image_create(width, height, 3);
HEMAN_FLOAT* data = img->data;
HEMAN_FLOAT invh = 1.0f / MAX(width, height);
HEMAN_FLOAT invw = 1.0f / MAX(width, height);
HEMAN_FLOAT freqs[] = {4.0, 16.0, 32.0, 64.0, 128.0};
HEMAN_FLOAT ampls[] = {0.2, 0.1, 0.05, 0.025, 0.0125};
float invh = 1.0f / MAX(width, height);
float invw = 1.0f / MAX(width, height);
float freqs[] = {4.0, 16.0, 32.0, 64.0, 128.0};
float ampls[] = {0.2, 0.1, 0.05, 0.025, 0.0125};

#pragma omp parallel for
for (int y = 0; y < height; ++y) {
HEMAN_FLOAT v = y * invh;
float v = y * invh;
HEMAN_FLOAT* dst = data + y * width * 3;
for (int x = 0; x < width; ++x) {
HEMAN_FLOAT u = x * invw;
float u = x * invw;
*dst++ = ampls[0] * NOISE(u * freqs[0], v * freqs[0]) +
ampls[1] * NOISE(u * freqs[1], v * freqs[1]) +
ampls[2] * NOISE(u * freqs[2], v * freqs[2]);
Expand Down Expand Up @@ -72,7 +72,6 @@ heman_image* heman_generate_island_heightmap(int width, int height, int seed)

heman_image* heightmap = heman_distance_create_sdf(coastmask);
heman_image_destroy(coastmask);

heman_image* result = heman_image_create(width, height, 1);
data = result->data;

Expand Down Expand Up @@ -132,3 +131,49 @@ heman_image* heman_generate_simplex_fbm(int width, int height, float frequency,
open_simplex_noise_free(ctx);
return img;
}

static void sphere(float u, float v, float r, kmVec3* dst)
{
dst->x = r * sin(v) * cos(u);
dst->y = r * cos(v);
dst->z = r * -sin(v) * sin(u);
}

heman_image* heman_generate_planet_heightmap(int width, int height, int seed)
{
struct osn_context* ctx;
open_simplex_noise(seed, &ctx);
heman_image* result = heman_image_create(width, height, 1);
float scalex = 2.0f * PI / width;
float scaley = PI / height;
float invh = 1.0f / height;

#pragma omp parallel for
for (int y = 0; y < height; ++y) {
HEMAN_FLOAT* dst = result->data + y * width;
kmVec3 p;
float v = y * invh;
float s = 0.95;
HEMAN_FLOAT antarctic_influence = MAX(10 * (v - s) / s, -0.5);
v = fabs(v - 0.5);
v = 1.5 * (0.5 - v);
equatorial_influence = v * v;
v = y * scaley;
for (int x = 0; x < width; ++x) {
float u = x * scalex;
float freq = 1;
float amp = 1;
HEMAN_FLOAT h = antarctic_influence + equatorial_influence;
for (int oct = 0; oct < 6; oct++) {
sphere(u, v, freq, &p);
h += amp * NOISE3(p);
amp *= 0.5;
freq *= 2;
}
*dst++ = h;
}
}

open_simplex_noise_free(ctx);
return result;
}
4 changes: 4 additions & 0 deletions src/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ extern float _gamma;
#define MAX(a, b) (a > b ? a : b)
#define CLAMP(v, lo, hi) MAX(lo, MIN(hi, v))
#define CLAMP01(v) CLAMP(v, 0.0f, 1.0f)
#define SGN(x) ((x > 0) - (x < 0))
#define EDGE(value, upper) MAX(0, MIN(upper - 1, value))
#define TWO_OVER_PI (0.63661977236)
#define PI (3.1415926535)
6 changes: 0 additions & 6 deletions src/lighting.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
#include <memory.h>
#include <kazmath/vec3.h>

#define MIN(a, b) (a > b ? b : a)
#define MAX(a, b) (a > b ? a : b)
#define SGN(x) ((x > 0) - (x < 0))
#define EDGE(value, upper) MAX(0, MIN(upper - 1, value))

heman_image* heman_lighting_compute_normals(heman_image* heightmap)
{
assert(heightmap->nbands == 1);
Expand Down Expand Up @@ -103,7 +98,6 @@ heman_image* heman_lighting_apply(heman_image* heightmap, heman_image* albedo,

#define NUM_SCANS (16)
#define INV_SCANS (1.0f / 16.0f)
#define TWO_OVER_PI (0.63661977236)

static HEMAN_FLOAT azimuth_slope(kmVec3 a, kmVec3 b)
{
Expand Down
46 changes: 46 additions & 0 deletions test/test_planet.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <heman.h>
#include "hut.h"

#define COUNT(a) (sizeof(a) / sizeof(a[0]))
#define OUTFOLDER "build/"
#define SEED 7000
#define HEIGHT 512

static int CP_LOCATIONS[] = {
000, // Dark Blue
126, // Light Blue
127, // Yellow
150, // Dark Green
170, // Brown
200, // Brown
240, // White
255, // White
};

static heman_color CP_COLORS[] = {
0x001070, // Dark Blue
0x2C5A7C, // Light Blue
0xE0F0A0, // Yellow
0x5D943C, // Dark Green
0x606011, // Brown
0x606011, // Brown
0xFFFFFF, // White
0xFFFFFF, // White
};

static float LIGHTPOS[] = {-0.5f, 0.5f, 1.0f};

int main(int argc, char** argv)
{
heman_image* grad = heman_color_create_gradient(
256, COUNT(CP_COLORS), CP_LOCATIONS, CP_COLORS);
heman_image* hmap = heman_generate_planet_heightmap(HEIGHT * 2, HEIGHT, SEED);
heman_image* albedo = heman_color_apply_gradient(hmap, -0.5, 0.5, grad);
heman_image_destroy(grad);
heman_image* planet =
heman_lighting_apply(hmap, albedo, 1, 1, 0.75, LIGHTPOS);
heman_image_destroy(hmap);
heman_image_destroy(albedo);
hut_write_image(OUTFOLDER "planet.png", planet, 0, 1);
heman_image_destroy(planet);
}

0 comments on commit b42794a

Please sign in to comment.