From 34fa6f1babd6aa4099e167ab607a9d7b707887ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Thu, 7 Feb 2013 16:06:26 +0000 Subject: [PATCH] paysages : WIP on terrain painting. git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@518 b1fd45b6-86a6-48da-8261-f70d1f35bdcc --- gui_qt/widgetheightmap.cpp | 70 ++++---- gui_qt/widgetheightmap.h | 1 + lib_paysages/terrain/main.c | 26 +-- lib_paysages/terrain/painting.c | 285 ++++++++++++++++---------------- lib_paysages/terrain/private.h | 19 ++- lib_paysages/terrain/public.h | 2 + 6 files changed, 204 insertions(+), 199 deletions(-) diff --git a/gui_qt/widgetheightmap.cpp b/gui_qt/widgetheightmap.cpp index b50a53b..9cb40e8 100644 --- a/gui_qt/widgetheightmap.cpp +++ b/gui_qt/widgetheightmap.cpp @@ -237,6 +237,7 @@ void WidgetHeightMap::initializeGL() glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_EMISSION); //glFrontFace(GL_CCW); //glCullFace(GL_BACK); @@ -328,46 +329,37 @@ void WidgetHeightMap::paintGL() glBegin(GL_QUAD_STRIP); for (int z = 0; z < rz; z++) { - _VertexInfo* vertex = _vertices + z * rx + x; - double diff_x, diff_z, diff; + for (int dx = 1; dx >= 0; dx--) + { + _VertexInfo* vertex = _vertices + z * rx + x + dx; + double diff_x, diff_z, diff; - diff_x = (vertex + 1)->point.x - _brush_x; - diff_z = (vertex + 1)->point.z - _brush_z; - diff = sqrt(diff_x * diff_x + diff_z * diff_z); - if (diff > _brush_size) - { - diff = 0.0; + diff_x = vertex->point.x - _brush_x; + diff_z = vertex->point.z - _brush_z; + diff = sqrt(diff_x * diff_x + diff_z * diff_z); + if (diff > _brush_size) + { + diff = 0.0; + } + else if (diff > _brush_size * (1.0 - _brush_smoothing)) + { + diff = 1.0 - (diff - _brush_size * (1.0 - _brush_smoothing)) / (_brush_size * _brush_smoothing); + } + else + { + diff = 1.0; + } + if (vertex->painted) + { + glColor3d(0.2 + diff, 0.0, 0.0); + } + else + { + glColor3d(diff, 0.0, 0.0); + } + glNormal3d(vertex->normal.x, vertex->normal.y, vertex->normal.z); + glVertex3d(vertex->point.x, vertex->point.y, vertex->point.z); } - else if (diff > _brush_size * (1.0 - _brush_smoothing)) - { - diff = 1.0 - (diff - _brush_size * (1.0 - _brush_smoothing)) / (_brush_size * _brush_smoothing); - } - else - { - diff = 1.0; - } - glColor3d(1.0, 1.0 - diff, 1.0 - diff); - glNormal3d((vertex + 1)->normal.x, (vertex + 1)->normal.y, (vertex + 1)->normal.z); - glVertex3d((vertex + 1)->point.x, (vertex + 1)->point.y, (vertex + 1)->point.z); - - diff_x = vertex->point.x - _brush_x; - diff_z = vertex->point.z - _brush_z; - diff = sqrt(diff_x * diff_x + diff_z * diff_z); - if (diff > _brush_size) - { - diff = 0.0; - } - else if (diff > _brush_size * (1.0 - _brush_smoothing)) - { - diff = 1.0 - (diff - _brush_size * (1.0 - _brush_smoothing)) / (_brush_size * _brush_smoothing); - } - else - { - diff = 1.0; - } - glColor3d(1.0, 1.0 - diff, 1.0 - diff); - glNormal3d(vertex->normal.x, vertex->normal.y, vertex->normal.z); - glVertex3d(vertex->point.x, vertex->point.y, vertex->point.z); } glEnd(); } @@ -409,6 +401,8 @@ void WidgetHeightMap::updateVertexInfo() vertex->point.z = (double)dz; vertex->point.y = terrainGetGridHeight(_terrain, dx, dz, 1); + + vertex->painted = terrainIsPainted(_terrain->height_map, x, z); } } diff --git a/gui_qt/widgetheightmap.h b/gui_qt/widgetheightmap.h index 17a50ed..d322b19 100644 --- a/gui_qt/widgetheightmap.h +++ b/gui_qt/widgetheightmap.h @@ -11,6 +11,7 @@ typedef struct { Vector3 point; Vector3 normal; + int painted; } _VertexInfo; typedef enum diff --git a/lib_paysages/terrain/main.c b/lib_paysages/terrain/main.c index a859bce..c4d171d 100644 --- a/lib_paysages/terrain/main.c +++ b/lib_paysages/terrain/main.c @@ -95,17 +95,7 @@ static double _fakeGetHeight(Renderer* renderer, double x, double z, int with_pa static double _getHeight(Renderer* renderer, double x, double z, int with_painting) { - double height; - TerrainDefinition* definition = renderer->terrain->definition; - x /= definition->scaling; - z /= definition->scaling; - - if (!with_painting || !terrainHeightmapGetHeight(definition->height_map, x, z, &height)) - { - height = noiseGet2DTotal(definition->_height_noise, x, z); - } - - return height * definition->height * definition->scaling; + return terrainGetInterpolatedHeight(renderer->terrain->definition, x, z, with_painting); } static Color _fakeGetFinalColor(Renderer* renderer, Vector3 location, double precision) @@ -286,6 +276,20 @@ double terrainGetGridHeight(TerrainDefinition* definition, int x, int z, int wit return height; } +double terrainGetInterpolatedHeight(TerrainDefinition* definition, double x, double z, int with_painting) +{ + double height; + x /= definition->scaling; + z /= definition->scaling; + + if (!with_painting || !terrainHeightmapGetHeight(definition->height_map, x, z, &height)) + { + height = noiseGet2DTotal(definition->_height_noise, x, z); + } + + return height * definition->height * definition->scaling; +} + /******************** Renderer ********************/ static TerrainRenderer* _createRenderer() { diff --git a/lib_paysages/terrain/painting.c b/lib_paysages/terrain/painting.c index bc2a444..d9f6eda 100644 --- a/lib_paysages/terrain/painting.c +++ b/lib_paysages/terrain/painting.c @@ -216,21 +216,38 @@ static inline void _resetRect(TerrainHeightMap* heightmap, int x1, int x2, int z } } +static inline IntegerRect _getBrushRect(TerrainBrush* brush) +{ + IntegerRect result; + double s = brush->smoothed_size + brush->hard_radius; + + result.xstart = (int)floor(brush->relative_x - s); + result.xend = (int)ceil(brush->relative_x + s); + result.zstart = (int)floor(brush->relative_z - s); + result.zend = (int)ceil(brush->relative_z + s); + + result.xsize = result.xend - result.xstart + 1; + result.zsize = result.zend - result.zstart + 1; + + return result; +} + +static inline int _isInRect(IntegerRect rect, int x, int z) +{ + return (x >= rect.xstart && x <= rect.xend && z >= rect.zstart && z <= rect.zend); +} + static void _prepareBrushStroke(TerrainHeightMap* heightmap, TerrainBrush* brush) { - double s = brush->smoothed_size + brush->hard_radius; - int x1 = (int)floor(brush->relative_x - s); - int x2 = (int)ceil(brush->relative_x + s); - int z1 = (int)floor(brush->relative_z - s); - int z2 = (int)ceil(brush->relative_z + s); + IntegerRect brush_rect = _getBrushRect(brush); /* Prepare floating data */ if (heightmap->floating_used) { - int gx1 = (x1 < heightmap->floating_data.rect.xstart) ? heightmap->floating_data.rect.xstart - x1 : 0; - int gx2 = (x2 > heightmap->floating_data.rect.xend) ? x2 - heightmap->floating_data.rect.xend : 0; - int gz1 = (z1 < heightmap->floating_data.rect.zstart) ? heightmap->floating_data.rect.zstart - z1 : 0; - int gz2 = (z2 > heightmap->floating_data.rect.zend) ? z2 - heightmap->floating_data.rect.zend : 0; + int gx1 = (brush_rect.xstart < heightmap->floating_data.rect.xstart) ? heightmap->floating_data.rect.xstart - brush_rect.xstart : 0; + int gx2 = (brush_rect.xend > heightmap->floating_data.rect.xend) ? brush_rect.xend - heightmap->floating_data.rect.xend : 0; + int gz1 = (brush_rect.xstart < heightmap->floating_data.rect.zstart) ? heightmap->floating_data.rect.zstart - brush_rect.zstart : 0; + int gz2 = (brush_rect.zend > heightmap->floating_data.rect.zend) ? brush_rect.zend - heightmap->floating_data.rect.zend : 0; if (gx1 || gx2 || gz1 || gz2) { /* Floating area needs growing */ @@ -255,18 +272,13 @@ static void _prepareBrushStroke(TerrainHeightMap* heightmap, TerrainBrush* brush else { /* Init floating area */ - heightmap->floating_data.rect.xstart = x1; - heightmap->floating_data.rect.xend = x2; - heightmap->floating_data.rect.xsize = x2 - x1 + 1; - heightmap->floating_data.rect.zstart = z1; - heightmap->floating_data.rect.zend = z2; - heightmap->floating_data.rect.zsize = z2 - z1 + 1; + heightmap->floating_data.rect = brush_rect; size_t new_size; - new_size = sizeof(double) * heightmap->floating_data.rect.xsize * heightmap->floating_data.rect.zsize; + new_size = sizeof(double) * brush_rect.xsize * brush_rect.zsize; heightmap->floating_data.data = realloc(heightmap->floating_data.data, new_size); - _resetRect(heightmap, 0, heightmap->floating_data.rect.xsize - 1, 0, heightmap->floating_data.rect.zsize - 1); + _resetRect(heightmap, 0, brush_rect.xsize - 1, 0, brush_rect.zsize - 1); heightmap->floating_used = 1; } @@ -290,23 +302,136 @@ size_t terrainGetMemoryStats(TerrainDefinition* definition) return result; } +int terrainIsPainted(TerrainHeightMap* heightmap, int x, int z) +{ + int i; + + for (i = 0; i < heightmap->fixed_count; i++) + { + if (_isInRect(heightmap->fixed_data[i].rect, x, z)) + { + return 1; + } + } + + if (heightmap->floating_used && _isInRect(heightmap->floating_data.rect, x, z)) + { + return 1; + } + else + { + return 0; + } +} + +typedef double (*BrushCallback)(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data); + +static inline void _applyBrush(TerrainHeightMap* heightmap, TerrainBrush* brush, double force, void* data, BrushCallback callback) +{ + IntegerRect brush_rect = _getBrushRect(brush); + int x, z; + double dx, dz, distance, influence; + + if (!heightmap->floating_used) + { + return; + } + + for (x = brush_rect.xstart; x <= brush_rect.xend; x++) + { + dx = (double)x; + for (z = brush_rect.zstart; z <= brush_rect.zend; z++) + { + if (_isInRect(heightmap->floating_data.rect, x, z)) /* TODO Rect intersection */ + { + dz = (double)z; + distance = sqrt((brush->relative_x - dx) * (brush->relative_x - dx) + (brush->relative_z - dz) * (brush->relative_z - dz)); + + if (distance > brush->hard_radius) + { + if (distance <= brush->hard_radius + brush->smoothed_size) + { + influence = (1.0 - (distance - brush->hard_radius) / brush->smoothed_size); + } + else + { + continue; + } + } + else + { + influence = 1.0; + } + + int ix = x - heightmap->floating_data.rect.xstart; + int iz = z - heightmap->floating_data.rect.zstart; + double base_value = heightmap->floating_data.data[iz * heightmap->floating_data.rect.xsize + ix]; + heightmap->floating_data.data[iz * heightmap->floating_data.rect.xsize + ix] = callback(heightmap, brush, dx, dz, base_value, influence, force, data); + } + } + } +} + +static double _applyBrushElevation(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) +{ + UNUSED(heightmap); + UNUSED(data); + return basevalue + influence * force; +} + void terrainBrushElevation(TerrainHeightMap* heightmap, TerrainBrush* brush, double value) { _prepareBrushStroke(heightmap, brush); + _applyBrush(heightmap, brush, value, NULL, _applyBrushElevation); +} + +static double _applyBrushSmooth(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) +{ + double ideal, factor; + ideal = terrainGetInterpolatedHeight(heightmap->terrain, x + brush->total_radius * 0.5, z, 1); + ideal += terrainGetInterpolatedHeight(heightmap->terrain, x - brush->total_radius * 0.5, z, 1); + ideal += terrainGetInterpolatedHeight(heightmap->terrain, x, z - brush->total_radius * 0.5, 1); + ideal += terrainGetInterpolatedHeight(heightmap->terrain, x, z + brush->total_radius * 0.5, 1); + ideal /= 4.0; + factor = influence * force; + if (factor > 1.0) + { + factor = 0.0; + } + return basevalue + (ideal - basevalue) * factor; } void terrainBrushSmooth(TerrainHeightMap* heightmap, TerrainBrush* brush, double value) { _prepareBrushStroke(heightmap, brush); + _applyBrush(heightmap, brush, value, NULL, _applyBrushSmooth); +} + +static double _applyBrushAddNoise(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) +{ + UNUSED(heightmap); + return basevalue + noiseGet2DTotal((NoiseGenerator*)data, x / brush->total_radius, z / brush->total_radius) * influence * force * brush->total_radius; } void terrainBrushAddNoise(TerrainHeightMap* heightmap, TerrainBrush* brush, NoiseGenerator* generator, double value) { _prepareBrushStroke(heightmap, brush); + _applyBrush(heightmap, brush, value, generator, _applyBrushAddNoise); +} + +static double _applyBrushReset(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) +{ + UNUSED(brush); + UNUSED(data); + + double ideal = terrainGetInterpolatedHeight(heightmap->terrain, x, z, 1); + return basevalue + (ideal - basevalue) * influence * force; } void terrainBrushReset(TerrainHeightMap* heightmap, TerrainBrush* brush, double value) { + /* No need to prepare the floating data, it can't grow here */ + _applyBrush(heightmap, brush, value, NULL, _applyBrushReset); } void terrainEndBrushStroke(TerrainHeightMap* heightmap) @@ -323,6 +448,7 @@ void terrainEndBrushStroke(TerrainHeightMap* heightmap) memcpy(heightmap->fixed_data[heightmap->fixed_count].data, heightmap->floating_data.data, mapsize); heightmap->fixed_count++; + heightmap->floating_used = 0; heightmap->floating_data.data = realloc(heightmap->floating_data.data, sizeof(double)); } @@ -449,129 +575,4 @@ void heightmapRevertToTerrain(HeightMap* heightmap, TerrainDefinition* terrain, } } } - - -static inline void _getBrushBoundaries(TerrainBrush* brush, int rx, int rz, int* x1, int* x2, int* z1, int* z2) -{ - double cx = brush->relative_x * rx; - double cz = brush->relative_z * rz; - double s = brush->smoothed_size + brush->hard_radius; - double sx = s * rx; - double sz = s * rz; - *x1 = (int)floor(cx - sx); - *x2 = (int)ceil(cx + sx); - *z1 = (int)floor(cz - sz); - *z2 = (int)ceil(cz + sz); - if (*x1 < 0) - { - *x1 = 0; - } - else if (*x1 > rx) - { - *x1 = rx; - } - if (*x2 < 0) - { - *x2 = 0; - } - else if (*x2 > rx) - { - *x2 = rx; - } - if (*z1 < 0) - { - *z1 = 0; - } - else if (*z1 > rz) - { - *z1 = rz; - } - if (*z2 < 0) - { - *z2 = 0; - } - else if (*z2 > rz) - { - *z2 = rz; - } -} - -typedef double (*BrushCallback)(HeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data); - -static inline void _applyBrush(HeightMap* heightmap, TerrainBrush* brush, double force, void* data, BrushCallback callback) -{ - int x, x1, x2, z, z1, z2; - double dx, dz, distance, influence; - - _getBrushBoundaries(brush, heightmap->resolution_x - 1, heightmap->resolution_z - 1, &x1, &x2, &z1, &z2); - - for (x = x1; x <= x2; x++) - { - dx = (double)x / (double)heightmap->resolution_x; - for (z = z1; z <= z2; z++) - { - dz = (double)z / (double)heightmap->resolution_z; - distance = sqrt((brush->relative_x - dx) * (brush->relative_x - dx) + (brush->relative_z - dz) * (brush->relative_z - dz)); - - if (distance > brush->hard_radius) - { - if (distance <= brush->hard_radius + brush->smoothed_size) - { - influence = (1.0 - (distance - brush->hard_radius) / brush->smoothed_size); - } - else - { - continue; - } - } - else - { - influence = 1.0; - } - - heightmap->data[z * heightmap->resolution_x + x] = callback(heightmap, brush, dx, dz, heightmap->data[z * heightmap->resolution_x + x], influence, force, data); - } - } -} - -static double _applyBrushElevation(HeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) -{ - return basevalue + influence * force * brush->total_radius; -} - -void terrainBrushElevation(HeightMap* heightmap, TerrainBrush* brush, double value) -{ - _applyBrush(heightmap, brush, value, NULL, _applyBrushElevation); -} - -static double _applyBrushSmooth(HeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) -{ - double ideal, factor; - ideal = heightmapGetValue(heightmap, x + brush->total_radius * 0.5, z); - ideal += heightmapGetValue(heightmap, x - brush->total_radius * 0.5, z); - ideal += heightmapGetValue(heightmap, x, z - brush->total_radius * 0.5); - ideal += heightmapGetValue(heightmap, x, z + brush->total_radius * 0.5); - ideal /= 4.0; - factor = influence * force; - if (factor > 1.0) - { - factor = 0.0; - } - return basevalue + (ideal - basevalue) * factor; -} - -void terrainBrushSmooth(HeightMap* heightmap, TerrainBrush* brush, double value) -{ - _applyBrush(heightmap, brush, value, NULL, _applyBrushSmooth); -} - -static double _applyBrushAddNoise(HeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data) -{ - return basevalue + noiseGet2DTotal((NoiseGenerator*)data, x / brush->total_radius, z / brush->total_radius) * influence * force * brush->total_radius; -} - -void terrainBrushAddNoise(HeightMap* heightmap, TerrainBrush* brush, NoiseGenerator* generator, double value) -{ - _applyBrush(heightmap, brush, value, generator, _applyBrushAddNoise); -} #endif diff --git a/lib_paysages/terrain/private.h b/lib_paysages/terrain/private.h index 4681328..b118500 100644 --- a/lib_paysages/terrain/private.h +++ b/lib_paysages/terrain/private.h @@ -5,14 +5,17 @@ typedef struct { - struct { - int xstart; - int xend; - int xsize; - int zstart; - int zend; - int zsize; - } rect; + int xstart; + int xend; + int xsize; + int zstart; + int zend; + int zsize; +} IntegerRect; + +typedef struct +{ + IntegerRect rect; double* data; } TerrainHeightMapChunk; diff --git a/lib_paysages/terrain/public.h b/lib_paysages/terrain/public.h index 6ca3158..1696dc1 100644 --- a/lib_paysages/terrain/public.h +++ b/lib_paysages/terrain/public.h @@ -52,6 +52,7 @@ extern StandardRenderer TerrainRendererClass; void terrainAutoPreset(TerrainDefinition* definition, TerrainPreset preset); void terrainRenderSurface(Renderer* renderer); double terrainGetGridHeight(TerrainDefinition* definition, int x, int z, int with_painting); +double terrainGetInterpolatedHeight(TerrainDefinition* definition, double x, double z, int with_painting); size_t terrainGetMemoryStats(TerrainDefinition* definition); Renderer* terrainCreatePreviewRenderer(); @@ -67,6 +68,7 @@ typedef struct } TerrainBrush; /* Heightmap manipulation */ +int terrainIsPainted(TerrainHeightMap* heightmap, int x, int z); void terrainBrushElevation(TerrainHeightMap* heightmap, TerrainBrush* brush, double value); void terrainBrushSmooth(TerrainHeightMap* heightmap, TerrainBrush* brush, double value); void terrainBrushAddNoise(TerrainHeightMap* heightmap, TerrainBrush* brush, NoiseGenerator* generator, double value);