reorganize
git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@191 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
26
CMakeLists.txt
Normal 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
|
@ -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
BIN
data/textures/grass1.jpg
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
data/textures/rock1.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
data/textures/rock2.jpg
Normal file
After Width: | Height: | Size: 716 KiB |
BIN
data/textures/rock3.jpg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
data/textures/rock4.jpg
Normal file
After Width: | Height: | Size: 1.9 MiB |
BIN
data/textures/rock5.jpg
Normal file
After Width: | Height: | Size: 728 KiB |
BIN
data/textures/rock6.jpg
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
data/textures/rock7.jpg
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
data/textures/snow1.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
data/textures/white.jpg
Normal file
After Width: | Height: | Size: 306 B |
107
src/array.c
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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], ¶ms))
|
||||
{
|
||||
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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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, ¤t.vertex);
|
||||
|
||||
#ifdef RENDER_WIREFRAME
|
||||
if (cury == starty || cury == endy)
|
||||
{
|
||||
current.vertex.color = COLOR_RED;
|
||||
}
|
||||
#endif
|
||||
|
||||
renderAddFragment(¤t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|