reorganize

git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@191 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
This commit is contained in:
Michaël Lemaire 2011-12-10 13:25:22 +00:00 committed by ThunderK
commit 763e75f77e
46 changed files with 8316 additions and 0 deletions

26
CMakeLists.txt Normal file
View file

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.8)
project(paysages)
include(FindPkgConfig)
find_package(DevIL)
pkg_check_modules(GTK3 gtk+-3.0)
include_directories(${IL_INCLUDE_DIR})
include_directories(${GTK3_INCLUDE_DIRS})
aux_source_directory(src SOURCE_FILES)
aux_source_directory(src/gui SOURCE_FILES)
#set(CMAKE_C_FLAGS "${GTK3_CFLAGS_OTHER}")
set(CMAKE_C_FLAGS_PROFILE "-pg -O0")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE debug)
endif(NOT CMAKE_BUILD_TYPE)
add_executable(paysages ${SOURCE_FILES})
target_link_libraries(paysages ${IL_LIBRARIES} ${ILU_LIBRARIES} ${GTK3_LIBRARIES})

23
config.vim Normal file
View file

@ -0,0 +1,23 @@
function! RunDebug()
silent !ctags src/*.c src/shared/*.h
silent !cmake -D CMAKE_BUILD_TYPE:STRING=Debug .
!make && ./paysages
endfunction
function! RunRelease()
silent !ctags src/*.c src/shared/*.h
silent !cmake -D CMAKE_BUILD_TYPE:STRING=Release .
!make && ./paysages
endfunction
function! RunProfile()
silent !ctags src/*.c src/shared/*.h
silent !cmake -D CMAKE_BUILD_TYPE:STRING=Profile .
!make && ./paysages && gprof
endfunction
map <silent> <F9> :call RunDebug()<CR>
map <silent> <C-F9> :call RunProfile()<CR>
map <silent> <A-F9> :call RunRelease()<CR>
map <silent> <F10> :!eog output/resultaa.png<CR>

1525
data/gui.glade Normal file

File diff suppressed because it is too large Load diff

BIN
data/textures/grass1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
data/textures/rock1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
data/textures/rock2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 KiB

BIN
data/textures/rock3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
data/textures/rock4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
data/textures/rock5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

BIN
data/textures/rock6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
data/textures/rock7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
data/textures/snow1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
data/textures/white.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

107
src/array.c Normal file
View file

@ -0,0 +1,107 @@
#include "shared/types.h"
#include "shared/functions.h"
#include <stdlib.h>
#include <string.h>
void arrayCreate(Array* array, int item_size)
{
array->length = 0;
array->alloc_length = 3;
array->item_size = item_size;
array->dirty = 1;
array->data = malloc((size_t)item_size * 3);
}
void arrayDelete(Array* array)
{
free(array->data);
array->data = NULL;
}
void* arrayAppend(Array* array, void* item)
{
void* dest;
size_t item_size = (size_t)array->item_size;
if (array->length >= array->alloc_length)
{
array->alloc_length += 10;
array->data = realloc(array->data, item_size * array->alloc_length);
}
dest = array->data + item_size * array->length;
memcpy(dest, item, item_size);
array->length++;
array->dirty = 1;
return dest;
}
void arrayInsert(Array* array, void* item, int position)
{
size_t item_size;
void* dest;
if (position >= array->length)
{
arrayAppend(array, item);
}
else if (position >= 0)
{
item_size = (size_t)array->item_size;
if (array->length >= array->alloc_length)
{
array->alloc_length += 10;
array->data = realloc(array->data, item_size * array->alloc_length);
}
dest = array->data + item_size * position;
memmove(dest + item_size, dest, array->length - position);
memcpy(array->data + item_size * position, item, item_size);
array->length++;
array->dirty = 1;
}
}
void arrayReplace(Array* array, void* item, int position)
{
size_t item_size;
if (position >= 0 && position < array->length)
{
item_size = (size_t)array->item_size;
memcpy(array->data + item_size * position, item, item_size);
array->dirty = 1;
}
}
void arrayLStrip(Array* array, int count)
{
size_t item_size;
if (count >= array->length)
{
arrayClear(array);
}
else if (count >= 0)
{
item_size = (size_t)array->item_size;
memmove(array->data, array->data + item_size * count, item_size * (array->length - count));
array->length -= count;
array->dirty = 1;
}
}
void arrayClear(Array* array)
{
free(array->data);
array->length = 0;
array->alloc_length = 3;
array->data = malloc((size_t)array->item_size * 3);
array->dirty = 1;
}

336
src/auto.c Normal file
View file

@ -0,0 +1,336 @@
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/constants.h"
#include "shared/globals.h"
#include "shared/system.h"
#include "water.h"
#include "clouds.h"
static int _cpu_count = 1;
static int _is_rendering = 0;
void autoInit()
{
_cpu_count = (int)sysconf(_SC_NPROCESSORS_ONLN);
renderSetBackgroundColor(&COLOR_BLACK);
terrainInit();
waterInit();
}
void autoSave(char* filepath)
{
FILE* f = fopen(filepath, "wb");
texturesSave(f);
cameraSave(f);
cloudsSave(f);
fogSave(f);
lightingSave(f);
renderSave(f);
skySave(f);
terrainSave(f);
waterSave(f);
fclose(f);
}
void autoLoad(char* filepath)
{
FILE* f = fopen(filepath, "rb");
texturesLoad(f);
cameraLoad(f);
cloudsLoad(f);
fogLoad(f);
lightingLoad(f);
renderLoad(f);
skyLoad(f);
terrainLoad(f);
waterLoad(f);
fclose(f);
}
void autoSetDaytime(int hour, int minute)
{
autoSetDaytimeFraction((double)hour / 24.0 + (double)minute / 1440.0);
}
void autoSetDaytimeFraction(double daytime)
{
daytime = fmod(daytime, 1.0);
if (daytime < 0.0)
{
daytime += 1.0;
}
ColorGradation grad_zenith, grad_haze, grad_sky, grad_sun;
Color sun, zenith, haze;
lightingSetSunAngle(0.0, (daytime + 0.25) * M_PI * 2.0);
grad_sun = colorGradationCreate();
colorGradationAddRgba(&grad_sun, 0.2, 0.1, 0.1, 0.1, 1.0);
colorGradationAddRgba(&grad_sun, 0.25, 0.9, 0.5, 0.5, 1.0);
colorGradationAddRgba(&grad_sun, 0.3, 0.8, 0.8, 0.8, 1.0);
colorGradationAddRgba(&grad_sun, 0.5, 1.0, 1.0, 1.0, 1.0);
colorGradationAddRgba(&grad_sun, 0.7, 0.8, 0.8, 0.8, 1.0);
colorGradationAddRgba(&grad_sun, 0.75, 0.7, 0.6, 0.5, 1.0);
colorGradationAddRgba(&grad_sun, 0.8, 0.1, 0.1, 0.1, 1.0);
sun = colorGradationGet(&grad_sun, daytime);
lightingSetSunColor(sun);
grad_zenith = colorGradationCreate();
colorGradationAddRgba(&grad_zenith, 0.2, 0.03, 0.03, 0.05, 1.0);
colorGradationAddRgba(&grad_zenith, 0.25, 0.25, 0.33, 0.37, 1.0);
colorGradationAddRgba(&grad_zenith, 0.35, 0.52, 0.63, 0.8, 1.0);
colorGradationAddRgba(&grad_zenith, 0.65, 0.52, 0.63, 0.8, 1.0);
colorGradationAddRgba(&grad_zenith, 0.75, 0.25, 0.33, 0.37, 1.0);
colorGradationAddRgba(&grad_zenith, 0.8, 0.03, 0.03, 0.05, 1.0);
zenith = colorGradationGet(&grad_zenith, daytime);
grad_haze = colorGradationCreate();
colorGradationAddRgba(&grad_haze, 0.2, 0.05, 0.05, 0.08, 1.0);
colorGradationAddRgba(&grad_haze, 0.25, 0.55, 0.42, 0.42, 1.0);
colorGradationAddRgba(&grad_haze, 0.3, 0.6, 0.6, 0.6, 1.0);
colorGradationAddRgba(&grad_haze, 0.4, 0.92, 0.93, 1.0, 1.0);
colorGradationAddRgba(&grad_haze, 0.6, 0.92, 0.93, 1.0, 1.0);
colorGradationAddRgba(&grad_haze, 0.7, 0.6, 0.6, 0.8, 1.0);
colorGradationAddRgba(&grad_haze, 0.75, 0.62, 0.50, 0.42, 1.0);
colorGradationAddRgba(&grad_haze, 0.8, 0.05, 0.05, 0.08, 1.0);
haze = colorGradationGet(&grad_haze, daytime);
grad_sky = colorGradationCreate();
colorGradationAdd(&grad_sky, 0.0, &haze);
colorGradationAdd(&grad_sky, 0.45, &haze);
colorGradationAdd(&grad_sky, 0.75, &zenith);
colorGradationAdd(&grad_sky, 1.0, &zenith);
skySetGradation(grad_sky);
fogSetColor(haze);
}
void autoSetRenderQuality(int quality)
{
WaterQuality water;
CloudsQuality clouds;
if (quality < 1)
{
quality = 1;
}
if (quality > 10)
{
quality = 10;
}
renderSetQuality(quality);
terrainSetChunkSize(0.1 / (double)render_quality, 0.05 / (double)render_quality);
water.detail_boost = 5.0;
water.force_detail = 0.0;
waterSetQuality(water);
clouds.precision = 3.3 - 0.3 * (double)render_quality;
cloudsSetQuality(clouds);
}
void autoGenRealisticLandscape(int seed)
{
Texture* tex;
WaterDefinition water;
CloudsDefinition cloud;
int layer;
HeightModifier* mod;
Zone* zone;
NoiseGenerator* noise;
if (!seed)
{
seed = time(NULL);
}
srand(seed);
/* Cloud layer */
cloud = cloudsCreateDefinition();
cloud.ymin = 10.0;
cloud.ycenter = 40.0;
cloud.ymax = 100.0;
cloud.color = COLOR_WHITE;
cloud.color.a = 0.1;
cloud.scaling = 50.0;
cloud.coverage = 0.5;
noiseGenerateBaseNoise(cloud.noise, 262144);
noiseAddLevelSimple(cloud.noise, 50.0, 0.3);
noiseAddLevelSimple(cloud.noise, 50.0 / 2.0, 0.2);
noiseAddLevelSimple(cloud.noise, 50.0 / 4.0, 0.1);
noiseAddLevelSimple(cloud.noise, 50.0 / 10.0, 0.05);
noiseAddLevelSimple(cloud.noise, 50.0 / 20.0, 0.03);
noiseAddLevelSimple(cloud.noise, 50.0 / 40.0, 0.02);
noiseAddLevelSimple(cloud.noise, 50.0 / 60.0, 0.01);
noiseAddLevelSimple(cloud.noise, 50.0 / 80.0, 0.005);
noiseAddLevelSimple(cloud.noise, 50.0 / 100.0, 0.02);
noiseAddLevelSimple(cloud.noise, 50.0 / 150.0, 0.005);
noiseAddLevelSimple(cloud.noise, 50.0 / 200.0, 0.003);
noiseAddLevelSimple(cloud.noise, 50.0 / 400.0, 0.008);
noiseAddLevelSimple(cloud.noise, 50.0 / 800.0, 0.001);
noiseAddLevelSimple(cloud.noise, 50.0 / 1000.0, 0.0005);
layer = cloudsAddLayer();
cloudsSetDefinition(layer, cloud);
/* Water */
water.height = 0.0;
water.transparency = 0.4;
water.reflection = 0.5;
water.main_color.r = 0.1;
water.main_color.g = 0.3;
water.main_color.b = 0.4;
water.main_color.a = 1.0;
water.height_noise = noiseCreateGenerator();
noiseGenerateBaseNoise(water.height_noise, 262144);
noiseAddLevelsSimple(water.height_noise, 2, 0.2, 0.015);
noiseAddLevelsSimple(water.height_noise, 3, 0.03, 0.003);
waterSetDefinition(water);
noiseDeleteGenerator(water.height_noise);
noise = noiseCreateGenerator();
noiseGenerateBaseNoise(noise, 1048576);
noiseAddLevelsSimple(noise, 10, 10.0, 1.0);
noiseNormalizeHeight(noise, -12.0, 12.0, 0);
terrainSetNoiseGenerator(noise);
noiseDeleteGenerator(noise);
tex = textureCreateFromFile("./data/textures/rock3.jpg");
tex->scaling_x = 0.003;
tex->scaling_y = 0.003;
tex->scaling_z = 0.003;
zone = zoneCreate(1.0);
terrainAddTexture(tex, 0.05, zone, 1.0);
tex = textureCreateFromFile("./data/textures/grass1.jpg");
tex->scaling_x = 0.0004;
tex->scaling_y = 0.0004;
tex->scaling_z = 0.0004;
zone = zoneCreate(0.0);
zoneAddHeightRange(zone, 1.0, -1.0, 0.0, 3.0, 15.0);
zoneAddSteepnessRange(zone, 1.0, 0.0, 0.0, 0.3, 0.4);
terrainAddTexture(tex, 0.15, zone, 0.05);
/*tex = textureCreateFromFile("./data/textures/snow1.jpg");
tex->scaling_x = 0.001;
tex->scaling_y = 0.001;
tex->scaling_z = 0.001;
zone = zoneCreate(0.0);
zoneAddHeightRange(zone, 1.0, 3.0, 4.0, 100.0, 100.0);
terrainAddTexture(tex, 0.5, zone, 0.1);*/
/* DEBUG */
mod = modifierCreate();
zone = modifierGetZone(mod);
zoneIncludeCircleArea(zone, 0.4, 0.0, 0.0, 8.0, 20.0);
modifierActionFixValue(mod, -2.0);
terrainAddModifier(mod);
mod = modifierCreate();
zone = modifierGetZone(mod);
zoneIncludeCircleArea(zone, 1.0, 0.0, 0.0, 0.3, 8.0);
modifierActionAddValue(mod, 8.0);
terrainAddModifier(mod);
mod = modifierCreate();
zone = modifierGetZone(mod);
zoneIncludeCircleArea(zone, 0.8, 0.0, 0.0, 0.3, 4.0);
modifierActionFixValue(mod, -8.0);
terrainAddModifier(mod);
fogSetDistance(20.0, 100.0);
}
void* _renderFirstPass(void* data)
{
if (!renderSetNextProgressStep(0.0, 0.1))
{
_is_rendering = 0;
return NULL;
}
skyRender(renderTellProgress);
if (!renderSetNextProgressStep(0.1, 0.3))
{
_is_rendering = 0;
return NULL;
}
terrainRender(renderTellProgress);
if (!renderSetNextProgressStep(0.3, 0.4))
{
_is_rendering = 0;
return NULL;
}
waterRender(renderTellProgress);
_is_rendering = 0;
return NULL;
}
void autoRenderSceneTwoPass(int postonly)
{
Thread* thread;
int loops;
if (!postonly)
{
renderClear();
_is_rendering = 1;
thread = threadCreate(_renderFirstPass, NULL);
loops = 0;
while (_is_rendering)
{
timeSleepMs(100);
if (++loops >= 10)
{
renderUpdate();
loops = 0;
}
}
threadJoin(thread);
}
if (renderSetNextProgressStep(0.4, 1.0))
{
renderPostProcess(_cpu_count);
}
}
static int _postProcessRayTracingOverlay(RenderFragment* fragment)
{
Vector3 terrain_hit, look;
look = v3Sub(fragment->vertex.location, camera_location);
if (!terrainProjectRay(camera_location, look, &terrain_hit, &fragment->vertex.color))
{
fragment->vertex.color = skyProjectRay(camera_location, look);
}
return 1;
}
void autoRenderSceneRayTracing()
{
renderClear();
cameraPushOverlay(COLOR_RED, _postProcessRayTracingOverlay);
renderUpdate();
if (renderSetNextProgressStep(0.0, 1.0))
{
renderPostProcess(_cpu_count);
}
}

120
src/camera.c Normal file
View file

@ -0,0 +1,120 @@
#include <stdlib.h>
#include <math.h>
#include "shared/types.h"
#include "shared/globals.h"
#include "shared/constants.h"
#include "shared/functions.h"
Vector3 camera_location;
static Vector3 camera_target;
static Vector3 camera_up;
static Matrix4 matrix_project;
static Matrix4 matrix_project_inverse;
static void __updateMatrix()
{
/* TODO Recompute up vector */
camera_up.x = 0.0;
camera_up.y = 1.0;
camera_up.z = 0.0;
matrix_project = m4Mult(m4NewPerspective(1.57, 1.333333, 1.0, 1000.0), m4NewLookAt(VECTOR_ZERO, v3Sub(camera_target, camera_location), camera_up));
matrix_project_inverse = m4Inverse(matrix_project);
}
void cameraSave(FILE* f)
{
}
void cameraLoad(FILE* f)
{
}
void cameraSetLocation(double x, double y, double z)
{
camera_location.x = x;
camera_location.y = y;
camera_location.z = z;
__updateMatrix();
}
void cameraSetTarget(double x, double y, double z)
{
camera_target.x = x;
camera_target.y = y;
camera_target.z = z;
__updateMatrix();
}
void cameraSetAngle(double angle)
{
/* TODO */
}
Vector3 cameraProject(Vector3 point)
{
point = m4Transform(matrix_project, v3Sub(point, camera_location));
point.x = (-point.x + 1.0) * 0.5 * (double)render_width;
point.y = (-point.y + 1.0) * 0.5 * (double)render_height;
return point;
}
Vector3 cameraUnproject(Vector3 point)
{
point.x = -(point.x / (0.5 * (double)render_width) - 1.0);
point.y = -(point.y / (0.5 * (double)render_height) - 1.0);
return v3Add(m4Transform(matrix_project_inverse, point), camera_location);
}
void cameraProjectToFragment(double x, double y, double z, RenderFragment* result)
{
Vector3 point = {x, y, z};
point = m4Transform(matrix_project, v3Sub(point, camera_location));
result->x = lround((-point.x + 1.0) * 0.5 * (double)render_width);
result->y = lround((-point.y + 1.0) * 0.5 * (double)render_height);
result->z = point.z;
}
/**
* Render a quad that will fill the view in front of the camera.
* This quad can be used for post-processing.
*
* @param col Color of the polygon.
* @param callback Post-processing callback.
*/
void cameraPushOverlay(Color col, f_RenderFragmentCallback callback)
{
Vertex v1, v2, v3, v4;
Vector3 v;
v.x = 0.0;
v.y = 0.0;
v.z = 10.0;
v1.location = cameraUnproject(v);
v1.color = col;
v1.callback = callback;
v.x = 0.0;
v.y = (double)render_height;
v.z = 10.0;
v2.location = cameraUnproject(v);
v2.color = col;
v2.callback = callback;
v.x = (double)render_width;
v.y = (double)render_height;
v.z = 10.0;
v3.location = cameraUnproject(v);
v3.color = col;
v3.callback = callback;
v.x = (double)render_width;
v.y = 0.0;
v.z = 10.0;
v4.location = cameraUnproject(v);
v4.color = col;
v4.callback = callback;
renderPushQuad(&v1, &v2, &v3, &v4);
}

492
src/clouds.c Normal file
View file

@ -0,0 +1,492 @@
#include <math.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/constants.h"
#include "shared/globals.h"
#include "clouds.h"
#define MAX_LAYERS 10
static int _layers_count = 0;
static CloudsDefinition _layers[MAX_LAYERS];
static CloudsQuality _quality;
static CloudsEnvironment _environment;
typedef struct
{
Vector3 start;
Vector3 end;
double length;
} CloudSegment;
void cloudsInit()
{
_layers_count = 0;
_quality.precision = 0.5;
}
void cloudsSave(FILE* f)
{
int i;
CloudsDefinition* layer;
toolsSaveInt(f, _layers_count);
for (i = 0; i < _layers_count; i++)
{
layer = _layers + i;
toolsSaveDouble(f, layer->ycenter);
toolsSaveDouble(f, layer->ymin);
toolsSaveDouble(f, layer->ymax);
noiseSave(layer->noise, f);
colorSave(layer->color, f);
toolsSaveDouble(f, layer->scaling);
toolsSaveDouble(f, layer->coverage);
}
}
void cloudsLoad(FILE* f)
{
int i;
CloudsDefinition* layer;
_layers_count = toolsLoadInt(f);
for (i = 0; i < _layers_count; i++)
{
layer = _layers + i;
layer->ycenter = toolsLoadDouble(f);
layer->ymin = toolsLoadDouble(f);
layer->ymax = toolsLoadDouble(f);
noiseLoad(layer->noise, f);
layer->color = colorLoad(f);
layer->scaling = toolsLoadDouble(f);
layer->coverage = toolsLoadDouble(f);
}
}
int cloudsGetLayerCount()
{
return _layers_count;
}
int cloudsAddLayer()
{
CloudsDefinition* layer;
layer = _layers + _layers_count;
layer->noise = noiseCreateGenerator();
layer->coverage = 0.0;
return _layers_count++;
}
void cloudsDeleteLayer(int layer)
{
/* TODO */
}
CloudsDefinition cloudsCreateDefinition()
{
CloudsDefinition result;
result.color = COLOR_WHITE;
result.coverage = 0.0;
result.noise = noiseCreateGenerator();
result.scaling = 1.0;
result.ymin = 0.0;
result.ycenter = 0.0;
result.ymax = 0.0;
return result;
}
void cloudsDeleteDefinition(CloudsDefinition definition)
{
noiseDeleteGenerator(definition.noise);
}
void cloudsCopyDefinition(CloudsDefinition source, CloudsDefinition* destination)
{
NoiseGenerator* noise;
noise = destination->noise;
*destination = source;
destination->noise = noise;
noiseCopy(source.noise, destination->noise);
}
void cloudsSetDefinition(int layer, CloudsDefinition definition)
{
CloudsDefinition* destination;
if (layer >= 0 && layer < _layers_count)
{
destination = _layers + layer;
cloudsCopyDefinition(definition, destination);
if (destination->coverage < 0.5)
{
noiseNormalizeHeight(destination->noise, -1.0, destination->coverage * 2.0, 0);
}
else
{
noiseNormalizeHeight(destination->noise, -(1.0 - destination->coverage) * 2.0, 1.0, 0);
}
}
}
CloudsDefinition cloudsGetDefinition(int layer)
{
return _layers[layer];
}
void cloudsSetQuality(CloudsQuality quality)
{
_quality = quality;
}
CloudsQuality cloudsGetQuality()
{
return _quality;
}
/*int cloudsAddLayer(double ymin, double ycenter, double ymax, Color color, double scaling, double coverage)
{
CloudsDefinition* layer = _layers + _layers_count;
layer->ycenter = ycenter;
layer->ymin = ymin;
layer->ymax = ymax;
layer->color = color;
layer->scaling = scaling;
layer->coverage = coverage;
layer->noise = noiseCreateGenerator();
noiseGenerateBaseNoise(layer->noise, 262144);
noiseAddLevelSimple(layer->noise, scaling, 0.3);
noiseAddLevelSimple(layer->noise, scaling / 2.0, 0.2);
noiseAddLevelSimple(layer->noise, scaling / 4.0, 0.1);
noiseAddLevelSimple(layer->noise, scaling / 10.0, 0.05);
noiseAddLevelSimple(layer->noise, scaling / 20.0, 0.03);
noiseAddLevelSimple(layer->noise, scaling / 40.0, 0.02);
noiseAddLevelSimple(layer->noise, scaling / 60.0, 0.01);
noiseAddLevelSimple(layer->noise, scaling / 80.0, 0.005);
noiseAddLevelSimple(layer->noise, scaling / 100.0, 0.02);
noiseAddLevelSimple(layer->noise, scaling / 150.0, 0.005);
noiseAddLevelSimple(layer->noise, scaling / 200.0, 0.003);
noiseAddLevelSimple(layer->noise, scaling / 400.0, 0.008);
noiseAddLevelSimple(layer->noise, scaling / 800.0, 0.001);
noiseAddLevelSimple(layer->noise, scaling / 1000.0, 0.0005);
if (coverage < 0.5)
{
noiseNormalizeHeight(layer->noise, -1.0, coverage * 2.0, 0);
}
else
{
noiseNormalizeHeight(layer->noise, -(1.0 - coverage) * 2.0, 1.0, 0);
}
return _layers_count++;
}*/
static inline double _getDistanceToBorder(CloudsDefinition* layer, Vector3 position, double detail)
{
double val, min;
if (position.y > layer->ycenter)
{
min = (position.y - layer->ycenter) / (layer->ymax - layer->ycenter);
}
else
{
min = (layer->ycenter - position.y) / (layer->ycenter - layer->ymin);
}
val = noiseGet3DDetail(layer->noise, position.x, position.y, position.z, detail);
return (val - min) * layer->scaling;
}
static inline Vector3 _getNormal(CloudsDefinition* layer, Vector3 position, double detail)
{
Vector3 result = {0.0, 0.0, 0.0};
double val, dval;
val = noiseGet3DDetail(layer->noise, position.x, position.y, position.z, detail);
dval = val - noiseGet3DDetail(layer->noise, position.x + detail, position.y, position.z, detail);
result.x += dval;
dval = val - noiseGet3DDetail(layer->noise, position.x - detail, position.y, position.z, detail);
result.x -= dval;
dval = val - noiseGet3DDetail(layer->noise, position.x, position.y + detail, position.z, detail);
result.y += dval;
dval = val - noiseGet3DDetail(layer->noise, position.x, position.y - detail, position.z, detail);
result.y -= dval;
dval = val - noiseGet3DDetail(layer->noise, position.x, position.y, position.z + detail, detail);
result.z += dval;
dval = val - noiseGet3DDetail(layer->noise, position.x, position.y, position.z - detail, detail);
result.z -= dval;
return result;
}
/**
* Optimize the search limits in a layer.
*
* @param layer The cloud layer
* @param start Start of the search to optimize
* @param end End of the search to optimize
* @return 0 if the search is useless
*/
static int _optimizeSearchLimits(CloudsDefinition* layer, Vector3* start, Vector3* end)
{
Vector3 diff;
if (start->y > layer->ymax)
{
if (end->y >= layer->ymax)
{
return 0;
}
else
{
diff = v3Sub(*end, *start);
*start = v3Add(*start, v3Scale(diff, (layer->ymax - start->y) / diff.y));
if (end->y < layer->ymin)
{
*end = v3Add(*end, v3Scale(diff, (layer->ymin - end->y) / diff.y));
}
}
}
else if (start->y < layer->ymin)
{
if (end->y <= layer->ymin)
{
return 0;
}
else
{
diff = v3Sub(*end, *start);
*start = v3Add(*start, v3Scale(diff, (layer->ymin - start->y) / diff.y));
if (end->y > layer->ymax)
{
*end = v3Add(*end, v3Scale(diff, (layer->ymax - end->y) / diff.y));
}
}
}
else /* start is inside layer */
{
diff = v3Sub(*end, *start);
if (end->y > layer->ymax)
{
*end = v3Add(*start, v3Scale(diff, (layer->ymax - start->y) / diff.y));
}
else if (end->y < layer->ymin)
{
*end = v3Add(*start, v3Scale(diff, (layer->ymin - start->y) / diff.y));
}
}
/* TODO Limit the search length */
return 1;
}
/**
* Go through the cloud layer to find segments (parts of the lookup that are inside the cloud).
*
* @param definition The cloud layer
* @param quality Render quality
* @param start Start position of the lookup (already optimized)
* @param direction Normalized direction of the lookup
* @param detail Level of noise detail required
* @param max_segments Maximum number of segments to collect
* @param max_inside_length Maximum length to spend inside the cloud
* @param max_total_length Maximum lookup length
* @param inside_length Resulting length inside cloud (sum of all segments length)
* @param total_length Resulting lookup length
* @param out_segments Allocated space to fill found segments
* @return Number of segments found
*/
static int _findSegments(CloudsDefinition* definition, CloudsQuality* quality, Vector3 start, Vector3 direction, double detail, int max_segments, double max_inside_length, double max_total_length, double* inside_length, double* total_length, CloudSegment* out_segments)
{
int inside, segment_count;
double current_total_length, current_inside_length;
double step_length, segment_length, remaining_length;
double noise_distance, last_noise_distance;
Vector3 walker, step, segment_start;
if (max_segments <= 0)
{
return 0;
}
segment_count = 0;
current_total_length = 0.0;
current_inside_length = 0.0;
segment_length = 0.0;
walker = start;
noise_distance = _getDistanceToBorder(definition, start, detail) * quality->precision;
inside = (noise_distance > 0.0) ? 1 : 0;
step = v3Scale(direction, quality->precision);
do
{
walker = v3Add(walker, step);
step_length = v3Norm(step);
last_noise_distance = noise_distance;
noise_distance = _getDistanceToBorder(definition, walker, detail) * quality->precision;
current_total_length += step_length;
if (noise_distance > 0.0)
{
if (inside)
{
// inside the cloud
segment_length += step_length;
current_inside_length += step_length;
step = v3Scale(direction, (noise_distance < quality->precision) ? quality->precision : noise_distance);
}
else
{
// entering the cloud
inside = 1;
segment_length = step_length * noise_distance / (noise_distance - last_noise_distance);
segment_start = v3Add(walker, v3Scale(direction, -segment_length));
current_inside_length += segment_length;
step = v3Scale(direction, quality->precision);
}
}
else
{
if (inside)
{
// exiting the cloud
remaining_length = step_length * last_noise_distance / (last_noise_distance - noise_distance);
segment_length += remaining_length;
current_inside_length += remaining_length;
out_segments->start = segment_start;
out_segments->end = v3Add(walker, v3Scale(direction, remaining_length - step_length));
out_segments->length = segment_length;
out_segments++;
if (++segment_count >= max_segments)
{
break;
}
inside = 0;
step = v3Scale(direction, quality->precision);
}
else
{
// searching for a cloud
step = v3Scale(direction, (noise_distance > -quality->precision) ? quality->precision : -noise_distance);
}
}
} while (inside || (walker.y <= definition->ymax + 0.001 && walker.y >= definition->ymin - 0.001 && current_total_length < max_total_length && current_inside_length < max_inside_length));
*total_length = current_total_length;
*inside_length = current_inside_length;
return segment_count;
}
static Color _applyLayerLighting(CloudsDefinition* definition, CloudsQuality* quality, Vector3 position, Color base, double detail)
{
Vector3 direction, normal;
double inside_depth, total_depth;
CloudSegment segments[20];
Color result;
normal = _getNormal(definition, position, 0.5);
result = lightingApply(position, normal, 0.0, base, 0.3, 0.1);
direction = sun_direction_inv;
detail = (detail < 0.1) ? 0.1 : detail;
/* FIXME Dont hard-code max_total_length */
_findSegments(definition, quality, position, direction, detail, 20, 50.0, 300.0, &inside_depth, &total_depth, segments);
inside_depth *= 0.02;
if (inside_depth > 1.0)
{
inside_depth = 1.0;
}
result.r = base.r * sun_color_lum * (0.9 - 0.2 * inside_depth) + result.r * 0.2 * inside_depth + (0.1 - inside_depth * 0.1) * sun_color_lum;
result.g = base.g * sun_color_lum * (0.9 - 0.2 * inside_depth) + result.g * 0.2 * inside_depth + (0.1 - inside_depth * 0.1) * sun_color_lum;
result.b = base.b * sun_color_lum * (0.9 - 0.2 * inside_depth) + result.b * 0.2 * inside_depth + (0.1 - inside_depth * 0.1) * sun_color_lum;
return result;
}
Color cloudsGetColorCustom(Vector3 start, Vector3 end, CloudsDefinition* definition, CloudsQuality* quality, CloudsEnvironment* environment)
{
int i, segment_count;
double max_length, detail, total_length, inside_length;
Vector3 direction;
Color result, col;
CloudSegment segments[20];
if (quality == NULL)
{
quality = &_quality;
}
if (environment == NULL)
{
environment = &_environment;
}
if (!_optimizeSearchLimits(definition, &start, &end))
{
return COLOR_TRANSPARENT;
}
direction = v3Sub(end, start);
max_length = v3Norm(direction);
direction = v3Normalize(direction);
result = COLOR_TRANSPARENT;
/* TODO Flexible precision */
detail = renderGetPrecision(start) / definition->scaling;
segment_count = _findSegments(definition, quality, start, direction, detail, 20, 60.0, max_length, &inside_length, &total_length, segments);
for (i = 0; i < segment_count; i++)
{
col = _applyLayerLighting(definition, quality, segments[i].start, definition->color, detail);
col.a = (segments[i].length >= 50.0) ? 1.0 : (segments[i].length / 50.0);
colorMask(&result, &col);
}
return result;
}
Color cloudsGetColor(Vector3 start, Vector3 end)
{
int i;
Color layer_color, result;
if (end.y < start.y)
{
return cloudsGetColor(end, start);
}
if (_layers_count < 1 || end.y - start.y < 0.001)
{
return COLOR_TRANSPARENT;
}
result = COLOR_TRANSPARENT;
for (i = 0; i < _layers_count; i++)
{
layer_color = cloudsGetColorCustom(start, end, _layers + i, &_quality, &_environment);
if (layer_color.a > 0.0)
{
colorMask(&result, &layer_color);
}
}
return result;
}

48
src/clouds.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef _PAYSAGES_CLOUDS_H_
#define _PAYSAGES_CLOUDS_H_
#include "shared/types.h"
#include <stdio.h>
typedef struct
{
double ycenter;
double ymin;
double ymax;
NoiseGenerator* noise;
Color color;
double scaling;
double coverage;
} CloudsDefinition;
typedef struct
{
double precision;
} CloudsQuality;
typedef struct
{
int unused;
} CloudsEnvironment;
void cloudsInit();
void cloudsSave(FILE* f);
void cloudsLoad(FILE* f);
int cloudsGetLayerCount();
int cloudsAddLayer();
void cloudsDeleteLayer(int layer);
CloudsDefinition cloudsCreateDefinition();
void cloudsDeleteDefinition(CloudsDefinition definition);
void cloudsCopyDefinition(CloudsDefinition source, CloudsDefinition* destination);
void cloudsSetDefinition(int layer, CloudsDefinition definition);
CloudsDefinition cloudsGetDefinition(int layer);
void cloudsSetQuality(CloudsQuality quality);
CloudsQuality cloudsGetQuality();
Color cloudsGetColorCustom(Vector3 start, Vector3 end, CloudsDefinition* definition, CloudsQuality* quality, CloudsEnvironment* environment);
Color cloudsGetColor(Vector3 start, Vector3 end);
#endif

184
src/color.c Normal file
View file

@ -0,0 +1,184 @@
#include <stdlib.h>
#include "shared/types.h"
#include "shared/constants.h"
#include "shared/functions.h"
Color COLOR_TRANSPARENT = {0.0, 0.0, 0.0, 0.0};
Color COLOR_BLACK = {0.0, 0.0, 0.0, 1.0};
Color COLOR_RED = {1.0, 0.0, 0.0, 1.0};
Color COLOR_GREEN = {0.0, 1.0, 0.0, 1.0};
Color COLOR_BLUE = {0.0, 0.0, 1.0, 1.0};
Color COLOR_WHITE = {1.0, 1.0, 1.0, 1.0};
Color COLOR_GREY = {0.5, 0.5, 0.5, 1.0};
void colorSave(Color col, FILE* f)
{
toolsSaveDouble(f, col.r);
toolsSaveDouble(f, col.g);
toolsSaveDouble(f, col.b);
toolsSaveDouble(f, col.a);
}
Color colorLoad(FILE* f)
{
Color col;
col.r = toolsLoadDouble(f);
col.g = toolsLoadDouble(f);
col.b = toolsLoadDouble(f);
col.a = toolsLoadDouble(f);
return col;
}
unsigned int colorTo32BitRGBA(Color* col)
{
return (((unsigned int)(col->a * 255.0)) << 24) | (((unsigned int)(col->b * 255.0)) << 16) | (((unsigned int)(col->g * 255.0)) << 8) | ((unsigned int)(col->r * 255.0));
}
unsigned int colorTo32BitBGRA(Color* col)
{
return (((unsigned int)(col->a * 255.0)) << 24) | (((unsigned int)(col->r * 255.0)) << 16) | (((unsigned int)(col->g * 255.0)) << 8) | ((unsigned int)(col->b * 255.0));
}
unsigned int colorTo32BitARGB(Color* col)
{
return (((unsigned int)(col->b * 255.0)) << 24) | (((unsigned int)(col->g * 255.0)) << 16) | (((unsigned int)(col->r * 255.0)) << 8) | ((unsigned int)(col->a * 255.0));
}
unsigned int colorTo32BitABGR(Color* col)
{
return (((unsigned int)(col->r * 255.0)) << 24) | (((unsigned int)(col->g * 255.0)) << 16) | (((unsigned int)(col->b * 255.0)) << 8) | ((unsigned int)(col->a * 255.0));
}
void colorMask(Color* base, Color* mask)
{
double new_a;
new_a = base->a + mask->a - (base->a * mask->a);
base->r = (mask->r * mask->a + base->r * base->a - base->r * base->a * mask->a) / new_a;
base->g = (mask->g * mask->a + base->g * base->a - base->g * base->a * mask->a) / new_a;
base->b = (mask->b * mask->a + base->b * base->a - base->b * base->a * mask->a) / new_a;
base->a = new_a;
/*double mask_weight = mask->a;
double base_weight = 1.0 - mask_weight;
base->r = mask->r * mask_weight + base->r * base_weight;
base->g = mask->g * mask_weight + base->g * base_weight;
base->b = mask->b * mask_weight + base->b * base_weight;
base->a = base->a + mask_weight * (1.0 - base->a);*/
}
double colorNormalize(Color* col)
{
double max = colorGetValue(col);
if (max > 1.0)
{
col->r /= max;
col->g /= max;
col->b /= max;
}
return max;
}
double colorGetValue(Color* col)
{
double max;
max = col->r;
if (col->g > max)
{
max = col->g;
}
if (col->b > max)
{
max = col->b;
}
return max;
}
ColorGradation colorGradationCreate()
{
ColorGradation result;
result.nbparts = 0;
return result;
}
int _part_compare(const void* part1, const void* part2)
{
if (((_ColorGradationPart*)part1)->start > ((_ColorGradationPart*)part2)->start)
{
return 1;
}
else
{
return -1;
}
}
void colorGradationAdd(ColorGradation* gradation, double value, Color* col)
{
if (gradation->nbparts == MAX_COLORGRADATION_PARTS)
{
return;
}
else
{
gradation->parts[gradation->nbparts].start = value;
gradation->parts[gradation->nbparts].col = *col;
if (gradation->nbparts++ > 1)
{
qsort(gradation->parts, gradation->nbparts, sizeof(_ColorGradationPart), _part_compare);
}
}
}
void colorGradationAddRgba(ColorGradation* gradation, double value, double r, double g, double b, double a)
{
Color col;
col.r = r;
col.g = g;
col.b = b;
col.a = a;
colorGradationAdd(gradation, value, &col);
}
Color colorGradationGet(ColorGradation* gradation, double value)
{
Color result;
int i;
double fact;
if (gradation->nbparts == 0)
{
return COLOR_TRANSPARENT;
}
else if (gradation->nbparts == 1 || value <= gradation->parts[0].start)
{
return gradation->parts[0].col;
}
else if (value >= gradation->parts[gradation->nbparts - 1].start)
{
return gradation->parts[gradation->nbparts - 1].col;
}
else
{
for (i = 1; i < gradation->nbparts; i++)
{
if (value < gradation->parts[i].start)
{
fact = (value - gradation->parts[i - 1].start) / (gradation->parts[i].start - gradation->parts[i - 1].start);
result.r = gradation->parts[i - 1].col.r + (gradation->parts[i].col.r - gradation->parts[i - 1].col.r) * fact;
result.g = gradation->parts[i - 1].col.g + (gradation->parts[i].col.g - gradation->parts[i - 1].col.g) * fact;
result.b = gradation->parts[i - 1].col.b + (gradation->parts[i].col.b - gradation->parts[i - 1].col.b) * fact;
result.a = gradation->parts[i - 1].col.a + (gradation->parts[i].col.a - gradation->parts[i - 1].col.a) * fact;
return result;
}
}
return gradation->parts[gradation->nbparts - 1].col;
}
}

409
src/euclid.c Normal file
View file

@ -0,0 +1,409 @@
#include <math.h>
#include "shared/functions.h"
#include "shared/types.h"
Vector3 VECTOR_ZERO = {0.0, 0.0, 0.0};
void v3Save(Vector3 v, FILE* f)
{
toolsSaveDouble(f, v.x);
toolsSaveDouble(f, v.y);
toolsSaveDouble(f, v.z);
}
Vector3 v3Load(FILE* f)
{
Vector3 result;
result.x = toolsLoadDouble(f);
result.y = toolsLoadDouble(f);
result.z = toolsLoadDouble(f);
return result;
}
void m4Save(Matrix4 m, FILE* f)
{
toolsSaveDouble(f, m.a);
toolsSaveDouble(f, m.b);
toolsSaveDouble(f, m.c);
toolsSaveDouble(f, m.d);
toolsSaveDouble(f, m.e);
toolsSaveDouble(f, m.f);
toolsSaveDouble(f, m.g);
toolsSaveDouble(f, m.h);
toolsSaveDouble(f, m.i);
toolsSaveDouble(f, m.j);
toolsSaveDouble(f, m.k);
toolsSaveDouble(f, m.l);
toolsSaveDouble(f, m.m);
toolsSaveDouble(f, m.n);
toolsSaveDouble(f, m.o);
toolsSaveDouble(f, m.p);
}
Matrix4 m4Load(FILE* f)
{
Matrix4 result;
result.a = toolsLoadDouble(f);
result.b = toolsLoadDouble(f);
result.c = toolsLoadDouble(f);
result.d = toolsLoadDouble(f);
result.e = toolsLoadDouble(f);
result.f = toolsLoadDouble(f);
result.g = toolsLoadDouble(f);
result.h = toolsLoadDouble(f);
result.i = toolsLoadDouble(f);
result.j = toolsLoadDouble(f);
result.k = toolsLoadDouble(f);
result.l = toolsLoadDouble(f);
result.m = toolsLoadDouble(f);
result.n = toolsLoadDouble(f);
result.o = toolsLoadDouble(f);
result.p = toolsLoadDouble(f);
return result;
}
Vector3 v3Translate(Vector3 v1, double x, double y, double z)
{
Vector3 result;
result.x = v1.x + x;
result.y = v1.y + y;
result.z = v1.z + z;
return result;
}
Vector3 v3Add(Vector3 v1, Vector3 v2)
{
Vector3 result;
result.x = v1.x + v2.x;
result.y = v1.y + v2.y;
result.z = v1.z + v2.z;
return result;
}
Vector3 v3Sub(Vector3 v1, Vector3 v2)
{
Vector3 result;
result.x = v1.x - v2.x;
result.y = v1.y - v2.y;
result.z = v1.z - v2.z;
return result;
}
Vector3 v3Neg(Vector3 v)
{
Vector3 result;
result.x = -v.x;
result.y = -v.y;
result.z = -v.z;
return result;
}
Vector3 v3Scale(Vector3 v, double scale)
{
Vector3 result;
result.x = v.x * scale;
result.y = v.y * scale;
result.z = v.z * scale;
return result;
}
double v3Norm(Vector3 v)
{
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
Vector3 v3Normalize(Vector3 v)
{
double norm = v3Norm(v);
if (norm == 0.0)
{
return VECTOR_ZERO;
}
else
{
return v3Scale(v, 1.0 / norm);
}
}
double v3Dot(Vector3 v1, Vector3 v2)
{
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
Vector3 v3Cross(Vector3 v1, Vector3 v2)
{
Vector3 result;
result.x = v1.y * v2.z - v1.z * v2.y;
result.y = v1.z * v2.x - v1.x * v2.z;
result.z = v1.x * v2.y - v1.y * v2.x;
return result;
}
Matrix4 m4NewIdentity()
{
Matrix4 result;
result.a = result.f = result.k = result.p = 1.0;
result.b = result.c = result.d = result.e = result.g = result.h = 0.0;
result.i = result.j = result.l = result.m = result.n = result.o = 0.0;
return result;
}
Matrix4 m4Mult(Matrix4 m1, Matrix4 m2)
{
Matrix4 result;
result.a = m1.a * m2.a + m1.b * m2.e + m1.c * m2.i + m1.d * m2.m;
result.b = m1.a * m2.b + m1.b * m2.f + m1.c * m2.j + m1.d * m2.n;
result.c = m1.a * m2.c + m1.b * m2.g + m1.c * m2.k + m1.d * m2.o;
result.d = m1.a * m2.d + m1.b * m2.h + m1.c * m2.l + m1.d * m2.p;
result.e = m1.e * m2.a + m1.f * m2.e + m1.g * m2.i + m1.h * m2.m;
result.f = m1.e * m2.b + m1.f * m2.f + m1.g * m2.j + m1.h * m2.n;
result.g = m1.e * m2.c + m1.f * m2.g + m1.g * m2.k + m1.h * m2.o;
result.h = m1.e * m2.d + m1.f * m2.h + m1.g * m2.l + m1.h * m2.p;
result.i = m1.i * m2.a + m1.j * m2.e + m1.k * m2.i + m1.l * m2.m;
result.j = m1.i * m2.b + m1.j * m2.f + m1.k * m2.j + m1.l * m2.n;
result.k = m1.i * m2.c + m1.j * m2.g + m1.k * m2.k + m1.l * m2.o;
result.l = m1.i * m2.d + m1.j * m2.h + m1.k * m2.l + m1.l * m2.p;
result.m = m1.m * m2.a + m1.n * m2.e + m1.o * m2.i + m1.p * m2.m;
result.n = m1.m * m2.b + m1.n * m2.f + m1.o * m2.j + m1.p * m2.n;
result.o = m1.m * m2.c + m1.n * m2.g + m1.o * m2.k + m1.p * m2.o;
result.p = m1.m * m2.d + m1.n * m2.h + m1.o * m2.l + m1.p * m2.p;
return result;
}
Vector3 m4MultPoint(Matrix4 m, Vector3 v)
{
Vector3 result;
result.x = m.a * v.x + m.b * v.y + m.c * v.z + m.d;
result.y = m.e * v.x + m.f * v.y + m.g * v.z + m.h;
result.z = m.i * v.x + m.j * v.y + m.k * v.z + m.l;
return result;
}
Vector3 m4Transform(Matrix4 m, Vector3 v)
{
Vector3 result;
double w;
result.x = m.a * v.x + m.b * v.y + m.c * v.z + m.d;
result.y = m.e * v.x + m.f * v.y + m.g * v.z + m.h;
result.z = m.i * v.x + m.j * v.y + m.k * v.z + m.l;
w = m.m * v.x + m.n * v.y + m.o * v.z + m.p;
if (w != 0.0)
{
result.x /= w;
result.y /= w;
result.z /= w;
}
return result;
}
Matrix4 m4Transpose(Matrix4 m)
{
Matrix4 result;
result.a = m.a;
result.e = m.b;
result.i = m.c;
result.m = m.d;
result.b = m.e;
result.f = m.f;
result.j = m.g;
result.n = m.h;
result.c = m.i;
result.g = m.j;
result.k = m.k;
result.o = m.l;
result.d = m.m;
result.h = m.n;
result.l = m.o;
result.p = m.p;
return result;
}
Matrix4 m4NewScale(double x, double y, double z)
{
Matrix4 result = m4NewIdentity();
result.a = x;
result.f = y;
result.k = z;
return result;
}
Matrix4 m4NewTranslate(double x, double y, double z)
{
Matrix4 result = m4NewIdentity();
result.d = x;
result.h = y;
result.l = z;
return result;
}
Matrix4 m4NewRotateX(double angle)
{
Matrix4 result = m4NewIdentity();
double s = sin(angle);
double c = cos(angle);
result.f = result.k = c;
result.g = -s;
result.j = s;
return result;
}
Matrix4 m4NewRotateY(double angle)
{
Matrix4 result = m4NewIdentity();
double s = sin(angle);
double c = cos(angle);
result.a = result.k = c;
result.c = s;
result.i = -s;
return result;
}
Matrix4 m4NewRotateZ(double angle)
{
Matrix4 result = m4NewIdentity();
double s = sin(angle);
double c = cos(angle);
result.a = result.f = c;
result.b = -s;
result.e = s;
return result;
}
Matrix4 m4NewRotateAxis(double angle, Vector3 axis)
{
Matrix4 result = m4NewIdentity();
double s = sin(angle);
double c = cos(angle);
double c1 = 1.0 - c;
axis = v3Normalize(axis);
result.a = axis.x * axis.x * c1 + c;
result.b = axis.x * axis.y * c1 - axis.z * s;
result.c = axis.x * axis.z * c1 + axis.y * s;
result.e = axis.y * axis.x * c1 + axis.z * s;
result.f = axis.y * axis.y * c1 + c;
result.g = axis.y * axis.z * c1 - axis.x * s;
result.i = axis.x * axis.z * c1 - axis.y * s;
result.j = axis.y * axis.z * c1 + axis.x * s;
result.k = axis.z * axis.z * c1 + c;
return result;
}
Matrix4 m4NewRotateEuler(double heading, double attitude, double bank)
{
Matrix4 result = m4NewIdentity();
double ch = cos(heading);
double sh = sin(heading);
double ca = cos(attitude);
double sa = sin(attitude);
double cb = cos(bank);
double sb = sin(bank);
result.a = ch * ca;
result.b = sh * sb - ch * sa * cb;
result.c = ch * sa * sb + sh * cb;
result.e = sa;
result.f = ca * cb;
result.g = -ca * sb;
result.i = -sh * ca;
result.j = sh * sa * cb + ch * sb;
result.k = -sh * sa * sb + ch * cb;
return result;
}
Matrix4 m4NewRotateTripleAxis(Vector3 x, Vector3 y, Vector3 z)
{
Matrix4 result = m4NewIdentity();
result.a = x.x;
result.b = y.x;
result.c = z.x;
result.e = x.y;
result.f = y.y;
result.g = z.y;
result.i = x.z;
result.j = y.z;
result.k = z.z;
return result;
}
Matrix4 m4NewLookAt(Vector3 eye, Vector3 at, Vector3 up)
{
Vector3 z = v3Normalize(v3Sub(eye, at));
Vector3 x = v3Normalize(v3Cross(up, z));
Vector3 y = v3Cross(z, x);
Matrix4 result = m4NewRotateTripleAxis(x, y, v3Neg(z));
result.d = eye.x;
result.h = eye.y;
result.l = eye.z;
return result;
}
Matrix4 m4NewPerspective(double fov_y, double aspect, double near, double far)
{
Matrix4 result = m4NewIdentity();
double f = 1 / tan(fov_y / 2.0);
result.a = f / aspect;
result.f = f;
result.k = (far + near) / (near - far);
result.l = 2.0 * far * near / (near - far);
result.o = -1.0;
result.p = 0.0;
return result;
}
double m4Determinant(Matrix4 m)
{
return ((m.a * m.f - m.e * m.b)
* (m.k * m.p - m.o * m.l)
- (m.a * m.j - m.i * m.b)
* (m.g * m.p - m.o * m.h)
+ (m.a * m.n - m.m * m.b)
* (m.g * m.l - m.k * m.h)
+ (m.e * m.j - m.i * m.f)
* (m.c * m.p - m.o * m.d)
- (m.e * m.n - m.m * m.f)
* (m.c * m.l - m.k * m.d)
+ (m.i * m.n - m.m * m.j)
* (m.c * m.h - m.g * m.d));
}
Matrix4 m4Inverse(Matrix4 m)
{
Matrix4 result;
double d = m4Determinant(m);
if (fabs(d) < 0.00001)
{
return m4NewIdentity();
}
else
{
d = 1.0 / d;
result.a = d * (m.f * (m.k * m.p - m.o * m.l) + m.j * (m.o * m.h - m.g * m.p) + m.n * (m.g * m.l - m.k * m.h));
result.e = d * (m.g * (m.i * m.p - m.m * m.l) + m.k * (m.m * m.h - m.e * m.p) + m.o * (m.e * m.l - m.i * m.h));
result.i = d * (m.h * (m.i * m.n - m.m * m.j) + m.l * (m.m * m.f - m.e * m.n) + m.p * (m.e * m.j - m.i * m.f));
result.m = d * (m.e * (m.n * m.k - m.j * m.o) + m.i * (m.f * m.o - m.n * m.g) + m.m * (m.j * m.g - m.f * m.k));
result.b = d * (m.j * (m.c * m.p - m.o * m.d) + m.n * (m.k * m.d - m.c * m.l) + m.b * (m.o * m.l - m.k * m.p));
result.f = d * (m.k * (m.a * m.p - m.m * m.d) + m.o * (m.i * m.d - m.a * m.l) + m.c * (m.m * m.l - m.i * m.p));
result.j = d * (m.l * (m.a * m.n - m.m * m.b) + m.p * (m.i * m.b - m.a * m.j) + m.d * (m.m * m.j - m.i * m.n));
result.n = d * (m.i * (m.n * m.c - m.b * m.o) + m.m * (m.b * m.k - m.j * m.c) + m.a * (m.j * m.o - m.n * m.k));
result.c = d * (m.n * (m.c * m.h - m.g * m.d) + m.b * (m.g * m.p - m.o * m.h) + m.f * (m.o * m.d - m.c * m.p));
result.g = d * (m.o * (m.a * m.h - m.e * m.d) + m.c * (m.e * m.p - m.m * m.h) + m.g * (m.m * m.d - m.a * m.p));
result.k = d * (m.p * (m.a * m.f - m.e * m.b) + m.d * (m.e * m.n - m.m * m.f) + m.h * (m.m * m.b - m.a * m.n));
result.o = d * (m.m * (m.f * m.c - m.b * m.g) + m.a * (m.n * m.g - m.f * m.o) + m.e * (m.b * m.o - m.n * m.c));
result.d = d * (m.b * (m.k * m.h - m.g * m.l) + m.f * (m.c * m.l - m.k * m.d) + m.j * (m.g * m.d - m.c * m.h));
result.h = d * (m.c * (m.i * m.h - m.e * m.l) + m.g * (m.a * m.l - m.i * m.d) + m.k * (m.e * m.d - m.a * m.h));
result.l = d * (m.d * (m.i * m.f - m.e * m.j) + m.h * (m.a * m.j - m.i * m.b) + m.l * (m.e * m.b - m.a * m.f));
result.p = d * (m.a * (m.f * m.k - m.j * m.g) + m.e * (m.j * m.c - m.b * m.k) + m.i * (m.b * m.g - m.f * m.c));
return result;
}
}

49
src/fog.c Normal file
View file

@ -0,0 +1,49 @@
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/globals.h"
#include "shared/constants.h"
static double _near = 0.0, _far = 1.0;
static Color _col = {0.0, 0.0, 0.0, 0.0};
void fogSave(FILE* f)
{
}
void fogLoad(FILE* f)
{
}
void fogSetColor(Color col)
{
_col = col;
}
void fogSetDistance(double near, double far)
{
_near = near;
_far = far;
}
Color fogApplyToLocation(Vector3 location, Color base)
{
Color mask = _col;
double distance = v3Norm(v3Sub(camera_location, location));
double value;
if (distance < _near)
{
return base;
}
else if (distance > _far)
{
distance = _far;
}
value = 0.8 * (distance - _near) / (_far - _near);
mask.a *= value;
colorMask(&base, &mask);
return base;
}

50
src/gui/common.h Normal file
View file

@ -0,0 +1,50 @@
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "../shared/types.h"
#include <stdio.h>
typedef void (*GuiNoiseChangedCallback)(NoiseGenerator* generator);
typedef struct SmallPreview SmallPreview;
typedef Color (*SmallPreviewCallback)(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling);
extern GtkBuilder* gui_definition;
static inline GtkWidget* _get_widget(const char* name, const char* file, int line)
{
GtkWidget* result;
result = (GtkWidget*)gtk_builder_get_object(gui_definition, (name));
if (result == NULL)
{
printf("Widget not found (%s:%d) : %s\n", file, line, name);
}
return result;
}
#define GET_WIDGET(_name_) (_get_widget(_name_, __FILE__, __LINE__))
void guiTerrainInit();
void guiWaterInit();
void guiWaterUpdate();
void guiCloudsInit();
void guiCloudsUpdate();
void guiRenderInit();
void guiNoiseInit();
void guiNoiseEdit(NoiseGenerator* generator, GuiNoiseChangedCallback callback);
void guiPreviewStart();
void guiPreviewRedraw(SmallPreview* preview);
void guiPreviewRedrawAll();
SmallPreview* guiPreviewNew(GtkImage* image);
void guiPreviewSetTerrainHeight(SmallPreview* preview);
void guiPreviewSetTerrainColor(SmallPreview* preview);
void guiPreviewSetNoise1D(SmallPreview* preview, NoiseGenerator* generator);
void guiPreviewConfigScrolling(SmallPreview* preview, double xmin, double xmax, double ymin, double ymax);
void guiPreviewConfigScaling(SmallPreview* preview, double min, double max, double step);
void guiPreviewSetRenderer(SmallPreview* preview, SmallPreviewCallback renderer);
void guiPreviewSetViewport(SmallPreview* preview, double xoffset, double yoffset, double scaling);

218
src/gui/dlg_noise.c Normal file
View file

@ -0,0 +1,218 @@
/* Noise editor dialog */
#include "common.h"
#include "../shared/functions.h"
#include "../shared/constants.h"
static GtkWidget* _dialog;
static SmallPreview* _preview;
static GtkTreeView* _level_list;
static GtkListStore* _level_list_model;
static NoiseGenerator* _generator;
static GuiNoiseChangedCallback _callback;
static int _current_mode;
static Color _cbPreview1DRenderPixel(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
if (y > noiseGet1DTotal(_generator, x))
{
return COLOR_WHITE;
}
else
{
return COLOR_BLACK;
}
}
static Color _cbPreview2DRenderPixel(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
Color col;
double max_value;
/* TODO Cache this value */
max_value = noiseGetMaxValue(_generator);
col.r = col.g = col.b = (noiseGet2DTotal(_generator, x, y) / max_value) * 0.5 + 0.5;
col.a = 1.0;
return col;
}
static void _setPreviewMode(int mode)
{
GtkButton* button;
double max_value;
max_value = noiseGetMaxValue(_generator);
button = GTK_BUTTON(GET_WIDGET("noise_editor_preview_mode"));
if (mode == 1)
{
_current_mode = 1;
guiPreviewSetRenderer(_preview, _cbPreview1DRenderPixel);
gtk_button_set_label(button, "1D");
guiPreviewConfigScrolling(_preview, -max_value * 100.0, max_value * 100.0, -max_value, max_value);
}
else if (mode == 2)
{
_current_mode = 2;
guiPreviewSetRenderer(_preview, _cbPreview2DRenderPixel);
gtk_button_set_label(button, "2D");
guiPreviewConfigScrolling(_preview, -max_value * 100.0, max_value * 100.0, -max_value * 100.0, max_value * 100.0);
}
guiPreviewConfigScaling(_preview, max_value * 0.001, max_value * 0.1, max_value * 0.001);
guiPreviewSetViewport(_preview, 0.0, 0.0, max_value * 0.01);
}
static void _redrawPreview()
{
guiPreviewRedraw(_preview);
}
static void _resetPreview()
{
_setPreviewMode(_current_mode);
_redrawPreview();
}
static void _applyLayerParams()
{
GtkTreePath* path;
GtkTreeViewColumn* column;
GtkTreeIter row;
int* indices;
double height, scale;
gtk_tree_view_get_cursor(_level_list, &path, &column);
indices = gtk_tree_path_get_indices(path);
if (indices)
{
height = (double)gtk_spin_button_get_value(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_height")));
scale = (double)gtk_spin_button_get_value(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_scale")));
noiseSetLevelSimple(_generator, indices[0], scale, height);
gtk_tree_model_get_iter(GTK_TREE_MODEL(_level_list_model), &row, path);
gtk_list_store_set(_level_list_model, &row, 0, height, 1, scale, -1);
}
_redrawPreview();
}
static void _cbGenerateClicked(GtkButton* button, gpointer user_data)
{
noiseGenerateBaseNoise(_generator, gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_base_size"))));
_redrawPreview();
}
static void _cbPreviewModeClicked(GtkButton* button, gpointer user_data)
{
if (_current_mode == 1)
{
_setPreviewMode(2);
}
else
{
_setPreviewMode(1);
}
_redrawPreview();
}
static void _cbPreviewResetClicked(GtkButton* button, gpointer user_data)
{
_resetPreview();
}
static void _cbApplyClicked(GtkButton* button, gpointer user_data)
{
gtk_widget_hide(_dialog);
_callback(_generator);
}
static void _cbCancelClicked(GtkButton* button, gpointer user_data)
{
gtk_widget_hide(_dialog);
}
static void _cbLevelParamChanged(GtkWidget* widget, gpointer user_data)
{
_applyLayerParams();
}
static void _cbRowSelected(GtkTreeView* tree_view, gpointer user_data)
{
GtkTreePath* path;
GtkTreeViewColumn* column;
int* indices;
NoiseLevel params;
gtk_tree_view_get_cursor(tree_view, &path, &column);
indices = gtk_tree_path_get_indices(path);
if (indices)
{
if (noiseGetLevel(_generator, indices[0], &params))
{
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_height")), params.height);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_scale")), params.scaling);
}
}
}
void guiNoiseInit()
{
_dialog = GET_WIDGET("dialog_noise");
_level_list = GTK_TREE_VIEW(GET_WIDGET("noise_editor_levels"));
_level_list_model = GTK_LIST_STORE(gtk_tree_view_get_model(_level_list));
_generator = noiseCreateGenerator();
gtk_spin_button_set_range(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_base_size")), 1, 4000000);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_base_size")), 100, 100000);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_scale")), 0.0001, 100.0);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_scale")), 0.0001, 0.1);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_height")), 0.0, 100.0);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_height")), 0.0001, 0.1);
_preview = guiPreviewNew(GTK_IMAGE(GET_WIDGET("noise_editor_preview")));
_resetPreview();
g_signal_connect(_dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
g_signal_connect(GET_WIDGET("noise_editor_generate"), "clicked", G_CALLBACK(_cbGenerateClicked), NULL);
g_signal_connect(GET_WIDGET("noise_editor_preview_reset"), "clicked", G_CALLBACK(_cbPreviewResetClicked), NULL);
g_signal_connect(GET_WIDGET("noise_editor_preview_mode"), "clicked", G_CALLBACK(_cbPreviewModeClicked), NULL);
g_signal_connect(GET_WIDGET("noise_editor_apply"), "clicked", G_CALLBACK(_cbApplyClicked), NULL);
g_signal_connect(GET_WIDGET("noise_editor_cancel"), "clicked", G_CALLBACK(_cbCancelClicked), NULL);
g_signal_connect(_level_list, "cursor-changed", G_CALLBACK(_cbRowSelected), NULL);
g_signal_connect(GET_WIDGET("noise_editor_height"), "value-changed", G_CALLBACK(_cbLevelParamChanged), NULL);
g_signal_connect(GET_WIDGET("noise_editor_scale"), "value-changed", G_CALLBACK(_cbLevelParamChanged), NULL);
}
void guiNoiseEdit(NoiseGenerator* generator, GuiNoiseChangedCallback callback)
{
int i, n;
NoiseLevel level;
GtkTreeIter row;
_callback = callback;
noiseCopy(generator, _generator);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_WIDGET("noise_editor_base_size")), noiseGetBaseSize(_generator));
gtk_list_store_clear(_level_list_model);
n = noiseGetLevelCount(_generator);
for (i = 0 ; i < n; i++)
{
noiseGetLevel(_generator, i, &level);
gtk_list_store_append(_level_list_model, &row);
gtk_list_store_set(_level_list_model, &row, 0, level.height, 1, level.scaling, -1);
}
_setPreviewMode(1);
_resetPreview();
gtk_widget_show(_dialog);
}

106
src/gui/global.c Normal file
View file

@ -0,0 +1,106 @@
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "../shared/functions.h"
#include "../shared/constants.h"
#include "common.h"
GtkBuilder* gui_definition;
static GtkWindow* _main_window;
static void _cbQuit(GtkWidget* widget, gpointer data)
{
gtk_main_quit();
exit(0);
}
static void _cbLoad(GtkWidget* widget, gpointer data)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new("Load File",
_main_window,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
{
char *filename;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
autoLoad(filename);
g_free(filename);
}
/* Update all GUI */
guiUpdate();
gtk_widget_destroy(dialog);
}
static void _cbSaveAs(GtkWidget* widget, gpointer data)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new("Save File",
_main_window,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
{
char *filename;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
autoSave(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void guiInit()
{
GError* p_err = NULL;
int argc = 0;
char** argv = NULL;
gtk_init(&argc, &argv);
gui_definition = gtk_builder_new();
gtk_builder_add_from_file(gui_definition, "data/gui.glade", &p_err);
_main_window = GTK_WINDOW(GET_WIDGET("main_window"));
g_signal_connect(_main_window, "delete_event", G_CALLBACK(_cbQuit), NULL);
/* Menu bar */
g_signal_connect(GET_WIDGET("menu_quit"), "activate", G_CALLBACK(_cbQuit), NULL);
g_signal_connect(GET_WIDGET("menu_load"), "activate", G_CALLBACK(_cbLoad), NULL);
g_signal_connect(GET_WIDGET("menu_saveas"), "activate", G_CALLBACK(_cbSaveAs), NULL);
/* Dialogs */
guiNoiseInit();
/* Tabs */
guiRenderInit();
guiTerrainInit();
guiWaterInit();
guiCloudsInit();
}
void guiStart()
{
guiPreviewStart();
guiUpdate();
gtk_widget_show_all(GTK_WIDGET(_main_window));
gtk_main();
}
void guiUpdate()
{
guiWaterUpdate();
guiCloudsUpdate();
guiPreviewRedrawAll();
}

432
src/gui/preview.c Normal file
View file

@ -0,0 +1,432 @@
/* Small preview management */
#include <string.h>
#include <math.h>
#include "common.h"
#include "../shared/types.h"
#include "../shared/functions.h"
#include "../shared/constants.h"
#include "../shared/system.h"
#include "../water.h"
#define MAX_PREVIEWS 30
struct SmallPreview
{
double conf_scroll_xmin;
double conf_scroll_xmax;
double conf_scroll_ymin;
double conf_scroll_ymax;
double conf_scale_min;
double conf_scale_max;
double conf_scale_step;
double xoffset;
double yoffset;
double scaling;
Mutex* lock;
GdkPixbuf* pixbuf;
GtkImage* image;
int mousex;
int mousey;
int need_rerender;
int need_render;
int need_update;
SmallPreviewCallback renderer;
};
static int _previews_count = 0;
static SmallPreview _preview[MAX_PREVIEWS];
static Thread* _thread_update;
static inline void _forceRender(SmallPreview* preview)
{
gdk_pixbuf_fill(preview->pixbuf, 0x00000000);
preview->need_rerender = 0;
preview->need_render = 1;
}
static inline void _updateImage(SmallPreview* preview)
{
gtk_image_set_from_pixbuf(preview->image, preview->pixbuf);
}
static inline void _renderPixbuf(SmallPreview* preview)
{
int x, y, done;
Color col;
void* pixels = gdk_pixbuf_get_pixels(preview->pixbuf);
guint32* pixel;
int rowstride = gdk_pixbuf_get_rowstride(preview->pixbuf);
/* TODO Use pixbuf size */
for (x = 0; x < 256; x++)
{
mutexAcquire(preview->lock);
if (preview->need_rerender)
{
mutexRelease(preview->lock);
break;
}
done = 0;
for (y = 0; y < 256; y++)
{
pixel = (guint32*)(pixels + y * rowstride + x * 4);
if (!*pixel)
{
col = preview->renderer(preview, (double)(x - 128) * preview->scaling + preview->xoffset, (double)(y - 128) * preview->scaling + preview->yoffset, preview->xoffset, preview->yoffset, preview->scaling);
*pixel = (guint32)colorTo32BitRGBA(&col);
done = 1;
}
}
if (done)
{
preview->need_update = 1;
}
mutexRelease(preview->lock);
}
}
static void* _doRenders(void* data)
{
int i;
SmallPreview* preview;
while (1)
{
for (i = 0; i < _previews_count; i++)
{
preview = _preview + i;
if (preview->need_rerender)
{
_forceRender(preview);
}
if (preview->need_render)
{
preview->need_render = 0;
_renderPixbuf(preview);
}
}
timeSleepMs(100);
}
return NULL;
}
static int _doUpdates(void* data)
{
int i;
SmallPreview* preview;
for (i = 0; i < _previews_count; i++)
{
preview = _preview + i;
if (preview->need_update)
{
preview->need_update = 0;
_updateImage(preview);
}
}
return 1;
}
static void _scrollPixbuf(SmallPreview* preview, int dx, int dy)
{
int xstart, ystart, xsize, ysize, y;
void* pixels = gdk_pixbuf_get_pixels(preview->pixbuf);
int rowstride = gdk_pixbuf_get_rowstride(preview->pixbuf);
preview->xoffset -= (double)dx * preview->scaling;
preview->yoffset -= (double)dy * preview->scaling;
/* TODO Use pixbuf size */
if (dx == 0 && dy == 0)
{
return;
}
else if (dx <= -256 || dx >= 256 || dy <= -256 || dy >= 256)
{
_forceRender(preview);
}
else
{
if (dx < 0)
{
xstart = -dx;
xsize = 256 + dx;
}
else
{
xstart = 0;
xsize = 256 - dx;
}
if (dy < 0)
{
ystart = -dy;
ysize = 256 + dy;
}
else
{
ystart = 0;
ysize = 256 - dy;
}
memmove(pixels + (ystart + dy) * rowstride + (xstart + dx) * 4, pixels + ystart * rowstride + xstart * 4, (ysize - 1) * rowstride + xsize * 4);
if (dy < 0)
{
memset(pixels + (256 + dy) * rowstride, 0, (-dy - 1) * rowstride + 256 * 4);
}
else if (dy > 0)
{
memset(pixels, 0, (dy - 1) * rowstride + 256 * 4);
}
if (dx < 0)
{
for (y = ystart + dy; y < ystart + dy + ysize; y++)
{
memset(pixels + y * rowstride + xsize * 4, 0, -dx * 4);
}
}
else if (dx > 0)
{
for (y = ystart + dy; y < ystart + dy + ysize; y++)
{
memset(pixels + y * rowstride, 0, dx * 4);
}
}
preview->need_render = 1;
}
}
static inline int _fixScroll(SmallPreview* preview, int dx, int dy, int* new_dx, int* new_dy)
{
*new_dx = dx;
*new_dy = dy;
if (preview->xoffset - dx * preview->scaling > preview->conf_scroll_xmax)
{
*new_dx = (int)floor((preview->conf_scroll_xmax - preview->xoffset) / preview->scaling);
}
if (preview->xoffset - dx * preview->scaling < preview->conf_scroll_xmin)
{
*new_dx = (int)floor((preview->conf_scroll_xmin - preview->xoffset) / preview->scaling);
}
if (preview->yoffset - dy * preview->scaling > preview->conf_scroll_ymax)
{
*new_dy = (int)floor((preview->conf_scroll_ymax - preview->yoffset) / preview->scaling);
}
if (preview->yoffset - dy * preview->scaling < preview->conf_scroll_ymin)
{
*new_dy = (int)floor((preview->conf_scroll_ymin - preview->yoffset) / preview->scaling);
}
return (*new_dx == 0 && *new_dy == 0) ? 0 : 1;
}
static inline int _fixScaling(SmallPreview* preview, double scaling, double* new_scaling)
{
double old_scaling = preview->scaling;
*new_scaling = scaling;
if (scaling < preview->conf_scale_min)
{
*new_scaling = preview->conf_scale_min;
}
if (scaling > preview->conf_scale_max)
{
*new_scaling = preview->conf_scale_max;
}
return (old_scaling == *new_scaling) ? 0 : 1;
}
static int _cbMouseScroll(GtkEventBox* image, GdkEventScroll* event, gpointer data)
{
SmallPreview* preview = (SmallPreview*)data;
/* TODO Center the zoom on the cursor */
if (event->direction == GDK_SCROLL_UP)
{
mutexAcquire(preview->lock);
if (_fixScaling(preview, preview->scaling - preview->conf_scale_step, &preview->scaling))
{
preview->need_rerender = 1;
}
mutexRelease(preview->lock);
}
else if (event->direction == GDK_SCROLL_DOWN)
{
mutexAcquire(preview->lock);
if (_fixScaling(preview, preview->scaling + preview->conf_scale_step, &preview->scaling))
{
preview->need_rerender = 1;
}
mutexRelease(preview->lock);
}
return 1;
}
static int _cbMouseButtonPressed(GtkEventBox* image, GdkEventButton* event, gpointer data)
{
SmallPreview* preview = (SmallPreview*)data;
if (event->button == 1)
{
preview->mousex = (int)event->x;
preview->mousey = (int)event->y;
}
return 1;
}
static int _cbMouseMove(GtkEventBox* image, GdkEventMotion* event, gpointer data)
{
SmallPreview* preview = (SmallPreview*)data;
int dx, dy;
if (event->state & GDK_BUTTON1_MASK)
{
mutexAcquire(preview->lock);
dx = (int)event->x - preview->mousex;
dy = (int)event->y - preview->mousey;
if (_fixScroll(preview, dx, dy, &dx, &dy))
{
_scrollPixbuf(preview, dx, dy);
}
preview->mousex = (int)event->x;
preview->mousey = (int)event->y;
mutexRelease(preview->lock);
}
return 1;
}
static Color _renderNone(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
return COLOR_BLACK;
}
void guiPreviewStart()
{
_thread_update = threadCreate(_doRenders, NULL);
g_timeout_add(200, _doUpdates, NULL);
}
void guiPreviewRedraw(SmallPreview* preview)
{
mutexAcquire(preview->lock);
preview->need_rerender = 1;
mutexRelease(preview->lock);
}
void guiPreviewRedrawAll()
{
int i;
for (i = 0; i < _previews_count; i++)
{
guiPreviewRedraw(_preview + i);
}
}
SmallPreview* guiPreviewNew(GtkImage* image)
{
GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(image));
SmallPreview* preview;
/* TODO Check the parent can receive events (or is a GtkEventBox) */
if (_previews_count < MAX_PREVIEWS)
{
preview = _preview + _previews_count;
preview->lock = mutexCreate();
preview->conf_scroll_xmin = 0.0;
preview->conf_scroll_xmax = 0.0;
preview->conf_scroll_ymin = 0.0;
preview->conf_scroll_ymax = 0.0;
preview->conf_scale_min = 1.0;
preview->conf_scale_max = 1.0;
preview->conf_scale_step = 0.0;
preview->scaling = 1.0;
preview->xoffset = 0.0;
preview->yoffset = 0.0;
/* TODO Get size from GtkImage */
preview->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 1, 8, 256, 256);
preview->image = image;
preview->need_rerender = 0;
preview->need_render = 0;
preview->need_update = 0;
preview->renderer = _renderNone;
gtk_image_clear(image);
_forceRender(preview);
g_signal_connect(parent, "scroll-event", G_CALLBACK(_cbMouseScroll), (gpointer)preview);
g_signal_connect(parent, "button-press-event", G_CALLBACK(_cbMouseButtonPressed), (gpointer)preview);
g_signal_connect(parent, "motion-notify-event", G_CALLBACK(_cbMouseMove), (gpointer)preview);
_previews_count++;
return preview;
}
else
{
/* TODO Return fake preview */
return NULL;
}
}
void guiPreviewConfigScrolling(SmallPreview* preview, double xmin, double xmax, double ymin, double ymax)
{
mutexAcquire(preview->lock);
preview->conf_scroll_xmin = xmin;
preview->conf_scroll_xmax = xmax;
preview->conf_scroll_ymin = ymin;
preview->conf_scroll_ymax = ymax;
preview->need_rerender = 1;
mutexRelease(preview->lock);
}
void guiPreviewConfigScaling(SmallPreview* preview, double min, double max, double step)
{
mutexAcquire(preview->lock);
preview->conf_scale_min = min;
preview->conf_scale_max = max;
preview->conf_scale_step = step;
preview->need_rerender = 1;
mutexRelease(preview->lock);
}
void guiPreviewSetRenderer(SmallPreview* preview, SmallPreviewCallback renderer)
{
mutexAcquire(preview->lock);
preview->renderer = renderer;
preview->need_rerender = 1;
mutexRelease(preview->lock);
}
void guiPreviewSetViewport(SmallPreview* preview, double xoffset, double yoffset, double scaling)
{
mutexAcquire(preview->lock);
preview->xoffset = xoffset;
preview->yoffset = yoffset;
preview->scaling = scaling;
preview->need_rerender = 1;
mutexRelease(preview->lock);
}
void guiPreviewSetTerrainHeight(SmallPreview* preview)
{
/*preview->conf_scroll_x = 1;
preview->conf_scroll_y = 1;
preview->conf_zoom = 1;
preview->init_scaling = preview->scaling = 0.1;
preview->init_xoffset = preview->xoffset = 0.0;
preview->init_yoffset = preview->yoffset = 0.0;
preview->renderer = _renderTopDownHeight;*/
guiPreviewRedraw(preview);
}

130
src/gui/tab_clouds.c Normal file
View file

@ -0,0 +1,130 @@
/* Terrain tab */
#include "common.h"
#include "../shared/functions.h"
#include "../shared/constants.h"
#include "../clouds.h"
#include <math.h>
static SmallPreview* _preview;
static CloudsDefinition _definition;
static int _current_layer;
static GtkTreeView* _list_layers;
static GtkListStore* _list_layers_model;
/***** Internal functions *****/
static void _revertCurrentLayer()
{
if (_current_layer >= 0)
{
cloudsCopyDefinition(cloudsGetDefinition(_current_layer), &_definition);
}
/* TODO Revert layer from config */
guiPreviewRedraw(_preview);
}
static void _applyCurrentLayer()
{
/* TODO Apply layer config */
guiUpdate();
}
static void _revertAll()
{
int i, n;
CloudsDefinition layer;
GtkTreeIter row;
gtk_list_store_clear(_list_layers_model);
n = cloudsGetLayerCount();
for (i = 0 ; i < n; i++)
{
layer = cloudsGetDefinition(i);
gtk_list_store_append(_list_layers_model, &row);
gtk_list_store_set(_list_layers_model, &row, 0, layer.ymin, 1, layer.ymax - layer.ymin, -1);
}
if (_current_layer < 0 || _current_layer >= n)
{
_current_layer = -1;
}
_revertCurrentLayer();
}
/***** Preview callbacks *****/
static Color _cbPreviewPixel(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
Color result, layer_color;
Vector3 start, end;
if (_current_layer < 0)
{
return COLOR_BLACK;
}
else
{
start.x = end.x = x;
start.z = end.z = y;
start.y = _definition.ymin;
end.y = _definition.ymax;
result.r = 0.3;
result.g = 0.5;
result.b = 0.8;
result.a = 1.0;
layer_color = cloudsGetColorCustom(start, end, &_definition, NULL, NULL);
colorMask(&result, &layer_color);
return result;
}
}
/***** Config callbacks *****/
static void _cbLayerSelected(GtkTreeView* tree_view, gpointer user_data)
{
GtkTreePath* path;
GtkTreeViewColumn* column;
int* indices;
gtk_tree_view_get_cursor(tree_view, &path, &column);
indices = gtk_tree_path_get_indices(path);
if (indices)
{
_current_layer = 0;
}
else
{
_current_layer = -1;
}
_revertCurrentLayer();
}
/***** Public functions *****/
void guiCloudsInit()
{
_list_layers = GTK_TREE_VIEW(GET_WIDGET("clouds_layers"));
_list_layers_model = GTK_LIST_STORE(gtk_tree_view_get_model(_list_layers));
_definition = cloudsCreateDefinition();
_preview = guiPreviewNew(GTK_IMAGE(GET_WIDGET("clouds_preview")));
guiPreviewConfigScaling(_preview, 1.0, 100.0, 1.0);
guiPreviewConfigScrolling(_preview, -10000.0, 10000.0, -10000.0, 10000.0);
guiPreviewSetViewport(_preview, 0.0, 0.0, 10.0);
guiPreviewSetRenderer(_preview, _cbPreviewPixel);
g_signal_connect(_list_layers, "cursor-changed", G_CALLBACK(_cbLayerSelected), NULL);
guiCloudsUpdate();
}
void guiCloudsUpdate()
{
_revertAll();
}

172
src/gui/tab_render.c Normal file
View file

@ -0,0 +1,172 @@
/* Terrain tab */
#include "common.h"
#include "../shared/functions.h"
#include "../shared/constants.h"
#include "../shared/globals.h"
#include "../shared/system.h"
static GtkImage* _render_final;
static GdkPixbuf* _render_buffer = NULL;
static guchar* _render_buffer_pixels = NULL;
static int _render_buffer_ymax = 0;
static int _render_buffer_rowstride = 0;
static int _rendering = 0;
static void _previewResize(int width, int height)
{
if (_render_buffer)
{
gdk_pixbuf_unref(_render_buffer);
}
_render_buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 1, 8, width, height);
_render_buffer_pixels = gdk_pixbuf_get_pixels(_render_buffer);
_render_buffer_rowstride = gdk_pixbuf_get_rowstride(_render_buffer);
_render_buffer_ymax = height - 1;
}
static void _previewClear(Color col)
{
gdk_pixbuf_fill(_render_buffer, (guint32)colorTo32BitARGB(&col));
}
static void _previewDraw(int x, int y, Color col)
{
guint32* pixels = (guint32*)(_render_buffer_pixels + (_render_buffer_ymax - y) * _render_buffer_rowstride + x * 4);
*pixels = (guint32)colorTo32BitRGBA(&col);
}
static void _previewUpdate(double progress)
{
gtk_image_set_from_pixbuf(_render_final, _render_buffer);
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(GET_WIDGET("render_progress")), progress);
}
static void* _threadRender(void* data)
{
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(GET_WIDGET("render_mode_raytracing"))))
{
autoRenderSceneRayTracing();
}
else
{
autoRenderSceneTwoPass(0);
}
_rendering = 0;
return NULL;
}
static void _cbStartRender(GtkWidget* widget, gpointer data)
{
Thread* thread;
/* Prepare render */
renderSetSize(gtk_spin_button_get_value(GTK_SPIN_BUTTON(GET_WIDGET("render_width"))), gtk_spin_button_get_value(GTK_SPIN_BUTTON(GET_WIDGET("render_height"))));
autoSetRenderQuality((int)gtk_range_get_value(GTK_RANGE(GET_WIDGET("render_quality"))));
gtk_widget_set_size_request(GET_WIDGET("render_preview"), render_width, render_height);
gtk_image_clear(GTK_IMAGE(GET_WIDGET("render_preview")));
renderSetPreviewCallbacks(_previewResize, _previewClear, _previewDraw, _previewUpdate);
/* Open render dialog */
gtk_window_set_deletable(GTK_WINDOW(GET_WIDGET("dialog_render")), 0);
gtk_widget_show(GET_WIDGET("dialog_render"));
gtk_widget_set_sensitive(GET_WIDGET("render_stop"), 1);
gtk_widget_set_sensitive(GET_WIDGET("render_close"), 0);
/* Do the render */
_rendering = 1;
thread = threadCreate(_threadRender, NULL);
while (_rendering)
{
timeSleepMs(100);
while (gtk_events_pending())
{
gtk_main_iteration();
}
}
threadJoin(thread);
/* Clean up */
renderSetPreviewCallbacks(NULL, NULL, NULL, NULL);
gtk_widget_set_sensitive(GET_WIDGET("render_stop"), 0);
gtk_widget_set_sensitive(GET_WIDGET("render_close"), 1);
}
static void _cbStopRender(GtkWidget* widget, gpointer data)
{
if (_rendering)
{
renderInterrupt();
}
}
static void _cbCloseRender(GtkWidget* widget, gpointer data)
{
gtk_widget_hide(GET_WIDGET("dialog_render"));
}
static void _cbShowRender(GtkWidget* widget, gpointer data)
{
gtk_widget_show(GET_WIDGET("dialog_render"));
}
static void _cbSaveRender(GtkWidget* widget, gpointer data)
{
GtkWidget *dialog;
char *filename;
dialog = gtk_file_chooser_dialog_new("Save last render",
GTK_WINDOW(GET_WIDGET("main_window")),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), TRUE);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "render.png");
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
{
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
renderSaveToFile(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
static int _cbWindowClosed(GtkWidget* widget, GdkEvent* event, gpointer data)
{
gtk_widget_hide(widget);
if (_rendering)
{
renderInterrupt();
}
return 1;
}
void guiRenderInit()
{
_render_final = GTK_IMAGE(GET_WIDGET("render_preview"));
_previewResize(800, 600);
_previewClear(COLOR_BLACK);
_previewUpdate(0.0);
g_signal_connect(GET_WIDGET("render_show"), "clicked", G_CALLBACK(_cbShowRender), NULL);
g_signal_connect(GET_WIDGET("render_save"), "clicked", G_CALLBACK(_cbSaveRender), NULL);
g_signal_connect(GET_WIDGET("render_start"), "clicked", G_CALLBACK(_cbStartRender), NULL);
g_signal_connect(GET_WIDGET("render_stop"), "clicked", G_CALLBACK(_cbStopRender), NULL);
g_signal_connect(GET_WIDGET("render_close"), "clicked", G_CALLBACK(_cbCloseRender), NULL);
g_signal_connect(GET_WIDGET("dialog_render"), "delete-event", G_CALLBACK(_cbWindowClosed), NULL);
gtk_range_set_range(GTK_RANGE(GET_WIDGET("render_quality")), 1, 10);
gtk_range_set_value(GTK_RANGE(GET_WIDGET("render_quality")), 5);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(GET_WIDGET("render_width")), 100, 4000);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_WIDGET("render_width")), 1200);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(GET_WIDGET("render_width")), 10, 100);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(GET_WIDGET("render_height")), 100, 4000);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_WIDGET("render_height")), 900);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(GET_WIDGET("render_height")), 10, 100);
}

40
src/gui/tab_terrain.c Normal file
View file

@ -0,0 +1,40 @@
/* Terrain tab */
#include "common.h"
#include "../shared/functions.h"
static SmallPreview* _preview;
static Color _cbPreviewRenderPixel(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
Color result;
result.r = result.g = result.b = terrainGetHeightNormalized(x, y);
result.a = 1.0;
return result;
}
static void _cbEditNoiseDone(NoiseGenerator* generator)
{
terrainSetNoiseGenerator(generator);
/* TODO Redraw only affected by terrain */
guiPreviewRedrawAll();
}
static void _cbEditNoise(GtkWidget* widget, gpointer data)
{
guiNoiseEdit(terrainGetNoiseGenerator(), _cbEditNoiseDone);
}
void guiTerrainInit()
{
g_signal_connect(GET_WIDGET("terrain_noise_edit"), "clicked", G_CALLBACK(_cbEditNoise), NULL);
_preview = guiPreviewNew(GTK_IMAGE(GET_WIDGET("terrain_preview")));
guiPreviewConfigScaling(_preview, 0.01, 1.0, 0.05);
guiPreviewConfigScrolling(_preview, -1000.0, 1000.0, -1000.0, 1000.0);
guiPreviewSetViewport(_preview, 0.0, 0.0, 0.2);
guiPreviewSetRenderer(_preview, _cbPreviewRenderPixel);
}

207
src/gui/tab_water.c Normal file
View file

@ -0,0 +1,207 @@
/* Terrain tab */
#include "common.h"
#include "../shared/functions.h"
#include "../shared/constants.h"
#include "../water.h"
#include <math.h>
static SmallPreview* _preview_coverage;
static SmallPreview* _preview_render;
static WaterDefinition _definition;
static RayCastingResult _rayCastFromWater(Vector3 start, Vector3 direction)
{
RayCastingResult result;
double x, y;
result.hit = 1;
if (direction.z < 0.0001)
{
result.hit_color = COLOR_WHITE;
}
else
{
x = start.x + direction.x * (0.0 - start.z) / direction.z;
y = start.y + direction.y * (0.0 - start.z) / direction.z;
if (((int)ceil(x * 0.2) % 2 == 0) ^ ((int)ceil(y * 0.2 - 0.5) % 2 == 0))
{
result.hit_color = COLOR_WHITE;
}
else
{
result.hit_color = COLOR_GREY;
}
}
/* TODO hit_location */
return result;
}
static Color _cbPreviewCoverage(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
Color result;
double height;
height = terrainGetHeight(x, y);
if (height <= _definition.height)
{
return _definition.main_color;
}
else
{
result.r = result.g = result.b = terrainGetHeightNormalized(x, y);
result.a = 1.0;
return result;
}
}
static Color _cbPreviewRender(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
{
Vector3 eye, look, location;
WaterDefinition definition;
WaterEnvironment environment;
WaterQuality quality;
eye.x = 0.0;
eye.y = scaling;
eye.z = -10.0 * scaling;
look.x = x * 0.01 / scaling;
look.y = -y * 0.01 / scaling - 0.3;
look.z = 1.0;
look = v3Normalize(look);
if (look.y > -0.0001)
{
return _rayCastFromWater(eye, look).hit_color;
}
location.x = eye.x - look.x * eye.y / look.y;
location.y = 0.0;
location.z = eye.z - look.z * eye.y / look.y;
if (location.z > 0.0)
{
return _rayCastFromWater(eye, look).hit_color;
}
definition = _definition;
definition.height = 0.0;
environment.reflection_function = _rayCastFromWater;
environment.refraction_function = _rayCastFromWater;
environment.toggle_fog = 0;
environment.toggle_shadows = 0;
quality.force_detail = 0.0001;
return waterGetColorCustom(location, look, &definition, &quality, &environment).final;
}
static void _cbEditNoiseDone(NoiseGenerator* generator)
{
noiseCopy(generator, _definition.height_noise);
guiPreviewRedraw(_preview_render);
}
static void _cbEditNoise(GtkWidget* widget, gpointer data)
{
guiNoiseEdit(_definition.height_noise, _cbEditNoiseDone);
}
static void _cbHeightChanged(GtkRange* range, gpointer data)
{
_definition.height = gtk_range_get_value(range);
guiPreviewRedraw(_preview_coverage);
}
static void _cbTransparencyChanged(GtkRange* range, gpointer data)
{
_definition.transparency = gtk_range_get_value(range);
guiPreviewRedraw(_preview_render);
}
static void _cbReflectionChanged(GtkRange* range, gpointer data)
{
_definition.reflection = gtk_range_get_value(range);
guiPreviewRedraw(_preview_render);
}
static void _cbColorChanged(GtkColorButton* colorbutton, gpointer data)
{
GdkRGBA col;
gtk_color_button_get_rgba(colorbutton, &col);
_definition.main_color.r = col.red;
_definition.main_color.g = col.green;
_definition.main_color.b = col.blue;
_definition.main_color.a = 1.0;
guiPreviewRedraw(_preview_render);
guiPreviewRedraw(_preview_coverage);
}
static void _cbRevertConfig(GtkWidget* widget, gpointer data)
{
GdkRGBA col;
waterCopyDefinition(waterGetDefinition(), &_definition);
gtk_range_set_value(GTK_RANGE(GET_WIDGET("water_height")), _definition.height);
gtk_range_set_value(GTK_RANGE(GET_WIDGET("water_transparency")), _definition.transparency);
gtk_range_set_value(GTK_RANGE(GET_WIDGET("water_reflection")), _definition.reflection);
col.red = _definition.main_color.r;
col.green = _definition.main_color.g;
col.blue = _definition.main_color.b;
col.alpha = 1.0;
gtk_color_button_set_rgba(GTK_COLOR_BUTTON(GET_WIDGET("water_color")), &col);
guiPreviewRedraw(_preview_render);
guiPreviewRedraw(_preview_coverage);
}
static void _cbApplyConfig(GtkWidget* widget, gpointer data)
{
waterSetDefinition(_definition);
guiUpdate();
}
void guiWaterInit()
{
_definition = waterCreateDefinition();
/* Buttons */
g_signal_connect(GET_WIDGET("water_noise_edit"), "clicked", G_CALLBACK(_cbEditNoise), NULL);
g_signal_connect(GET_WIDGET("water_apply"), "clicked", G_CALLBACK(_cbApplyConfig), NULL);
g_signal_connect(GET_WIDGET("water_revert"), "clicked", G_CALLBACK(_cbRevertConfig), NULL);
/* Configs */
gtk_range_set_range(GTK_RANGE(GET_WIDGET("water_height")), -20.0, 20.0);
gtk_range_set_range(GTK_RANGE(GET_WIDGET("water_transparency")), 0.0, 1.0);
gtk_range_set_range(GTK_RANGE(GET_WIDGET("water_reflection")), 0.0, 1.0);
/* Config signals */
g_signal_connect(GET_WIDGET("water_height"), "value-changed", G_CALLBACK(_cbHeightChanged), NULL);
g_signal_connect(GET_WIDGET("water_transparency"), "value-changed", G_CALLBACK(_cbTransparencyChanged), NULL);
g_signal_connect(GET_WIDGET("water_reflection"), "value-changed", G_CALLBACK(_cbReflectionChanged), NULL);
g_signal_connect(GET_WIDGET("water_color"), "color-set", G_CALLBACK(_cbColorChanged), NULL);
/* Previews */
_preview_coverage = guiPreviewNew(GTK_IMAGE(GET_WIDGET("water_preview_coverage")));
guiPreviewConfigScaling(_preview_coverage, 0.01, 1.0, 0.05);
guiPreviewConfigScrolling(_preview_coverage, -1000.0, 1000.0, -1000.0, 1000.0);
guiPreviewSetViewport(_preview_coverage, 0.0, 0.0, 0.2);
guiPreviewSetRenderer(_preview_coverage, _cbPreviewCoverage);
_preview_render = guiPreviewNew(GTK_IMAGE(GET_WIDGET("water_preview_render")));
guiPreviewConfigScaling(_preview_render, 0.1, 1.0, 0.1);
guiPreviewConfigScrolling(_preview_render, -10.0, 10.0, -10.0, 10.0);
guiPreviewSetViewport(_preview_render, 0.0, 0.0, 0.5);
guiPreviewSetRenderer(_preview_render, _cbPreviewRender);
guiWaterUpdate();
}
void guiWaterUpdate()
{
_cbRevertConfig(NULL, NULL);
}

82
src/lighting.c Normal file
View file

@ -0,0 +1,82 @@
#include <stdlib.h>
#include <math.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/constants.h"
#include "shared/globals.h"
static Color sun_color;
double sun_color_lum;
Vector3 sun_direction;
Vector3 sun_direction_inv;
void lightingSave(FILE* f)
{
}
void lightingLoad(FILE* f)
{
}
void lightingSetSunDirection(double x, double y, double z)
{
sun_direction.x = x;
sun_direction.y = y;
sun_direction.z = z;
sun_direction = v3Normalize(sun_direction);
sun_direction_inv = v3Scale(sun_direction, -1.0);
}
void lightingSetSunAngle(double hor, double ver)
{
lightingSetSunDirection(cos(hor) * cos(ver), sin(ver), -sin(hor) * cos(ver));
}
void lightingSetSunColor(Color col)
{
sun_color = col;
sun_color_lum = colorGetValue(&col);
}
Color lightingApply(Vector3 location, Vector3 normal, double shadowing, Color base, double reflection, double shininess)
{
Color result, light;
double ambient, diffuse, specular;
Vector3 view, reflect;
light.r = sun_color.r * (1.0 - 0.4 * shadowing);
light.g = sun_color.g * (1.0 - 0.4 * shadowing);
light.b = sun_color.b * (1.0 - 0.4 * shadowing);
normal = v3Normalize(normal);
view = v3Normalize(v3Sub(location, camera_location));
reflect = v3Sub(v3Scale(normal, 2.0 * v3Dot(sun_direction_inv, normal)), sun_direction_inv);
ambient = 0.2;
diffuse = v3Dot(sun_direction_inv, normal);
diffuse = pow(diffuse * 0.5 + 0.5, 2.0) * (1.0 - shadowing) + (diffuse * 0.5 + 0.3) * shadowing;
if (diffuse > 0.0)
{
if (shininess > 0.0)
{
specular = pow(v3Dot(reflect, view) * reflection, shininess * 10.0 + 1.0);
}
else
{
specular = 0.0;
}
}
else
{
diffuse = 0.0;
specular = 0.0;
}
result.r = base.r * ambient + base.r * diffuse * light.r + base.r * specular * light.r;
result.g = base.g * ambient + base.g * diffuse * light.g + base.g * specular * light.g;
result.b = base.b * ambient + base.b * diffuse * light.b + base.b * specular * light.b;
result.a = base.a;
return result;
}

126
src/main.c Normal file
View file

@ -0,0 +1,126 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include "IL/il.h"
#include "IL/ilu.h"
#include "shared/types.h"
#include "shared/constants.h"
#include "shared/functions.h"
#include "shared/globals.h"
/*static char _filename[22];
static void _setupFilename(int number)
{
_filename[15] = (char)(97 + number / 26);
_filename[16] = (char)(97 + number % 26);
}
static void _doRender(int number, int postonly)
{
_setupFilename(number);
fprintf(stderr, "Rendering %s...\n", _filename);
autoRenderAll(postonly);
fprintf(stderr, "Saving %s...\n", _filename);
remove(_filename);
renderSaveToFile(_filename);
}
static void _renderTurnTable(int count)
{
int i;
for (i = 0; i < count; i++)
{
cameraSetLocation(sin(M_PI * 2.0 * (double)i / (double)count) * 20.0, 8.0, cos(M_PI * 2.0 * (double)i / (double)count) * 20.0);
_doRender(i, 0);
}
}
static void _renderFly(int count, double speed)
{
int i;
double x, y, z, ty1;
x = 0.0;
y = 8.0;
z = 0.0;
for (i = 0; i < count; i++)
{
cameraSetLocation(x, y, z);
cameraSetTarget(x, y, z + 1.0);
_doRender(i, 0);
ty1 = terrainGetHeight(x, z);
if (y > ty1 + 9.0)
{
y -= speed;
}
if (y < ty1 + 7.0)
{
y += speed;
}
z += speed;
}
}
static void _renderDayTime(int count)
{
int i;
for (i = 0; i < count; i++)
{
autoSetDaytimeFraction(0.4 + (double)i / (double)count);
_doRender(i, i > 0 ? 1 : 0);
}
}
static void _renderQuality()
{
int i;
for (i = 1; i <= 10; i++)
{
autoSetRenderQuality(i);
renderSetSize(800, 600);
_doRender(i - 1, 0);
}
}*/
int main(int argc, char** argv)
{
/*strcpy(_filename, "./output/result__.png");*/
ilInit();
iluInit();
cameraSetLocation(2.0, 5.0, 10.0);
cameraSetTarget(0.0, 5.0, 0.0);
autoInit();
guiInit();
autoSetRenderQuality(5);
autoGenRealisticLandscape(0);
autoSetDaytime(8, 30);
guiStart();
//_doRender(0, 0);
//_renderTurnTable(600);
//_renderDayTime(600);
//_renderFly(600, 0.1);
//_renderQuality();
return 0;
}

75
src/modifiers.c Normal file
View file

@ -0,0 +1,75 @@
#include "shared/types.h"
#include "shared/functions.h"
#include <stdlib.h>
typedef enum
{
MODE_NULL,
MODE_ADD_VALUE,
MODE_FIX_VALUE
} _EnumMode;
struct HeightModifier
{
Zone* zone;
_EnumMode mode;
double value;
};
HeightModifier* modifierCreate()
{
HeightModifier* modifier;
modifier = (HeightModifier*)malloc(sizeof(HeightModifier));
modifier->zone = zoneCreate();
modifier->mode = MODE_NULL;
return modifier;
}
void modifierDelete(HeightModifier* modifier)
{
zoneDelete(modifier->zone);
free(modifier);
}
Zone* modifierGetZone(HeightModifier* modifier)
{
return modifier->zone;
}
void modifierActionAddValue(HeightModifier* modifier, double value)
{
modifier->mode = MODE_ADD_VALUE;
modifier->value = value;
}
void modifierActionFixValue(HeightModifier* modifier, double value)
{
modifier->mode = MODE_FIX_VALUE;
modifier->value = value;
}
Vector3 modifierApply(HeightModifier* modifier, Vector3 location)
{
double influence, diff;
Vector3 normal;
switch (modifier->mode)
{
case MODE_ADD_VALUE:
influence = zoneGetValue(modifier->zone, location, normal);
location.y += modifier->value * influence;
break;
case MODE_FIX_VALUE:
influence = zoneGetValue(modifier->zone, location, normal);
diff = modifier->value - location.y;
location.y += diff * influence;
break;
case MODE_NULL:
break;
}
return location;
}

764
src/noise.c Normal file
View file

@ -0,0 +1,764 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct NoiseLevel;
struct NoiseGenerator
{
int size1;
int size2;
int size3;
double* noise;
double height_offset;
int level_count;
struct NoiseLevel* levels;
};
#include "shared/types.h"
#include "shared/functions.h"
static inline double _cubicInterpolate(double* p, double x)
{
return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
}
NoiseGenerator* noiseCreateGenerator()
{
NoiseGenerator* result;
/* initialize */
result = malloc(sizeof(NoiseGenerator));
result->size1 = 1;
result->size2 = 1;
result->size3 = 1;
result->noise = malloc(sizeof(double));
result->noise[0] = 0.0;
result->level_count = 0;
result->levels = malloc(sizeof(NoiseLevel));
result->height_offset = 0.0;
return result;
}
void noiseDeleteGenerator(NoiseGenerator* generator)
{
free(generator->noise);
free(generator->levels);
free(generator);
}
void noiseSave(NoiseGenerator* perlin, FILE* f)
{
int x;
double* it_noise;
toolsSaveInt(f, perlin->size1);
toolsSaveInt(f, perlin->size2);
toolsSaveInt(f, perlin->size3);
toolsSaveDouble(f, perlin->height_offset);
toolsSaveInt(f, perlin->level_count);
it_noise = perlin->noise;
for (x = 0; x < perlin->size1; x++)
{
toolsSaveDouble(f, *(it_noise++));
}
for (x = 0; x < perlin->level_count; x++)
{
NoiseLevel level = perlin->levels[x];
toolsSaveDouble(f, level.scaling);
toolsSaveDouble(f, level.height);
toolsSaveDouble(f, level.xoffset);
toolsSaveDouble(f, level.yoffset);
toolsSaveDouble(f, level.zoffset);
}
}
void noiseLoad(NoiseGenerator* perlin, FILE* f)
{
int x;
double* it_noise;
perlin->size1 = toolsLoadInt(f);
perlin->size2 = toolsLoadInt(f);
perlin->size3 = toolsLoadInt(f);
perlin->height_offset = toolsLoadDouble(f);
perlin->level_count = toolsLoadInt(f);
perlin->noise = realloc(perlin->noise, sizeof(double) * perlin->size1);
it_noise = perlin->noise;
for (x = 0; x < perlin->size1; x++)
{
*(it_noise++) = toolsLoadDouble(f);
}
perlin->levels = realloc(perlin->levels, sizeof(NoiseLevel) * perlin->level_count);
for (x = 0; x < perlin->level_count; x++)
{
NoiseLevel* level = perlin->levels + x;
level->scaling = toolsLoadDouble(f);
level->height = toolsLoadDouble(f);
level->xoffset = toolsLoadDouble(f);
level->yoffset = toolsLoadDouble(f);
level->zoffset = toolsLoadDouble(f);
}
}
void noiseCopy(NoiseGenerator* source, NoiseGenerator* destination)
{
destination->size1 = source->size1;
destination->size2 = source->size2;
destination->size3 = source->size3;
destination->height_offset = source->height_offset;
destination->level_count = source->level_count;
destination->noise = realloc(destination->noise, sizeof(double) * destination->size1);
memcpy(destination->noise, source->noise, sizeof(double) * destination->size1);
destination->levels = realloc(destination->levels, sizeof(NoiseLevel) * destination->level_count);
memcpy(destination->levels, source->levels, sizeof(NoiseLevel) * destination->level_count);
}
void noiseGenerateBaseNoise(NoiseGenerator* generator, int size)
{
int x;
double* it_noise;
size = (size < 1) ? 1 : size;
size = (size > 4000000) ? 4000000 : size;
generator->size1 = size;
generator->size2 = (int)floor(sqrt((float)size));
generator->size3 = (int)floor(cbrt((float)size));
generator->noise = realloc(generator->noise, sizeof(double) * size);
it_noise = generator->noise;
for (x = 0; x < size; x++)
{
*(it_noise++) = toolsRandom() - 0.5;
}
}
int noiseGetBaseSize(NoiseGenerator* generator)
{
return generator->size1;
}
double noiseGetMaxValue(NoiseGenerator* generator)
{
int x;
double result = generator->height_offset;
for (x = 0; x < generator->level_count; x++)
{
result += generator->levels[x].height / 2.0;
}
return result;
}
int noiseGetLevelCount(NoiseGenerator* generator)
{
return generator->level_count;
}
void noiseClearLevels(NoiseGenerator* generator)
{
generator->level_count = 0;
}
void noiseAddLevel(NoiseGenerator* generator, NoiseLevel level)
{
if (level.scaling < 0.0000001 || level.height < 0.0000001)
{
return;
}
generator->levels = realloc(generator->levels, sizeof(NoiseLevel) * (generator->level_count + 1));
generator->levels[generator->level_count] = level;
generator->level_count++;
}
void noiseAddLevelSimple(NoiseGenerator* generator, double scaling, double height)
{
NoiseLevel level;
level.scaling = scaling;
level.height = height;
level.xoffset = toolsRandom();
level.yoffset = toolsRandom();
level.zoffset = toolsRandom();
noiseAddLevel(generator, level);
}
void noiseAddLevels(NoiseGenerator* generator, int level_count, NoiseLevel start_level, double scaling_factor, double height_factor, int randomize_offset)
{
int i;
for (i = 0; i < level_count; i++)
{
if (randomize_offset)
{
start_level.xoffset = toolsRandom();
start_level.yoffset = toolsRandom();
start_level.zoffset = toolsRandom();
}
noiseAddLevel(generator, start_level);
start_level.scaling *= scaling_factor;
start_level.height *= height_factor;
}
}
void noiseAddLevelsSimple(NoiseGenerator* generator, int level_count, double scaling, double height)
{
NoiseLevel level;
level.scaling = scaling;
level.height = height;
noiseAddLevels(generator, level_count, level, 0.5, 0.5, 1);
}
int noiseGetLevel(NoiseGenerator* generator, int level, NoiseLevel* params)
{
if (level >= 0 && level < generator->level_count)
{
*params = generator->levels[level];
return 1;
}
else
{
return 0;
}
}
void noiseSetLevel(NoiseGenerator* generator, int level, NoiseLevel params)
{
if (level >= 0 && level < generator->level_count)
{
generator->levels[level] = params;
}
}
void noiseSetLevelSimple(NoiseGenerator* generator, int level, double scaling, double height)
{
NoiseLevel params;
params.scaling = scaling;
params.height = height;
params.xoffset = toolsRandom();
params.yoffset = toolsRandom();
params.zoffset = toolsRandom();
noiseSetLevel(generator, level, params);
}
void noiseNormalizeHeight(NoiseGenerator* generator, double min_height, double max_height, int adjust_scaling)
{
int level;
double height = 0.0;
double target_height = max_height - min_height;
if (generator->level_count == 0)
{
return;
}
for (level = 0; level < generator->level_count; level++)
{
height += generator->levels[level].height;
}
for (level = 0; level < generator->level_count; level++)
{
generator->levels[level].height *= target_height / height;
if (adjust_scaling)
{
generator->levels[level].scaling *= target_height / height;
}
}
generator->height_offset = min_height + target_height / 2.0;
}
static inline double _get1DRawNoiseValue(NoiseGenerator* generator, double x)
{
double* noise = generator->noise;
int size = generator->size1;
int xbase = (int)floor(x);
double xinternal = x - (double)xbase;
int x0 = (xbase - 1) % size;
if (x0 < 0)
{
x0 += size;
}
int x1 = xbase % size;
if (x1 < 0)
{
x1 += size;
}
int x2 = (xbase + 1) % size;
if (x2 < 0)
{
x2 += size;
}
int x3 = (xbase + 2) % size;
if (x3 < 0)
{
x3 += size;
}
double buf_cubic_x[4];
buf_cubic_x[0] = noise[x0];
buf_cubic_x[1] = noise[x1];
buf_cubic_x[2] = noise[x2];
buf_cubic_x[3] = noise[x3];
return _cubicInterpolate(buf_cubic_x, xinternal);
}
static inline double _get1DLevelValue(NoiseGenerator* generator, NoiseLevel* level, double x)
{
return _get1DRawNoiseValue(generator, x / level->scaling + level->xoffset * generator->size1) * level->height;
}
double noiseGet1DLevel(NoiseGenerator* generator, int level, double x)
{
if (level >= 0 && level < generator->level_count)
{
return _get1DLevelValue(generator, generator->levels + level, x);
}
else
{
return 0.0;
}
}
double noiseGet1DTotal(NoiseGenerator* generator, double x)
{
int level;
double result;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
result += _get1DLevelValue(generator, generator->levels + level, x);
}
return result + generator->height_offset;
}
double noiseGet1DDetail(NoiseGenerator* generator, double x, double detail)
{
int level;
double result, height, factor;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
height = generator->levels[level].height;
factor = 1.0;
if (height < detail * 0.25)
{
break;
}
else if (height < detail * 0.5)
{
factor = (detail * 0.5 - height) / 0.25;
}
result += _get1DLevelValue(generator, generator->levels + level, x) * factor;
}
return result + generator->height_offset;
}
static inline double _get2DRawNoiseValue(NoiseGenerator* generator, double x, double y)
{
double* noise = generator->noise;
int size = generator->size2;
int xbase = (int)floor(x);
int ybase = (int)floor(y);
double xinternal = x - (double)xbase;
double yinternal = y - (double)ybase;
int x0 = (xbase - 1) % size;
if (x0 < 0)
{
x0 += size;
}
int x1 = xbase % size;
if (x1 < 0)
{
x1 += size;
}
int x2 = (xbase + 1) % size;
if (x2 < 0)
{
x2 += size;
}
int x3 = (xbase + 2) % size;
if (x3 < 0)
{
x3 += size;
}
int y0 = (ybase - 1) % size;
if (y0 < 0)
{
y0 += size;
}
int y1 = ybase % size;
if (y1 < 0)
{
y1 += size;
}
int y2 = (ybase + 1) % size;
if (y2 < 0)
{
y2 += size;
}
int y3 = (ybase + 2) % size;
if (y3 < 0)
{
y3 += size;
}
double buf_cubic_x[4];
double buf_cubic_y[4];
buf_cubic_x[0] = noise[y0 * size + x0];
buf_cubic_x[1] = noise[y0 * size + x1];
buf_cubic_x[2] = noise[y0 * size + x2];
buf_cubic_x[3] = noise[y0 * size + x3];
buf_cubic_y[0] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y1 * size + x0];
buf_cubic_x[1] = noise[y1 * size + x1];
buf_cubic_x[2] = noise[y1 * size + x2];
buf_cubic_x[3] = noise[y1 * size + x3];
buf_cubic_y[1] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y2 * size + x0];
buf_cubic_x[1] = noise[y2 * size + x1];
buf_cubic_x[2] = noise[y2 * size + x2];
buf_cubic_x[3] = noise[y2 * size + x3];
buf_cubic_y[2] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y3 * size + x0];
buf_cubic_x[1] = noise[y3 * size + x1];
buf_cubic_x[2] = noise[y3 * size + x2];
buf_cubic_x[3] = noise[y3 * size + x3];
buf_cubic_y[3] = _cubicInterpolate(buf_cubic_x, xinternal);
return _cubicInterpolate(buf_cubic_y, yinternal);
}
static inline double _get2DLevelValue(NoiseGenerator* generator, NoiseLevel* level, double x, double y)
{
return _get2DRawNoiseValue(generator, x / level->scaling + level->xoffset * generator->size2, y / level->scaling + level->yoffset * generator->size2) * level->height;
}
double noiseGet2DLevel(NoiseGenerator* generator, int level, double x, double y)
{
if (level >= 0 && level < generator->level_count)
{
return _get2DLevelValue(generator, generator->levels + level, x, y);
}
else
{
return 0.0;
}
}
double noiseGet2DTotal(NoiseGenerator* generator, double x, double y)
{
int level;
double result;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
result += _get2DLevelValue(generator, generator->levels + level, x, y);
}
return result + generator->height_offset;
}
double noiseGet2DDetail(NoiseGenerator* generator, double x, double y, double detail)
{
int level;
double result, height, factor;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
height = generator->levels[level].height;
factor = 1.0;
if (height < detail * 0.25)
{
break;
}
else if (height < detail * 0.5)
{
factor = (detail * 0.5 - height) / 0.25;
}
result += _get2DLevelValue(generator, generator->levels + level, x, y) * factor;
}
return result + generator->height_offset;
}
static inline double _get3DRawNoiseValue(NoiseGenerator* generator, double x, double y, double z)
{
double* noise = generator->noise;
int size = generator->size3;
int xbase = (int)floor(x);
int ybase = (int)floor(y);
int zbase = (int)floor(z);
double xinternal = x - (double)xbase;
double yinternal = y - (double)ybase;
double zinternal = z - (double)zbase;
int x0 = (xbase - 1) % size;
if (x0 < 0)
{
x0 += size;
}
int x1 = xbase % size;
if (x1 < 0)
{
x1 += size;
}
int x2 = (xbase + 1) % size;
if (x2 < 0)
{
x2 += size;
}
int x3 = (xbase + 2) % size;
if (x3 < 0)
{
x3 += size;
}
int y0 = (ybase - 1) % size;
if (y0 < 0)
{
y0 += size;
}
int y1 = ybase % size;
if (y1 < 0)
{
y1 += size;
}
int y2 = (ybase + 1) % size;
if (y2 < 0)
{
y2 += size;
}
int y3 = (ybase + 2) % size;
if (y3 < 0)
{
y3 += size;
}
int z0 = (zbase - 1) % size;
if (z0 < 0)
{
z0 += size;
}
int z1 = zbase % size;
if (z1 < 0)
{
z1 += size;
}
int z2 = (zbase + 1) % size;
if (z2 < 0)
{
z2 += size;
}
int z3 = (zbase + 2) % size;
if (z3 < 0)
{
z3 += size;
}
double buf_cubic_x[4];
double buf_cubic_y[4];
double buf_cubic_z[4];
buf_cubic_x[0] = noise[y0 * size * size + x0 * size + z0];
buf_cubic_x[1] = noise[y0 * size * size + x1 * size + z0];
buf_cubic_x[2] = noise[y0 * size * size + x2 * size + z0];
buf_cubic_x[3] = noise[y0 * size * size + x3 * size + z0];
buf_cubic_y[0] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y1 * size * size + x0 * size + z0];
buf_cubic_x[1] = noise[y1 * size * size + x1 * size + z0];
buf_cubic_x[2] = noise[y1 * size * size + x2 * size + z0];
buf_cubic_x[3] = noise[y1 * size * size + x3 * size + z0];
buf_cubic_y[1] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y2 * size * size + x0 * size + z0];
buf_cubic_x[1] = noise[y2 * size * size + x1 * size + z0];
buf_cubic_x[2] = noise[y2 * size * size + x2 * size + z0];
buf_cubic_x[3] = noise[y2 * size * size + x3 * size + z0];
buf_cubic_y[2] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y3 * size * size + x0 * size + z0];
buf_cubic_x[1] = noise[y3 * size * size + x1 * size + z0];
buf_cubic_x[2] = noise[y3 * size * size + x2 * size + z0];
buf_cubic_x[3] = noise[y3 * size * size + x3 * size + z0];
buf_cubic_y[3] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_z[0] = _cubicInterpolate(buf_cubic_y, yinternal);
buf_cubic_x[0] = noise[y0 * size * size + x0 * size + z1];
buf_cubic_x[1] = noise[y0 * size * size + x1 * size + z1];
buf_cubic_x[2] = noise[y0 * size * size + x2 * size + z1];
buf_cubic_x[3] = noise[y0 * size * size + x3 * size + z1];
buf_cubic_y[0] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y1 * size * size + x0 * size + z1];
buf_cubic_x[1] = noise[y1 * size * size + x1 * size + z1];
buf_cubic_x[2] = noise[y1 * size * size + x2 * size + z1];
buf_cubic_x[3] = noise[y1 * size * size + x3 * size + z1];
buf_cubic_y[1] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y2 * size * size + x0 * size + z1];
buf_cubic_x[1] = noise[y2 * size * size + x1 * size + z1];
buf_cubic_x[2] = noise[y2 * size * size + x2 * size + z1];
buf_cubic_x[3] = noise[y2 * size * size + x3 * size + z1];
buf_cubic_y[2] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y3 * size * size + x0 * size + z1];
buf_cubic_x[1] = noise[y3 * size * size + x1 * size + z1];
buf_cubic_x[2] = noise[y3 * size * size + x2 * size + z1];
buf_cubic_x[3] = noise[y3 * size * size + x3 * size + z1];
buf_cubic_y[3] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_z[1] = _cubicInterpolate(buf_cubic_y, yinternal);
buf_cubic_x[0] = noise[y0 * size * size + x0 * size + z2];
buf_cubic_x[1] = noise[y0 * size * size + x1 * size + z2];
buf_cubic_x[2] = noise[y0 * size * size + x2 * size + z2];
buf_cubic_x[3] = noise[y0 * size * size + x3 * size + z2];
buf_cubic_y[0] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y1 * size * size + x0 * size + z2];
buf_cubic_x[1] = noise[y1 * size * size + x1 * size + z2];
buf_cubic_x[2] = noise[y1 * size * size + x2 * size + z2];
buf_cubic_x[3] = noise[y1 * size * size + x3 * size + z2];
buf_cubic_y[1] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y2 * size * size + x0 * size + z2];
buf_cubic_x[1] = noise[y2 * size * size + x1 * size + z2];
buf_cubic_x[2] = noise[y2 * size * size + x2 * size + z2];
buf_cubic_x[3] = noise[y2 * size * size + x3 * size + z2];
buf_cubic_y[2] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y3 * size * size + x0 * size + z2];
buf_cubic_x[1] = noise[y3 * size * size + x1 * size + z2];
buf_cubic_x[2] = noise[y3 * size * size + x2 * size + z2];
buf_cubic_x[3] = noise[y3 * size * size + x3 * size + z2];
buf_cubic_y[3] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_z[2] = _cubicInterpolate(buf_cubic_y, yinternal);
buf_cubic_x[0] = noise[y0 * size * size + x0 * size + z3];
buf_cubic_x[1] = noise[y0 * size * size + x1 * size + z3];
buf_cubic_x[2] = noise[y0 * size * size + x2 * size + z3];
buf_cubic_x[3] = noise[y0 * size * size + x3 * size + z3];
buf_cubic_y[0] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y1 * size * size + x0 * size + z3];
buf_cubic_x[1] = noise[y1 * size * size + x1 * size + z3];
buf_cubic_x[2] = noise[y1 * size * size + x2 * size + z3];
buf_cubic_x[3] = noise[y1 * size * size + x3 * size + z3];
buf_cubic_y[1] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y2 * size * size + x0 * size + z3];
buf_cubic_x[1] = noise[y2 * size * size + x1 * size + z3];
buf_cubic_x[2] = noise[y2 * size * size + x2 * size + z3];
buf_cubic_x[3] = noise[y2 * size * size + x3 * size + z3];
buf_cubic_y[2] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_x[0] = noise[y3 * size * size + x0 * size + z3];
buf_cubic_x[1] = noise[y3 * size * size + x1 * size + z3];
buf_cubic_x[2] = noise[y3 * size * size + x2 * size + z3];
buf_cubic_x[3] = noise[y3 * size * size + x3 * size + z3];
buf_cubic_y[3] = _cubicInterpolate(buf_cubic_x, xinternal);
buf_cubic_z[3] = _cubicInterpolate(buf_cubic_y, yinternal);
return _cubicInterpolate(buf_cubic_z, zinternal);
}
static inline double _get3DLevelValue(NoiseGenerator* generator, NoiseLevel* level, double x, double y, double z)
{
return _get3DRawNoiseValue(generator, x / level->scaling + level->xoffset * generator->size3, y / level->scaling + level->yoffset * generator->size3, z / level->scaling + level->zoffset * generator->size3) * level->height;
}
double noiseGet3DLevel(NoiseGenerator* generator, int level, double x, double y, double z)
{
if (level >= 0 && level < generator->level_count)
{
return _get3DLevelValue(generator, generator->levels + level, x, y, z);
}
else
{
return 0.0;
}
}
double noiseGet3DTotal(NoiseGenerator* generator, double x, double y, double z)
{
int level;
double result;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
result += _get3DLevelValue(generator, generator->levels + level, x, y, z);
}
return result + generator->height_offset;
}
double noiseGet3DDetail(NoiseGenerator* generator, double x, double y, double z, double detail)
{
int level;
double result, height, factor;
result = 0.0;
for (level = 0; level < generator->level_count; level++)
{
height = generator->levels[level].height;
factor = 1.0;
if (height < detail * 0.25)
{
break;
}
else if (height < detail * 0.5)
{
factor = (detail * 0.5 - height) / 0.25;
}
result += _get3DLevelValue(generator, generator->levels + level, x, y, z) * factor;
}
return result + generator->height_offset;
}

812
src/render.c Normal file
View file

@ -0,0 +1,812 @@
#include "shared/types.h"
#include "shared/constants.h"
#include "shared/functions.h"
#include "shared/system.h"
#include <stdlib.h>
#include <math.h>
#include "IL/il.h"
#include "IL/ilu.h"
int render_width;
int render_height;
int render_quality;
static int _pixel_count;
static Array* render_zone = NULL;
static RenderFragment* scanline_up;
static RenderFragment* scanline_down;
static int scanline_left;
static int scanline_right;
static Color background_color;
static volatile int _dirty_left;
static volatile int _dirty_right;
static volatile int _dirty_up;
static volatile int _dirty_down;
static volatile int _dirty_count;
static Mutex* _lock;
static volatile int _interrupt = 0;
static volatile int _progress_pixels;
static volatile double _progress = 0.0;
static volatile double _progress_step_start = 0.0;
static volatile double _progress_step_length = 1.0;
#define RENDER_INVERSE 1
#define RENDER_WIREFRAME 1
static void _previewResize(int width, int height) {}
static void _previewClear(Color col) {}
static void _previewDraw(int x, int y, Color col) {}
static void _previewUpdate(double progress) {}
static PreviewCallbackResize _cb_preview_resize = _previewResize;
static PreviewCallbackClear _cb_preview_clear = _previewClear;
static PreviewCallbackDraw _cb_preview_draw = _previewDraw;
static PreviewCallbackUpdate _cb_preview_update = _previewUpdate;
void renderSave(FILE* f)
{
}
void renderLoad(FILE* f)
{
}
void renderSetSize(int width, int height)
{
int x;
int y;
if (render_zone != NULL)
{
/* Delete previous render zone */
for (x = 0; x < render_width; x++)
{
for (y = 0; y < render_height; y++)
{
arrayDelete(render_zone + (y * render_width + x));
}
}
free(render_zone);
free(scanline_up);
free(scanline_down);
}
if (_lock == NULL)
{
_lock = mutexCreate();
}
render_width = width;
render_height = height;
render_zone = malloc(sizeof(Array) * width * height);
scanline_left = 0;
scanline_right = render_width - 1;
scanline_up = malloc(sizeof(RenderFragment) * width);
scanline_down = malloc(sizeof(RenderFragment) * width);
_dirty_left = render_width;
_dirty_right = -1;
_dirty_down = render_height;
_dirty_up = -1;
_dirty_count = 0;
_pixel_count = render_width * render_height;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
arrayCreate(render_zone + (y * width + x), sizeof(RenderFragment));
}
}
_cb_preview_resize(render_width, render_height);
_cb_preview_clear(background_color);
}
void renderSetQuality(int quality)
{
if (quality < 1)
{
render_quality = 1;
}
else
{
render_quality = quality;
}
}
void renderClear()
{
int x;
int y;
for (x = 0; x < render_width; x++)
{
for (y = 0; y < render_height; y++)
{
arrayClear(render_zone + (y * render_width + x));
}
}
scanline_left = 0;
scanline_right = render_width - 1;
_progress = 0.0;
_interrupt = 0;
_cb_preview_clear(background_color);
_cb_preview_update(_progress * _progress_step_length + _progress_step_start);
_dirty_left = render_width;
_dirty_right = -1;
_dirty_down = render_height;
_dirty_up = -1;
_dirty_count = 0;
}
void renderInterrupt()
{
_interrupt = 1;
}
void renderSetBackgroundColor(Color* col)
{
background_color = *col;
}
double renderGetPrecision(Vector3 location)
{
Vector3 projected;
projected = cameraProject(location);
projected.x += 1.0;
//projected.y += 1.0;
return v3Norm(v3Sub(cameraUnproject(projected), location)); // / (double)render_quality;
}
int _sortRenderFragment(void const* a, void const* b)
{
double za, zb;
za = ((RenderFragment*)a)->z;
zb = ((RenderFragment*)b)->z;
if (za > zb)
{
return 1;
}
else if (za < zb)
{
return -1;
}
else
{
return 0;
}
}
static Color _getPixelColor(Array* pixel_data)
{
RenderFragment* fragment;
Color result = background_color;
int i;
if (pixel_data->length > 0)
{
for (i = 0; i < pixel_data->length; i++)
{
fragment = ((RenderFragment*)pixel_data->data) + i;
colorMask(&result, &(fragment->vertex.color));
}
}
return result;
}
static inline void _setDirtyPixel(Array* pixel_data, int x, int y)
{
pixel_data->dirty = 1;
if (x < _dirty_left)
{
_dirty_left = x;
}
if (x > _dirty_right)
{
_dirty_right = x;
}
if (y < _dirty_down)
{
_dirty_down = y;
}
if (y > _dirty_up)
{
_dirty_up = y;
}
_dirty_count++;
}
static void _processDirtyPixels()
{
Color col;
Array* pixel_data;
int x, y;
for (y = _dirty_down; y <= _dirty_up; y++)
{
for (x = _dirty_left; x <= _dirty_right; x++)
{
pixel_data = render_zone + y * render_width + x;
if (pixel_data->dirty)
{
col = _getPixelColor(pixel_data);
_cb_preview_draw(x, y, col);
pixel_data->dirty = 0;
}
}
}
_cb_preview_update(_progress * _progress_step_length + _progress_step_start);
_dirty_left = render_width;
_dirty_right = -1;
_dirty_down = render_height;
_dirty_up = -1;
_dirty_count = 0;
}
void renderAddFragment(RenderFragment* fragment)
{
Array* pixel_data;
int x = fragment->x;
int y = fragment->y;
double z = fragment->z;
int i, dirty;
int fragments_count;
RenderFragment* fragments;
dirty = 0;
if (x >= 0 && x < render_width && y >= 0 && y < render_height && z > 1.0)
{
pixel_data = render_zone + (y * render_width + x);
fragments = (RenderFragment*)pixel_data->data;
fragments_count = pixel_data->length;
if (fragments_count == 0)
{
arrayAppend(pixel_data, fragment);
dirty = 1;
}
else if (fragments[0].z > z)
{
if (fragments[0].vertex.color.a < 1.0)
{
arrayInsert(pixel_data, fragment, 0);
dirty = 1;
}
}
else
{
for (i = 1; i <= fragments_count; i++)
{
if ((i == fragments_count) || (fragments[i].z > z))
{
if (fragment->vertex.color.a > 0.999999)
{
if (i > 1)
{
arrayLStrip(pixel_data, i - 1);
}
arrayReplace(pixel_data, fragment, 0);
}
else if (i == fragments_count)
{
arrayAppend(pixel_data, fragment);
}
else
{
arrayInsert(pixel_data, fragment, i);
}
dirty = 1;
break;
}
}
}
if (dirty)
{
_setDirtyPixel(pixel_data, x, y);
}
}
}
void renderPushFragment(int x, int y, double z, Vertex* vertex)
{
RenderFragment fragment;
fragment.x = x;
fragment.y = y;
fragment.z = z;
fragment.vertex = *vertex;
renderAddFragment(&fragment);
}
static void __vertexGetDiff(Vertex* v1, Vertex* v2, Vertex* result)
{
result->location.x = v2->location.x - v1->location.x;
result->location.y = v2->location.y - v1->location.y;
result->location.z = v2->location.z - v1->location.z;
result->normal.x = v2->normal.x - v1->normal.x;
result->normal.y = v2->normal.y - v1->normal.y;
result->normal.z = v2->normal.z - v1->normal.z;
result->color.r = v2->color.r - v1->color.r;
result->color.g = v2->color.g - v1->color.g;
result->color.b = v2->color.b - v1->color.b;
result->color.a = v2->color.a - v1->color.a;
result->callback = v1->callback;
}
static void __vertexInterpolate(Vertex* v1, Vertex* diff, double value, Vertex* result)
{
result->location.x = v1->location.x + diff->location.x * value;
result->location.y = v1->location.y + diff->location.y * value;
result->location.z = v1->location.z + diff->location.z * value;
result->normal.x = v1->normal.x + diff->normal.x * value;
result->normal.y = v1->normal.y + diff->normal.y * value;
result->normal.z = v1->normal.z + diff->normal.z * value;
result->color.r = v1->color.r + diff->color.r * value;
result->color.g = v1->color.g + diff->color.g * value;
result->color.b = v1->color.b + diff->color.b * value;
result->color.a = v1->color.a + diff->color.a * value;
result->callback = v1->callback;
}
static void __pushScanLinePoint(RenderFragment point)
{
if (point.x < 0 || point.x >= render_width)
{
return;
}
if (point.x > scanline_right)
{
scanline_right = point.x;
scanline_up[scanline_right] = point;
scanline_down[scanline_right] = point;
if (point.x < scanline_left)
{
scanline_left = point.x;
}
}
else if (point.x < scanline_left)
{
scanline_left = point.x;
scanline_up[scanline_left] = point;
scanline_down[scanline_left] = point;
}
else
{
if (point.y > scanline_up[point.x].y)
{
scanline_up[point.x] = point;
}
if (point.y < scanline_down[point.x].y)
{
scanline_down[point.x] = point;
}
}
}
static void __pushScanLineEdge(Vector3 v1, Vector3 v2, Vertex* vertex1, Vertex* vertex2)
{
double dx, dy, dz, fx;
Vertex diff;
int startx = lround(v1.x);
int endx = lround(v2.x);
int curx;
RenderFragment fragment;
if (endx < startx)
{
__pushScanLineEdge(v2, v1, vertex2, vertex1);
}
else if (endx < 0 || startx >= render_width)
{
return;
}
else if (startx == endx)
{
fragment.x = startx;
fragment.y = lround(v1.y);
fragment.z = v1.z;
fragment.vertex = *vertex1;
__pushScanLinePoint(fragment);
fragment.x = endx;
fragment.y = lround(v2.y);
fragment.z = v2.z;
fragment.vertex = *vertex2;
__pushScanLinePoint(fragment);
}
else
{
if (startx < 0)
{
startx = 0;
}
if (endx >= render_width)
{
endx = render_width - 1;
}
dx = v2.x - v1.x;
dy = v2.y - v1.y;
dz = v2.z - v1.z;
__vertexGetDiff(vertex1, vertex2, &diff);
for (curx = startx; curx <= endx; curx++)
{
fx = (double)curx + 0.5;
if (fx < v1.x)
{
fx = v1.x;
}
else if (fx > v2.x)
{
fx = v2.x;
}
fx = fx - v1.x;
fragment.x = curx;
fragment.y = lround(v1.y + dy * fx / dx);
fragment.z = v1.z + dz * fx / dx;
__vertexInterpolate(vertex1, &diff, fx / dx, &(fragment.vertex));
__pushScanLinePoint(fragment);
}
}
}
static void __clearScanLines()
{
int x;
for (x = scanline_left; x <= scanline_right; x++)
{
scanline_up[x].y = -1;
scanline_down[x].y = render_height;
}
scanline_left = render_width;
scanline_right = -1;
}
static void __renderScanLines()
{
int x, starty, endy, cury;
Vertex diff;
double dy, dz, fy;
RenderFragment up, down, current;
if (scanline_right > 0)
{
for (x = scanline_left; x <= scanline_right; x++)
{
up = scanline_up[x];
down = scanline_down[x];
starty = down.y;
endy = up.y;
if (endy < 0 || starty >= render_height)
{
continue;
}
if (starty < 0)
{
starty = 0;
}
if (endy >= render_height)
{
endy = render_height - 1;
}
dy = (double)(up.y - down.y);
dz = up.z - down.z;
__vertexGetDiff(&down.vertex, &up.vertex, &diff);
current.x = x;
for (cury = starty; cury <= endy; cury++)
{
fy = (double)cury - down.y;
current.y = cury;
current.z = down.z + dz * fy / dy;
__vertexInterpolate(&down.vertex, &diff, fy / dy, &current.vertex);
#ifdef RENDER_WIREFRAME
if (cury == starty || cury == endy)
{
current.vertex.color = COLOR_RED;
}
#endif
renderAddFragment(&current);
}
}
}
}
void renderPushTriangle(Vertex* v1, Vertex* v2, Vertex* v3)
{
Vector3 p1, p2, p3;
double limit_width = (double)(render_width - 1);
double limit_height = (double)(render_height - 1);
p1 = cameraProject(v1->location);
p2 = cameraProject(v2->location);
p3 = cameraProject(v3->location);
/* Filter if outside screen */
if (p1.z < 1.0 || p2.z < 1.0 || p3.z < 1.0 || (p1.x < 0.0 && p2.x < 0.0 && p3.x < 0.0) || (p1.y < 0.0 && p2.y < 0.0 && p3.y < 0.0) || (p1.x > limit_width && p2.x > limit_width && p3.x > limit_width) || (p1.y > limit_height && p2.y > limit_height && p3.y > limit_height))
{
return;
}
__clearScanLines();
__pushScanLineEdge(p1, p2, v1, v2);
__pushScanLineEdge(p2, p3, v2, v3);
__pushScanLineEdge(p3, p1, v3, v1);
mutexAcquire(_lock);
__renderScanLines();
mutexRelease(_lock);
}
void renderPushQuad(Vertex* v1, Vertex* v2, Vertex* v3, Vertex* v4)
{
renderPushTriangle(v2, v3, v1);
renderPushTriangle(v4, v1, v3);
}
typedef struct {
int startx;
int endx;
int starty;
int endy;
int finished;
int interrupt;
Thread* thread;
} RenderChunk;
void* _renderPostProcessChunk(void* data)
{
int x, y, i;
int dirty;
Array* pixel_data;
RenderFragment* fragments;
RenderChunk* chunk = (RenderChunk*)data;
#ifdef RENDER_INVERSE
for (y = render_height - 1 - chunk->starty; y >= render_height - 1 - chunk->endy; y--)
#else
for (y = chunk->starty; y <= chunk->endy; y++)
#endif
{
for (x = chunk->startx; x <= chunk->endx; x++)
{
pixel_data = render_zone + (y * render_width + x);
fragments = (RenderFragment*)pixel_data->data;
dirty = 0;
for (i = 0; i < pixel_data->length; i++)
{
if (fragments[i].vertex.callback)
{
if (fragments[i].vertex.callback(fragments + i))
{
/* TODO Store over-exposure */
colorNormalize(&fragments[i].vertex.color);
dirty = 1;
}
}
}
if (dirty)
{
mutexAcquire(_lock);
_setDirtyPixel(pixel_data, x, y);
mutexRelease(_lock);
}
_progress_pixels++;
}
if (chunk->interrupt)
{
break;
}
}
chunk->finished = 1;
return NULL;
}
#define MAX_CHUNKS 8
void renderPostProcess(int nbchunks)
{
volatile RenderChunk chunks[MAX_CHUNKS];
int i;
int x, y, dx, dy, nx, ny;
int loops, running;
if (nbchunks > MAX_CHUNKS)
{
nbchunks = MAX_CHUNKS;
}
if (nbchunks < 1)
{
nbchunks = 1;
}
nx = 10;
ny = 10;
dx = render_width / nx;
dy = render_height / ny;
x = 0;
y = 0;
_progress_pixels = 0;
for (i = 0; i < nbchunks; i++)
{
chunks[i].thread = NULL;
}
running = 0;
loops = 0;
while ((y < ny && !_interrupt) || running > 0)
{
timeSleepMs(100);
for (i = 0; i < nbchunks; i++)
{
if (chunks[i].thread)
{
if (chunks[i].finished)
{
threadJoin(chunks[i].thread);
chunks[i].thread = NULL;
running--;
}
else if (_interrupt)
{
chunks[i].interrupt = 1;
}
}
if (y < ny && !chunks[i].thread && !_interrupt)
{
chunks[i].finished = 0;
chunks[i].interrupt = 0;
chunks[i].startx = x * dx;
if (x == nx)
{
chunks[i].endx = render_width - 1;
}
else
{
chunks[i].endx = (x + 1) * dx - 1;
}
chunks[i].starty = y * dy;
if (y == ny)
{
chunks[i].endy = render_height - 1;
}
else
{
chunks[i].endy = (y + 1) * dy - 1;
}
chunks[i].thread = threadCreate(_renderPostProcessChunk, (void*)(chunks + i));
running++;
if (++x >= nx)
{
y++;
x = 0;
}
}
}
if (++loops >= 10)
{
mutexAcquire(_lock);
_progress = (double)_progress_pixels / (double)_pixel_count;
_processDirtyPixels();
mutexRelease(_lock);
loops = 0;
}
}
_progress = 1.0;
_processDirtyPixels();
}
void renderUpdate()
{
mutexAcquire(_lock);
_processDirtyPixels();
mutexRelease(_lock);
}
void renderSaveToFile(const char* path)
{
ILuint image_id;
ilGenImages(1, &image_id);
ilBindImage(image_id);
Color result;
ILuint x, y;
ILuint rgba;
ILuint data[render_height * render_width];
ILenum error;
Array* pixel_data;
for (y = 0; y < render_height; y++)
{
for (x = 0; x < render_width; x++)
{
pixel_data = render_zone + (y * render_width + x);
result = _getPixelColor(pixel_data);
rgba = colorTo32BitRGBA(&result);
data[y * render_width + x] = rgba;
}
}
ilTexImage((ILuint)render_width, (ILuint)render_height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, data);
ilSaveImage(path);
ilDeleteImages(1, &image_id);
while ((error=ilGetError()) != IL_NO_ERROR)
{
fprintf(stderr, "IL ERROR : %s\n", iluErrorString(error));
}
}
void renderSetPreviewCallbacks(PreviewCallbackResize resize, PreviewCallbackClear clear, PreviewCallbackDraw draw, PreviewCallbackUpdate update)
{
_cb_preview_resize = resize ? resize : _previewResize;
_cb_preview_clear = clear ? clear : _previewClear;
_cb_preview_draw = draw ? draw : _previewDraw;
_cb_preview_update = update ? update : _previewUpdate;
_cb_preview_resize(render_width, render_height);
/* TODO Send all pixels ? */
}
int renderSetNextProgressStep(double start, double end)
{
if (_interrupt)
{
return 0;
}
else
{
_progress = 0.0;
_progress_step_start = start;
_progress_step_length = end - start;
return 1;
}
}
int renderTellProgress(double progress)
{
if (_interrupt)
{
return 0;
}
else
{
_progress = progress;
return 1;
}
}

16
src/shared/constants.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef _PAYSAGES_CONSTANTS_H_
#define _PAYSAGES_CONSTANTS_H_
#include "types.h"
extern Color COLOR_TRANSPARENT;
extern Color COLOR_BLACK;
extern Color COLOR_RED;
extern Color COLOR_GREEN;
extern Color COLOR_BLUE;
extern Color COLOR_WHITE;
extern Color COLOR_GREY;
extern Vector3 VECTOR_ZERO;
#endif

215
src/shared/functions.h Normal file
View file

@ -0,0 +1,215 @@
#ifndef _PAYSAGES_FUNCTIONS_H_
#define _PAYSAGES_FUNCTIONS_H_
#include "types.h"
#include <stdio.h>
/* array.c */
void arrayCreate(Array* array, int item_size);
void arrayDelete(Array* array);
void* arrayAppend(Array* array, void* item);
void arrayInsert(Array* array, void* item, int position);
void arrayReplace(Array* array, void* item, int position);
void arrayLStrip(Array* array, int count);
void arrayClear(Array* array);
/* auto.c */
void autoInit();
void autoSave(char* filepath);
void autoLoad(char* filepath);
void autoSetDaytime(int hour, int minute);
void autoSetDaytimeFraction(double daytime);
void autoSetRenderQuality(int quality);
void autoGenRealisticLandscape(int seed);
void autoRenderSceneTwoPass();
void autoRenderSceneRayTracing();
/* camera.c */
void cameraSave(FILE* f);
void cameraLoad(FILE* f);
void cameraSetLocation(double x, double y, double z);
void cameraSetTarget(double x, double y, double z);
void cameraSetAngle(double angle);
Vector3 cameraProject(Vector3 point);
Vector3 cameraUnproject(Vector3 point);
void cameraProjectToFragment(double x, double y, double z, RenderFragment* result);
void cameraPushOverlay(Color col, f_RenderFragmentCallback callback);
/* color.c */
void colorSave(Color col, FILE* f);
Color colorLoad(FILE* f);
unsigned int colorTo32BitRGBA(Color* col);
unsigned int colorTo32BitBGRA(Color* col);
unsigned int colorTo32BitARGB(Color* col);
unsigned int colorTo32BitABGR(Color* col);
void colorMask(Color* base, Color* mask);
double colorNormalize(Color* col);
double colorGetValue(Color* col);
ColorGradation colorGradationCreate();
void colorGradationAdd(ColorGradation* gradation, double value, Color* col);
void colorGradationAddRgba(ColorGradation* gradation, double value, double r, double g, double b, double a);
Color colorGradationGet(ColorGradation* gradation, double value);
/* euclid.c */
void v3Save(Vector3 v, FILE* f);
Vector3 v3Load(FILE* f);
void m4Save(Matrix4 m, FILE* f);
Matrix4 m4Load(FILE* f);
Vector3 v3Translate(Vector3 v1, double x, double y, double z);
Vector3 v3Add(Vector3 v1, Vector3 v2);
Vector3 v3Sub(Vector3 v1, Vector3 v2);
Vector3 v3Neg(Vector3 v);
Vector3 v3Scale(Vector3 v, double scale);
double v3Norm(Vector3 v);
Vector3 v3Normalize(Vector3 v);
double v3Dot(Vector3 v1, Vector3 v2);
Vector3 v3Cross(Vector3 v1, Vector3 v2);
Matrix4 m4NewIdentity();
Matrix4 m4Mult(Matrix4 m1, Matrix4 m2);
Vector3 m4MultPoint(Matrix4 m, Vector3 v);
Vector3 m4Transform(Matrix4 m, Vector3 v);
Matrix4 m4Transpose(Matrix4 m);
Matrix4 m4NewScale(double x, double y, double z);
Matrix4 m4NewTranslate(double x, double y, double z);
Matrix4 m4NewRotateX(double angle);
Matrix4 m4NewRotateY(double angle);
Matrix4 m4NewRotateZ(double angle);
Matrix4 m4NewRotateAxis(double angle, Vector3 axis);
Matrix4 m4NewRotateEuler(double heading, double attitude, double bank);
Matrix4 m4NewRotateTripleAxis(Vector3 x, Vector3 y, Vector3 z);
Matrix4 m4NewLookAt(Vector3 eye, Vector3 at, Vector3 up);
Matrix4 m4NewPerspective(double fov_y, double aspect, double near, double far);
double m4Determinant(Matrix4 m);
Matrix4 m4Inverse(Matrix4 m);
/* fog.c */
void fogSave(FILE* f);
void fogLoad(FILE* f);
void fogSetColor(Color col);
void fogSetDistance(double near, double far);
Color fogApplyToLocation(Vector3 location, Color base);
/* gui.c */
void guiInit();
void guiStart();
void guiUpdate();
/* lighting.c */
void lightingSave(FILE* f);
void lightingLoad(FILE* f);
void lightingSetSunDirection(double x, double y, double z);
void lightingSetSunAngle(double hor, double ver);
void lightingSetSunColor(Color col);
Color lightingApply(Vector3 location, Vector3 normal, double shadowing, Color base, double reflection, double shininess);
/* modifiers.c */
HeightModifier* modifierCreate();
void modifierDelete(HeightModifier* modifier);
void modifierSave(HeightModifier* zone, FILE* f);
void modifierLoad(HeightModifier* zone, FILE* f);
Zone* modifierGetZone(HeightModifier* modifier);
void modifierActionAddValue(HeightModifier* modifier, double value);
void modifierActionFixValue(HeightModifier* modifier, double value);
Vector3 modifierApply(HeightModifier* modifier, Vector3 location);
/* noise.c */
NoiseGenerator* noiseCreateGenerator();
void noiseDeleteGenerator(NoiseGenerator* generator);
void noiseSave(NoiseGenerator* perlin, FILE* f);
void noiseLoad(NoiseGenerator* perlin, FILE* f);
void noiseCopy(NoiseGenerator* source, NoiseGenerator* destination);
void noiseGenerateBaseNoise(NoiseGenerator* generator, int size);
int noiseGetBaseSize(NoiseGenerator* generator);
double noiseGetMaxValue(NoiseGenerator* generator);
int noiseGetLevelCount(NoiseGenerator* generator);
void noiseClearLevels(NoiseGenerator* generator);
void noiseAddLevel(NoiseGenerator* generator, NoiseLevel level);
void noiseAddLevelSimple(NoiseGenerator* generator, double scaling, double height);
void noiseAddLevels(NoiseGenerator* generator, int level_count, NoiseLevel start_level, double scaling_factor, double height_factor, int randomize_offset);
void noiseAddLevelsSimple(NoiseGenerator* generator, int level_count, double scaling, double height);
int noiseGetLevel(NoiseGenerator* generator, int level, NoiseLevel* params);
void noiseSetLevel(NoiseGenerator* generator, int level, NoiseLevel params);
void noiseSetLevelSimple(NoiseGenerator* generator, int level, double scaling, double height);
void noiseNormalizeHeight(NoiseGenerator* generator, double min_height, double max_height, int adjust_scaling);
double noiseGet1DLevel(NoiseGenerator* generator, int level, double x);
double noiseGet1DTotal(NoiseGenerator* generator, double x);
double noiseGet1DDetail(NoiseGenerator* generator, double x, double detail);
double noiseGet2DLevel(NoiseGenerator* generator, int level, double x, double y);
double noiseGet2DTotal(NoiseGenerator* generator, double x, double y);
double noiseGet2DDetail(NoiseGenerator* generator, double x, double y, double detail);
double noiseGet3DLevel(NoiseGenerator* generator, int level, double x, double y, double z);
double noiseGet3DTotal(NoiseGenerator* generator, double x, double y, double z);
double noiseGet3DDetail(NoiseGenerator* generator, double x, double y, double z, double detail);
/* render.c */
void renderSave(FILE* f);
void renderLoad(FILE* f);
void renderSetSize(int width, int height);
void renderSetQuality(int quality);
void renderClear();
void renderUpdate();
void renderInterrupt();
void renderSetBackgroundColor(Color* col);
double renderGetPrecision(Vector3 location);
void renderAddFragment(RenderFragment* fragment);
void renderPushFragment(int x, int y, double z, Vertex* vertex);
void renderPushTriangle(Vertex* v1, Vertex* v2, Vertex* v3);
void renderPushQuad(Vertex* v1, Vertex* v2, Vertex* v3, Vertex* v4);
void renderPostProcess(int nbchunks);
void renderSaveToFile(const char* path);
void renderSetPreviewCallbacks(PreviewCallbackResize resize, PreviewCallbackClear clear, PreviewCallbackDraw draw, PreviewCallbackUpdate update);
int renderSetNextProgressStep(double start, double end);
int renderTellProgress(double progress);
/* sky.c */
void skySave(FILE* f);
void skyLoad(FILE* f);
Color skyGetColor(Vector3 start, Vector3 direction);
Color skyProjectRay(Vector3 start, Vector3 direction);
void skySetGradation(ColorGradation grad);
void skyRender(RenderProgressCallback callback);
/* terrain.c */
void terrainSave(FILE* f);
void terrainLoad(FILE* f);
void terrainInit();
NoiseGenerator* terrainGetNoiseGenerator();
void terrainSetNoiseGenerator(NoiseGenerator* generator);
void terrainAddTexture(Texture* tex, double subsurf_scale, Zone* zone, double border_scaling);
void terrainAddModifier(HeightModifier* modifier);
double terrainGetHeight(double x, double z);
double terrainGetHeightNormalized(double x, double z);
Color terrainGetColor(double x, double z, double precision);
double terrainGetShadow(Vector3 start, Vector3 direction);
int terrainProjectRay(Vector3 start, Vector3 direction, Vector3* hit_point, Color* hit_color);
void terrainSetChunkSize(double min_size, double visible_size);
void terrainRender(RenderProgressCallback callback);
/* textures.c */
void texturesSave(FILE* f);
void texturesLoad(FILE* f);
Texture* textureCreateFromFile(const char* filename);
Color textureApply(Texture* tex, Vector3 location, Vector3 normal);
/* tools.c */
double toolsRandom();
double toolsBicubicInterpolate(double stencil[16], double x, double y);
void toolsFloat2DMapCopy(double* src, double* dest, int src_xstart, int src_ystart, int dest_xstart, int dest_ystart, int xsize, int ysize, int src_xstep, int src_ystep, int dest_xstep, int dest_ystep);
Vector3 toolsGetNormalFromTriangle(Vector3 center, Vector3 bottom, Vector3 right);
void toolsSaveDouble(FILE* f, double value);
double toolsLoadDouble(FILE* f);
void toolsSaveInt(FILE* f, int value);
int toolsLoadInt(FILE* f);
/* zone.c */
Zone* zoneCreate();
void zoneDelete(Zone* zone);
void zoneSave(Zone* zone, FILE* f);
void zoneLoad(Zone* zone, FILE* f);
void zoneIncludeCircleArea(Zone* zone, double value, double centerx, double centerz, double softradius, double hardradius);
void zoneExcludeCircleArea(Zone* zone, double centerx, double centerz, double softradius, double hardradius);
void zoneAddHeightRange(Zone* zone, double value, double hardmin, double softmin, double softmax, double hardmax);
void zoneAddSteepnessRange(Zone* zone, double value, double hardmin, double softmin, double softmax, double hardmax);
double zoneGetValue(Zone* zone, Vector3 location, Vector3 normal);
#endif

16
src/shared/globals.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef _PAYSAGES_GLOBALS_H_
#define _PAYSAGES_GLOBALS_H_
#include "types.h"
extern Vector3 camera_location;
extern int render_width;
extern int render_height;
extern int render_quality;
extern double sun_color_lum;
extern Vector3 sun_direction;
extern Vector3 sun_direction_inv;
#endif

43
src/shared/system.h Normal file
View file

@ -0,0 +1,43 @@
#include <glib.h>
typedef GThread Thread;
typedef void*(*ThreadFunction)(void* data);
static inline Thread* threadCreate(ThreadFunction function, void* data)
{
GError* error;
return g_thread_create((GThreadFunc)function, data, 1, &error);
}
static inline void* threadJoin(Thread* thread)
{
return g_thread_join(thread);
}
typedef GMutex Mutex;
static inline Mutex* mutexCreate()
{
return g_mutex_new();
}
static inline void mutexDestroy(Mutex* mutex)
{
g_mutex_free(mutex);
}
static inline void mutexAcquire(Mutex* mutex)
{
g_mutex_lock(mutex);
}
static inline void mutexRelease(Mutex* mutex)
{
g_mutex_unlock(mutex);
}
static inline void timeSleepMs(unsigned long ms)
{
g_usleep(ms * 1000);
}

125
src/shared/types.h Normal file
View file

@ -0,0 +1,125 @@
#ifndef _PAYSAGES_TYPES_H_
#define _PAYSAGES_TYPES_H_
typedef struct
{
double x;
double y;
double z;
} Vector3;
typedef struct
{
double a;
double b;
double c;
double d;
double e;
double f;
double g;
double h;
double i;
double j;
double k;
double l;
double m;
double n;
double o;
double p;
} Matrix4;
typedef struct
{
double r;
double g;
double b;
double a;
} Color;
#define MAX_COLORGRADATION_PARTS 10
typedef struct
{
double start;
Color col;
} _ColorGradationPart;
typedef struct
{
int nbparts;
_ColorGradationPart parts[MAX_COLORGRADATION_PARTS];
} ColorGradation;
struct RenderFragment;
typedef int(*f_RenderFragmentCallback)(struct RenderFragment*);
typedef struct
{
Vector3 location;
Vector3 normal;
Color color;
f_RenderFragmentCallback callback;
} Vertex;
typedef struct RenderFragment
{
int x;
int y;
double z;
Vertex vertex;
} RenderFragment;
typedef struct
{
int width;
int height;
} RenderContext;
typedef struct
{
int length;
int alloc_length;
int item_size;
int dirty;
void* data;
} Array;
typedef struct {
void* pixels;
int bytes_per_pixel;
int picture_width;
int picture_height;
double scaling_x;
double scaling_y;
double scaling_z;
} Texture;
struct NoiseLevel
{
double scaling;
double height;
double xoffset;
double yoffset;
double zoffset;
};
typedef struct NoiseLevel NoiseLevel;
typedef struct NoiseGenerator NoiseGenerator;
typedef struct Zone Zone;
typedef struct HeightModifier HeightModifier;
typedef void (*PreviewCallbackResize)(int width, int height);
typedef void (*PreviewCallbackClear)(Color col);
typedef void (*PreviewCallbackDraw)(int x, int y, Color col);
typedef void (*PreviewCallbackUpdate)(double progress);
typedef int (*RenderProgressCallback)(double progress);
typedef struct
{
int hit;
Color hit_color;
Vector3 hit_location;
} RayCastingResult;
typedef RayCastingResult (*RayCastingFunction)(Vector3 start, Vector3 direction);
#endif

131
src/sky.c Normal file
View file

@ -0,0 +1,131 @@
#include <stdlib.h>
#include <math.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/globals.h"
#include "shared/constants.h"
#include "clouds.h"
ColorGradation _gradation;
#define SPHERE_SIZE 1000.0
void skySave(FILE* f)
{
}
void skyLoad(FILE* f)
{
}
Color skyGetColor(Vector3 start, Vector3 direction)
{
direction = v3Normalize(direction);
return colorGradationGet(&_gradation, direction.y * 0.5 + 0.5);
}
Color skyProjectRay(Vector3 start, Vector3 direction)
{
Color color_sky, color_clouds;
direction = v3Normalize(direction);
color_sky = skyGetColor(start, direction);
color_clouds = cloudsGetColor(start, v3Add(start, v3Scale(direction, SPHERE_SIZE)));
colorMask(&color_sky, &color_clouds);
return color_sky;
}
void skySetGradation(ColorGradation grad)
{
_gradation = grad;
}
static int _postProcessFragment(RenderFragment* fragment)
{
Vector3 location, direction;
Color color_sky, color_clouds;
location = fragment->vertex.location;
direction = v3Sub(location, camera_location);
color_sky = skyGetColor(camera_location, v3Normalize(direction));
color_clouds = cloudsGetColor(camera_location, v3Add(camera_location, v3Scale(direction, 10.0)));
colorMask(&color_sky, &color_clouds);
fragment->vertex.color = color_sky;
return 1;
}
void skyRender(RenderProgressCallback callback)
{
int res_i, res_j;
int i, j;
double step_i, step_j;
double current_i, current_j;
Vertex vertex1, vertex2, vertex3, vertex4;
Color col;
Vector3 direction;
res_i = render_quality * 20;
res_j = render_quality * 10;
step_i = M_PI * 2.0 / (double)res_i;
step_j = M_PI / (double)res_j;
col.r = 0.0;
col.g = 0.0;
col.a = 1.0;
for (j = 0; j < res_j; j++)
{
if (!callback((double)j / (double)(res_j - 1)))
{
return;
}
current_j = (double)(j - res_j / 2) * step_j;
for (i = 0; i < res_i; i++)
{
current_i = (double)i * step_i;
direction.x = SPHERE_SIZE * cos(current_i) * cos(current_j);
direction.y = SPHERE_SIZE * sin(current_j);
direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j);
vertex1.location = v3Add(camera_location, direction);
col.b = sin(direction.x) * sin(direction.x) * cos(direction.z) * cos(direction.z);
vertex1.color = col;
vertex1.callback = _postProcessFragment;
direction.x = SPHERE_SIZE * cos(current_i + step_i) * cos(current_j);
direction.y = SPHERE_SIZE * sin(current_j);
direction.z = SPHERE_SIZE * sin(current_i + step_i) * cos(current_j);
vertex2.location = v3Add(camera_location, direction);
col.b = sin(direction.x) * sin(direction.x) * cos(direction.z) * cos(direction.z);
vertex2.color = col;
vertex2.callback = _postProcessFragment;
direction.x = SPHERE_SIZE * cos(current_i + step_i) * cos(current_j + step_j);
direction.y = SPHERE_SIZE * sin(current_j + step_j);
direction.z = SPHERE_SIZE * sin(current_i + step_i) * cos(current_j + step_j);
vertex3.location = v3Add(camera_location, direction);
col.b = sin(direction.x) * sin(direction.x) * cos(direction.z) * cos(direction.z);
vertex3.color = col;
vertex3.callback = _postProcessFragment;
direction.x = SPHERE_SIZE * cos(current_i) * cos(current_j + step_j);
direction.y = SPHERE_SIZE * sin(current_j + step_j);
direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j + step_j);
vertex4.location = v3Add(camera_location, direction);
col.b = sin(direction.x) * sin(direction.x) * cos(direction.z) * cos(direction.z);
vertex4.color = col;
vertex4.callback = _postProcessFragment;
/* TODO Triangles at poles */
renderPushQuad(&vertex1, &vertex4, &vertex3, &vertex2);
}
}
}

402
src/terrain.c Normal file
View file

@ -0,0 +1,402 @@
#include <stdlib.h>
#include <math.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/globals.h"
#include "shared/constants.h"
#include "water.h"
#define MAX_TEXTURES 10
#define MAX_MODIFIERS 50
typedef struct {
Texture* tex;
double subsurf_scale;
Zone* zone;
double border_scaling;
} TerrainTexture;
static NoiseGenerator* _noise_height;
static NoiseGenerator* _noise_texture_borders;
static double _max_height = 1.0;
static double _base_chunk_size = 1.0, _visible_chunk_size = 1.0;
static int _texture_count = 0;
static TerrainTexture _textures[MAX_TEXTURES];
static int _modifiers_count = 0;
static HeightModifier* _modifiers[MAX_MODIFIERS];
void terrainSave(FILE* f)
{
noiseSave(_noise_height, f);
noiseSave(_noise_texture_borders, f);
toolsSaveDouble(f, _max_height);
toolsSaveDouble(f, _base_chunk_size);
toolsSaveDouble(f, _visible_chunk_size);
/* TODO Textures */
/* TODO Modifiers */
}
void terrainLoad(FILE* f)
{
noiseLoad(_noise_height, f);
noiseLoad(_noise_texture_borders, f);
_max_height = toolsLoadDouble(f);
_base_chunk_size = toolsLoadDouble(f);
_visible_chunk_size = toolsLoadDouble(f);
}
void terrainInit()
{
_noise_height = noiseCreateGenerator();
_max_height = noiseGetMaxValue(_noise_height);
_noise_texture_borders = noiseCreateGenerator();
noiseGenerateBaseNoise(_noise_texture_borders, 100);
noiseAddLevelsSimple(_noise_texture_borders, 10, 1.0, 1.0);
noiseNormalizeHeight(_noise_texture_borders, 0.0, 1.0, 0);
}
NoiseGenerator* terrainGetNoiseGenerator()
{
return _noise_height;
}
void terrainSetNoiseGenerator(NoiseGenerator* generator)
{
noiseCopy(generator, _noise_height);
_max_height = noiseGetMaxValue(_noise_height);
/* FIXME Max height depends on modifiers*/
}
void terrainAddTexture(Texture* tex, double subsurf_scale, Zone* zone, double border_scaling)
{
TerrainTexture ttex;
if (_texture_count < MAX_TEXTURES)
{
ttex.tex = tex;
ttex.subsurf_scale = subsurf_scale;
ttex.zone = zone;
ttex.border_scaling = border_scaling;
_textures[_texture_count++] = ttex;
}
}
void terrainAddModifier(HeightModifier* modifier)
{
if (_modifiers_count < MAX_MODIFIERS)
{
_modifiers[_modifiers_count++] = modifier;
}
}
static inline double _getHeight(double x, double z, double precision)
{
Vector3 location;
int i;
location.x = x;
location.y = noiseGet2DDetail(_noise_height, x, z, precision);
location.z = z;
for (i = 0; i < _modifiers_count; i++)
{
location = modifierApply(_modifiers[i], location);
}
return location.y;
}
static inline Vector3 _getPoint(double x, double z, double precision)
{
Vector3 result;
result.x = x;
result.y = _getHeight(x, z, precision);
result.z = z;
return result;
}
double terrainGetShadow(Vector3 start, Vector3 direction)
{
Vector3 inc_vector;
double inc_value, inc_base, inc_factor, height, diff, light, smoothing, length, water;
direction = v3Normalize(direction);
inc_factor = (double)render_quality;
inc_base = 1.0;
inc_value = inc_base / inc_factor;
smoothing = 0.03 * inc_factor;;
water = waterGetLightFactor(start);
light = 1.0;
length = 0.0;
do
{
inc_vector = v3Scale(direction, inc_value);
length += v3Norm(inc_vector);
start = v3Add(start, inc_vector);
height = _getHeight(start.x, start.z, inc_value);
diff = start.y - height;
if (diff < 0.0)
{
light += diff / smoothing;
}
if (diff < inc_base / inc_factor)
{
inc_value = inc_base / inc_factor;
}
else if (diff > inc_base)
{
inc_value = inc_base;
}
else
{
inc_value = diff;
}
} while (light > 0.0 && length < 50.0 && start.y <= _max_height);
light *= water;
if (light < 0.0)
{
return 1.0;
}
else
{
return 1.0 - light;
}
}
static inline Vector3 _getNormal(Vector3 point, double scale)
{
Vector3 dpoint, ref, normal;
ref.x = 0.0;
ref.y = 0.0;
dpoint = _getPoint(point.x - scale, point.z, scale * 0.3);
ref.z = -1.0;
normal = v3Normalize(v3Cross(ref, v3Sub(dpoint, point)));
dpoint = _getPoint(point.x + scale, point.z, scale * 0.3);
ref.z = 1.0;
normal = v3Add(normal, v3Normalize(v3Cross(ref, v3Sub(dpoint, point))));
ref.z = 0.0;
dpoint = _getPoint(point.x, point.z - scale, scale * 0.3);
ref.x = 1.0;
normal = v3Add(normal, v3Normalize(v3Cross(ref, v3Sub(dpoint, point))));
dpoint = _getPoint(point.x, point.z + scale, scale * 0.3);
ref.x = -1.0;
normal = v3Add(normal, v3Normalize(v3Cross(ref, v3Sub(dpoint, point))));
return v3Normalize(normal);
}
static Color _getColor(Vector3 point, double precision)
{
Vector3 normal;
Color color, tex_color;
int i;
double shadowed, coverage, value;
shadowed = terrainGetShadow(point, sun_direction_inv);
/* Apply textures and subsurface lighting */
color = COLOR_GREEN;
for (i = 0; i < _texture_count; i++)
{
/* TODO Don't recalculate normals for same precision */
/* TODO Don't compute textures that will be totally covered */
normal = _getNormal(point, precision * _textures[i].subsurf_scale);
coverage = zoneGetValue(_textures[i].zone, point, normal);
if (coverage > 0.0)
{
if (coverage < 1.0)
{
value = noiseGet2DTotal(_noise_texture_borders, point.x / _textures[i].border_scaling, point.z / _textures[i].border_scaling);
if (value < coverage)
{
/* TODO Make smoothness precision-dependant */
value = (coverage - value) / 0.1;
coverage = (value > 1.0) ? 1.0 : value;
}
else
{
coverage = 0.0;
}
}
if (coverage > 0.0)
{
tex_color = textureApply(_textures[i].tex, point, normal);
tex_color = lightingApply(point, normal, shadowed, tex_color, 0.1, 0.1);
tex_color.a = coverage;
colorMask(&color, &tex_color);
}
}
}
color = fogApplyToLocation(point, color);
//color = cloudsApplySegmentResult(color, camera_location, point);
return color;
}
int terrainProjectRay(Vector3 start, Vector3 direction, Vector3* hit_point, Color* hit_color)
{
Vector3 inc_vector;
double inc_value, inc_base, inc_factor, height, diff, length;
direction = v3Normalize(direction);
inc_factor = (double)render_quality;
inc_base = 1.0;
inc_value = inc_base / inc_factor;
length = 0.0;
do
{
inc_vector = v3Scale(direction, inc_value);
length += v3Norm(inc_vector);
start = v3Add(start, inc_vector);
height = _getHeight(start.x, start.z, inc_value);
diff = start.y - height;
if (diff < 0.0)
{
start.y = height;
*hit_point = start;
*hit_color = _getColor(start, inc_value);
return 1;
}
if (diff < inc_base / inc_factor)
{
inc_value = inc_base / inc_factor;
}
else if (diff > inc_base)
{
inc_value = inc_base;
}
else
{
inc_value = diff;
}
} while (length < 50.0 && start.y <= _max_height);
return 0;
}
static int _postProcessFragment(RenderFragment* fragment)
{
Vector3 point;
double precision;
point = fragment->vertex.location;
precision = renderGetPrecision(point);
point = _getPoint(point.x, point.z, precision);
fragment->vertex.color = _getColor(point, precision);
return 1;
}
static Vertex _getFirstPassVertex(double x, double z, double precision)
{
Vertex result;
double value;
result.location = _getPoint(x, z, 0.0);
value = sin(x) * sin(x) * cos(z) * cos(z);
result.color.r = value;
result.color.g = value;
result.color.b = value;
result.color.a = 1.0;
result.normal.x = result.normal.y = result.normal.z = 0.0;
result.callback = _postProcessFragment;
return result;
}
static void _renderQuad(double x, double z, double size)
{
Vertex v1, v2, v3, v4;
v1 = _getFirstPassVertex(x, z, size);
v2 = _getFirstPassVertex(x, z + size, size);
v3 = _getFirstPassVertex(x + size, z + size, size);
v4 = _getFirstPassVertex(x + size, z, size);
renderPushQuad(&v1, &v2, &v3, &v4);
}
double terrainGetHeight(double x, double z)
{
return _getHeight(x, z, 0.01 / (double)render_quality);
}
double terrainGetHeightNormalized(double x, double z)
{
return 0.5 + _getHeight(x, z, 0.01 / (double)render_quality) / (_max_height * 2.0);
}
Color terrainGetColor(double x, double z, double precision)
{
return _getColor(_getPoint(x, z, precision), precision);
}
void terrainSetChunkSize(double min_size, double visible_size)
{
_base_chunk_size = min_size;
_visible_chunk_size = visible_size;
}
void terrainRender(RenderProgressCallback callback)
{
int chunk_factor, chunk_count, i;
double cx = camera_location.x;
double cz = camera_location.z;
double radius_int, radius_ext, chunk_size;
chunk_factor = 1;
chunk_count = 2;
radius_int = 0.0;
radius_ext = _base_chunk_size;
chunk_size = _base_chunk_size;
while (radius_ext < 1000.0)
{
if (!callback(radius_ext / 1000.0))
{
return;
}
for (i = 0; i < chunk_count - 1; i++)
{
_renderQuad(cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
_renderQuad(cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
_renderQuad(cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
_renderQuad(cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
}
if (chunk_count % 64 == 0 && chunk_size / radius_int < _visible_chunk_size)
{
chunk_count /= 2;
chunk_factor *= 2;
/* TODO Fill in gaps with triangles */
}
chunk_count += 2;
chunk_size = _base_chunk_size * chunk_factor;
radius_int = radius_ext;
radius_ext += chunk_size;
}
}

137
src/textures.c Normal file
View file

@ -0,0 +1,137 @@
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/constants.h"
#include "shared/globals.h"
#include "IL/il.h"
#include "IL/ilu.h"
#define TEXTURES_MAX 50
static int _textures_count = 0;
static Texture _textures[TEXTURES_MAX];
void texturesSave(FILE* f)
{
}
void texturesLoad(FILE* f)
{
}
Texture* textureCreateFromFile(const char* filename)
{
Texture* result;
ILuint imageid;
if (_textures_count >= TEXTURES_MAX)
{
return _textures + (TEXTURES_MAX - 1);
}
else
{
result = _textures + _textures_count;
_textures_count++;
ilGenImages(1, &imageid);
ilBindImage(imageid);
ilLoadImage(filename);
result->bytes_per_pixel = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);
result->picture_width = ilGetInteger(IL_IMAGE_WIDTH);
result->picture_height = ilGetInteger(IL_IMAGE_HEIGHT);
result->pixels = malloc(result->bytes_per_pixel * result->picture_width * result->picture_height);
memcpy(result->pixels, ilGetData(), result->bytes_per_pixel * result->picture_width * result->picture_height);
result->scaling_x = 1.0;
result->scaling_y = 1.0;
result->scaling_z = 1.0;
ilDeleteImages(1, &imageid);
return result;
}
}
static inline Color _getRawValue(Texture* tex, int x, int y)
{
Color result;
void* texdata;
x = x % tex->picture_width;
if (x < 0)
{
x += tex->picture_width;
}
y = y % tex->picture_height;
if (y < 0)
{
y += tex->picture_height;
}
texdata = tex->pixels + (y * tex->picture_width + x) * tex->bytes_per_pixel;
result.r = ((double)(unsigned int)*((unsigned char*)texdata)) / 255.0;
result.g = ((double)(unsigned int)*((unsigned char*)(texdata + 1))) / 255.0;
result.b = ((double)(unsigned int)*((unsigned char*)(texdata + 2))) / 255.0;
return result;
}
static inline Color _getInterpolatedValue(Texture* tex, double fx, double fy)
{
Color result;
double r[16];
double g[16];
double b[16];
int ix, iy;
int sx, sy;
ix = (int)floor(fx);
iy = (int)floor(fy);
fx -= (double)ix;
fy -= (double)iy;
for (sy = 0; sy < 4; sy++)
{
for (sx = 0; sx < 4; sx++)
{
result = _getRawValue(tex, ix + sx - 1, iy + sy - 1);
r[sy * 4 + sx] = result.r;
g[sy * 4 + sx] = result.g;
b[sy * 4 + sx] = result.b;
}
}
result.r = toolsBicubicInterpolate(r, fx, fy);
result.g = toolsBicubicInterpolate(g, fx, fy);
result.b = toolsBicubicInterpolate(b, fx, fy);
result.a = 1.0;
return result;
}
Color textureApply(Texture* tex, Vector3 location, Vector3 normal)
{
Color col_x, col_y, col_z, result;
double x, y, z;
double distance;
distance = v3Norm(v3Sub(camera_location, location)) / 10.0;
x = location.x / (tex->scaling_x * distance);
y = location.y / (tex->scaling_y * distance);
z = location.z / (tex->scaling_z * distance);
col_x = _getInterpolatedValue(tex, y, z);
col_y = _getInterpolatedValue(tex, x, z);
col_z = _getInterpolatedValue(tex, x, y);
result.r = (col_x.r + col_y.r + col_z.r) / 3.0;
result.g = (col_x.g + col_y.g + col_z.g) / 3.0;
result.b = (col_x.b + col_y.b + col_z.b) / 3.0;
result.a = (col_x.a + col_y.a + col_z.a) / 3.0;
return result;
}

81
src/tools.c Normal file
View file

@ -0,0 +1,81 @@
#include <stdlib.h>
#include "shared/types.h"
#include "shared/functions.h"
double toolsRandom()
{
return (double)rand() / (double)RAND_MAX;
}
static inline double __cubicInterpolate(double p[4], double x)
{
return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
}
double toolsBicubicInterpolate(double stencil[16], double x, double y)
{
double buf_cubic_y[4];
buf_cubic_y[0] = __cubicInterpolate(stencil, x);
buf_cubic_y[1] = __cubicInterpolate(stencil + 4, x);
buf_cubic_y[2] = __cubicInterpolate(stencil + 8, x);
buf_cubic_y[3] = __cubicInterpolate(stencil + 12, x);
return __cubicInterpolate(buf_cubic_y, y);
}
void toolsFloat2DMapCopy(double* src, double* dest, int src_xstart, int src_ystart, int dest_xstart, int dest_ystart, int xsize, int ysize, int src_xstep, int src_ystep, int dest_xstep, int dest_ystep)
{
/* TODO Optimize with memcpy if src_xstep == dest_xstep == 1 */
int x, y;
double* src_row;
double* dest_row;
src += src_ystart * src_ystep + src_xstart * src_xstep;
dest += dest_ystart * dest_ystep + dest_xstart * dest_xstep;
for (y = 0; y < ysize; y++)
{
src_row = src;
dest_row = dest;
for (x = 0; x < xsize; x++)
{
*dest = *src;
src += src_xstep;
dest += dest_xstep;
}
src = src_row + src_ystep;
dest = dest_row + dest_ystep;
}
}
Vector3 toolsGetNormalFromTriangle(Vector3 center, Vector3 bottom, Vector3 right)
{
Vector3 dx = v3Sub(right, center);
Vector3 dz = v3Sub(bottom, center);
return v3Normalize(v3Cross(dz, dx));
}
void toolsSaveDouble(FILE* f, double value)
{
fprintf(f, "%.20le;", value);
}
double toolsLoadDouble(FILE* f)
{
double value;
fscanf(f, "%le;", &value);
return value;
}
void toolsSaveInt(FILE* f, int value)
{
fprintf(f, "%d;", value);
}
int toolsLoadInt(FILE* f)
{
int value;
fscanf(f, "%d;", &value);
return value;
}

334
src/water.c Normal file
View file

@ -0,0 +1,334 @@
#include "shared/types.h"
#include "shared/functions.h"
#include "shared/constants.h"
#include "shared/globals.h"
#include "water.h"
#include <math.h>
static WaterDefinition _definition;
static WaterQuality _quality;
static WaterEnvironment _environment;
static RayCastingResult _reflectionFunction(Vector3 start, Vector3 direction)
{
RayCastingResult result;
if (!terrainProjectRay(start, direction, &result.hit_location, &result.hit_color))
{
result.hit_color = skyProjectRay(start, direction);
/* TODO hit_location */
}
result.hit = 1;
return result;
}
static RayCastingResult _refractionFunction(Vector3 start, Vector3 direction)
{
RayCastingResult result;
result.hit = terrainProjectRay(start, direction, &result.hit_location, &result.hit_color);
return result;
}
void waterInit()
{
_definition = waterCreateDefinition();
/* TODO quality */
_environment.reflection_function = _reflectionFunction;
_environment.refraction_function = _refractionFunction;
_environment.toggle_fog = 1;
_environment.toggle_shadows = 1;
}
void waterSave(FILE* f)
{
toolsSaveDouble(f, _definition.height);
colorSave(_definition.main_color, f);
toolsSaveDouble(f, _definition.transparency);
toolsSaveDouble(f, _definition.reflection);
noiseSave(_definition.height_noise, f);
}
void waterLoad(FILE* f)
{
_definition.height = toolsLoadDouble(f);
_definition.main_color = colorLoad(f);
_definition.transparency = toolsLoadDouble(f);
_definition.reflection = toolsLoadDouble(f);
noiseLoad(_definition.height_noise, f);
}
WaterDefinition waterCreateDefinition()
{
WaterDefinition result;
result.height = -1000.0;
result.height_noise = noiseCreateGenerator();
return result;
}
void waterDeleteDefinition(WaterDefinition definition)
{
noiseDeleteGenerator(definition.height_noise);
}
void waterCopyDefinition(WaterDefinition source, WaterDefinition* destination)
{
NoiseGenerator* noise;
noise = destination->height_noise;
*destination = source;
destination->height_noise = noise;
noiseCopy(source.height_noise, destination->height_noise);
}
void waterSetDefinition(WaterDefinition config)
{
waterCopyDefinition(config, &_definition);
}
WaterDefinition waterGetDefinition()
{
return _definition;
}
void waterSetQuality(WaterQuality quality)
{
_quality = quality;
_quality.detail_boost = (_quality.detail_boost < 0.1) ? 0.1 : _quality.detail_boost;
}
WaterQuality waterGetQuality()
{
return _quality;
}
static inline double _getHeight(WaterDefinition* definition, double x, double z, double detail)
{
return definition->height + noiseGet2DDetail(definition->height_noise, x, z, detail);
}
static inline Vector3 _getNormal(WaterDefinition* definition, Vector3 base, double detail)
{
Vector3 back, right;
double x, z;
x = base.x;
z = base.z;
back.x = x;
back.y = _getHeight(definition, x, z + detail, detail);
back.z = z + detail;
back = v3Sub(back, base);
right.x = x + detail;
right.y = _getHeight(definition, x + detail, z, detail);
right.z = z;
right = v3Sub(right, base);
return v3Normalize(v3Cross(back, right));
}
static inline Vector3 _reflectRay(Vector3 incoming, Vector3 normal)
{
double c;
c = v3Dot(normal, v3Scale(incoming, -1.0));
return v3Add(incoming, v3Scale(normal, 2.0 * c));
}
static inline Vector3 _refractRay(Vector3 incoming, Vector3 normal)
{
double c1, c2, f;
f = 1.0 / 1.33;
c1 = v3Dot(normal, v3Scale(incoming, -1.0));
c2 = sqrt(1.0 - pow(f, 2.0) * (1.0 - pow(c1, 2.0)));
if (c1 >= 0.0)
{
return v3Add(v3Scale(incoming, f), v3Scale(normal, f * c1 - c2));
}
else
{
return v3Add(v3Scale(incoming, f), v3Scale(normal, c2 - f * c1));
}
}
WaterResult waterGetColorCustom(Vector3 location, Vector3 look, WaterDefinition* definition, WaterQuality* quality, WaterEnvironment* environment)
{
WaterResult result;
Vector3 normal;
Color color;
double shadowed, detail;
if (definition == NULL)
{
definition = &_definition;
}
if (quality == NULL)
{
quality = &_quality;
}
if (environment == NULL)
{
environment = &_environment;
}
if (quality->force_detail != 0.0)
{
detail = quality->force_detail;
}
else
{
detail = renderGetPrecision(location) / quality->detail_boost;
}
location.y = _getHeight(definition, location.x, location.z, detail);
result.location = location;
normal = _getNormal(definition, location, detail);
look = v3Normalize(look);
result.reflected = environment->reflection_function(location, _reflectRay(look, normal)).hit_color;
result.refracted = environment->refraction_function(location, _refractRay(look, normal)).hit_color;
color.r = definition->main_color.r * (1.0 - definition->transparency) + result.reflected.r * definition->reflection + result.refracted.r * definition->transparency;
color.g = definition->main_color.g * (1.0 - definition->transparency) + result.reflected.g * definition->reflection + result.refracted.g * definition->transparency;
color.b = definition->main_color.b * (1.0 - definition->transparency) + result.reflected.b * definition->reflection + result.refracted.b * definition->transparency;
color.a = 1.0;
if (environment->toggle_shadows)
{
shadowed = terrainGetShadow(location, sun_direction_inv);
}
else
{
shadowed = 0.0;
}
color = lightingApply(location, normal, shadowed, color, 0.8, 0.6);
if (environment->toggle_fog)
{
color = fogApplyToLocation(location, color);
}
result.base = definition->main_color;
result.final = color;
return result;
}
Color waterGetColor(Vector3 location, Vector3 look)
{
return waterGetColorCustom(location, look, &_definition, &_quality, &_environment).final;
}
static int _postProcessFragment(RenderFragment* fragment)
{
fragment->vertex.color = waterGetColor(fragment->vertex.location, v3Sub(fragment->vertex.location, camera_location));
return 1;
}
static Vertex _getFirstPassVertex(double x, double z, double precision)
{
Vertex result;
double value;
result.location.x = x;
result.location.y = _getHeight(&_definition, x, z, 0.0);
result.location.z = z;
value = sin(x) * sin(x) * cos(z) * cos(z);
result.color.r = 0.0;
result.color.g = value;
result.color.b = value;
result.color.a = 1.0;
result.normal.x = result.normal.y = result.normal.z = 0.0;
result.callback = _postProcessFragment;
return result;
}
static void _renderQuad(double x, double z, double size)
{
Vertex v1, v2, v3, v4;
v1 = _getFirstPassVertex(x, z, size);
v2 = _getFirstPassVertex(x, z + size, size);
v3 = _getFirstPassVertex(x + size, z + size, size);
v4 = _getFirstPassVertex(x + size, z, size);
renderPushQuad(&v1, &v2, &v3, &v4);
}
double waterGetLightFactor(Vector3 location)
{
double factor;
if (location.y < _definition.height)
{
if (sun_direction_inv.y > 0.00001)
{
factor = (_definition.height - location.y) / (sun_direction_inv.y * 3.0);
if (factor > 1.0)
{
factor = 1.0;
}
return 1.0 - 0.8 * factor;
}
else
{
return 0.0;
}
}
else
{
return 1.0;
}
}
void waterRender(RenderProgressCallback callback)
{
int chunk_factor, chunk_count, i;
double cx = camera_location.x;
double cz = camera_location.z;
double radius_int, radius_ext, base_chunk_size, chunk_size;
base_chunk_size = 2.0 / (double)render_quality;
chunk_factor = 1;
chunk_count = 2;
radius_int = 0.0;
radius_ext = base_chunk_size;
chunk_size = base_chunk_size;
while (radius_ext < 1000.0)
{
if (!callback(radius_ext / 1000.0))
{
return;
}
for (i = 0; i < chunk_count - 1; i++)
{
_renderQuad(cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
_renderQuad(cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
_renderQuad(cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
_renderQuad(cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
}
if (radius_int > 20.0 && chunk_count % 64 == 0 && (double)chunk_factor < radius_int / 20.0)
{
chunk_count /= 2;
chunk_factor *= 2;
}
chunk_count += 2;
chunk_size = base_chunk_size * chunk_factor;
radius_int = radius_ext;
radius_ext += chunk_size;
}
}

56
src/water.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef _PAYSAGES_WATER_H_
#define _PAYSAGES_WATER_H_
#include "shared/types.h"
#include <stdio.h>
typedef struct
{
double height;
double transparency;
double reflection;
Color main_color;
NoiseGenerator* height_noise;
} WaterDefinition;
typedef struct
{
double detail_boost;
double force_detail;
} WaterQuality;
typedef struct
{
RayCastingFunction reflection_function;
RayCastingFunction refraction_function;
int toggle_fog;
int toggle_shadows;
} WaterEnvironment;
typedef struct
{
Vector3 location;
Color base;
Color reflected;
Color refracted;
Color final;
} WaterResult;
void waterInit();
void waterSave(FILE* f);
void waterLoad(FILE* f);
WaterDefinition waterCreateDefinition();
void waterDeleteDefinition(WaterDefinition definition);
void waterCopyDefinition(WaterDefinition source, WaterDefinition* destination);
void waterSetDefinition(WaterDefinition definition);
WaterDefinition waterGetDefinition();
void waterSetQuality(WaterQuality quality);
WaterQuality waterGetQuality();
double waterGetLightFactor(Vector3 location);
WaterResult waterGetColorCustom(Vector3 location, Vector3 look, WaterDefinition* definition, WaterQuality* quality, WaterEnvironment* environment);
Color waterGetColor(Vector3 location, Vector3 look);
void waterRender(RenderProgressCallback callback);
#endif

227
src/zone.c Normal file
View file

@ -0,0 +1,227 @@
#include <stdlib.h>
#include <math.h>
#define MAX_RANGES 20
#define MAX_CIRCLES 20
typedef struct
{
double value;
double hardmin;
double softmin;
double softmax;
double hardmax;
} Range;
typedef struct
{
double value;
double centerx;
double centerz;
double softradius;
double hardradius;
} Circle;
struct Zone {
Range height_ranges[MAX_RANGES];
int height_ranges_count;
Range steepness_ranges[MAX_RANGES];
int steepness_ranges_count;
Circle circles_included[MAX_CIRCLES];
int circles_included_count;
Circle circles_excluded[MAX_CIRCLES];
int circles_excluded_count;
};
#include "shared/types.h"
#include "shared/functions.h"
void zoneSave(Zone* zone, FILE* f)
{
}
void zoneLoad(Zone* zone, FILE* f)
{
}
Zone* zoneCreate()
{
Zone* result;
result = (Zone*)malloc(sizeof(Zone));
result->height_ranges_count = 0;
result->steepness_ranges_count = 0;
result->circles_included_count = 0;
result->circles_excluded_count = 0;
return result;
}
void zoneDelete(Zone* zone)
{
free(zone);
}
void zoneIncludeCircleArea(Zone* zone, double value, double centerx, double centerz, double softradius, double hardradius)
{
Circle circle = {value, centerx, centerz, softradius, hardradius};
if (zone->circles_included_count < MAX_CIRCLES)
{
zone->circles_included[zone->circles_included_count++] = circle;
}
}
void zoneExcludeCircleArea(Zone* zone, double centerx, double centerz, double softradius, double hardradius)
{
/* TODO */
}
void zoneAddHeightRange(Zone* zone, double value, double hardmin, double softmin, double softmax, double hardmax)
{
Range range = {value, hardmin, softmin, softmax, hardmax};
if (zone->height_ranges_count < MAX_RANGES)
{
zone->height_ranges[zone->height_ranges_count++] = range;
}
}
void zoneAddSteepnessRange(Zone* zone, double value, double hardmin, double softmin, double softmax, double hardmax)
{
Range range = {value, hardmin, softmin, softmax, hardmax};
if (zone->steepness_ranges_count < MAX_RANGES)
{
zone->steepness_ranges[zone->steepness_ranges_count++] = range;
}
}
static inline double _getRangeInfluence(Range range, double position)
{
if (position >= range.hardmin && position <= range.hardmax)
{
if (position < range.softmin)
{
return range.value * (position - range.hardmin) / (range.softmin - range.hardmin);
}
else if (position > range.softmax)
{
return range.value * (range.hardmax - position) / (range.hardmax - range.softmax);
}
else
{
return range.value;
}
}
else
{
return 0.0;
}
}
static inline double _getCircleInfluence(Circle circle, Vector3 position)
{
double radius, dx, dz;
dx = position.x - circle.centerx;
dz = position.z - circle.centerz;
radius = sqrt(dx * dx + dz * dz);
if (radius > circle.hardradius)
{
return 0.0;
}
else if (radius < circle.softradius)
{
return circle.value;
}
else
{
return circle.value * (circle.hardradius - radius) / (circle.hardradius - circle.softradius);
}
}
double zoneGetValue(Zone* zone, Vector3 location, Vector3 normal)
{
int i;
double value, value_height, value_steepness, value_circle;
if (zone->circles_included_count > 0)
{
value_circle = 0.0;
for (i = 0; i < zone->circles_included_count; i++)
{
value = _getCircleInfluence(zone->circles_included[i], location);
if (value > value_circle)
{
value_circle = value;
}
}
}
else
{
value_circle = 1.0;
}
if (zone->height_ranges_count > 0)
{
value_height = 0.0;
for (i = 0; i < zone->height_ranges_count; i++)
{
value = _getRangeInfluence(zone->height_ranges[i], location.y);
if (value > value_height)
{
value_height = value;
}
}
}
else
{
value_height = 1.0;
}
if (zone->steepness_ranges_count > 0)
{
value_steepness = 0.0;
for (i = 0; i < zone->steepness_ranges_count; i++)
{
value = _getRangeInfluence(zone->steepness_ranges[i], (1.0 - normal.y));
if (value > value_steepness)
{
value_steepness = value;
}
}
}
else
{
value_steepness = 1.0;
}
if (value_steepness < value_height)
{
if (value_circle < value_steepness)
{
return value_circle;
}
else
{
return value_steepness;
}
}
else
{
if (value_circle < value_height)
{
return value_circle;
}
else
{
return value_height;
}
}
}