paysages : Terrain painting (WIP).
git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@493 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
This commit is contained in:
parent
b816ab1105
commit
5cad0fb39a
15 changed files with 134 additions and 58 deletions
|
@ -49,7 +49,7 @@ bool ExplorerChunkTerrain::onMaintainEvent()
|
|||
{
|
||||
if (_tessellation_current_size == 0 || i % old_tessellation_inc != 0 || j % old_tessellation_inc != 0)
|
||||
{
|
||||
double height = renderer->terrain->getHeight(renderer, _startx + _tessellation_step * (double)i, _startz + _tessellation_step * (double)j);
|
||||
double height = renderer->terrain->getHeight(renderer, _startx + _tessellation_step * (double)i, _startz + _tessellation_step * (double)j, 1);
|
||||
_tessellation[j * (_tessellation_max_size + 1) + i] = height;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ protected:
|
|||
{
|
||||
Vector3 down = {0.0, -1.0, 0.0};
|
||||
Vector3 location;
|
||||
double height = _renderer.terrain->getHeight(&_renderer, x, y);
|
||||
double height = _renderer.terrain->getHeight(&_renderer, x, y, 1);
|
||||
|
||||
if (height < _water.height)
|
||||
{
|
||||
|
|
|
@ -49,8 +49,8 @@ FormTerrain::FormTerrain(QWidget *parent):
|
|||
addPreview(previewColor, tr("Lighted preview (no texture)"));
|
||||
|
||||
//addInputNoise(tr("Noise"), _definition.height_noise);
|
||||
addInputDouble(tr("Height"), &_definition->height, 0.0, 20.0, 0.1, 1.0);
|
||||
addInputDouble(tr("Scaling"), &_definition->scaling, 20.0, 200.0, 1.0, 10.0);
|
||||
addInputDouble(tr("Scaling"), &_definition->scaling, 0.1, 3.0, 0.03, 0.3);
|
||||
addInputDouble(tr("Height modifier"), &_definition->height, 0.0, 3.0, 0.01, 0.1);
|
||||
addInputDouble(tr("Shadow smoothing"), &_definition->shadow_smoothing, 0.0, 0.3, 0.003, 0.03);
|
||||
|
||||
revertConfig();
|
||||
|
|
|
@ -40,7 +40,7 @@ protected:
|
|||
Vector3 location;
|
||||
double coverage;
|
||||
location.x = x;
|
||||
location.y = _renderer.terrain->getHeight(&_renderer, x, y);
|
||||
location.y = _renderer.terrain->getHeight(&_renderer, x, y, 1);
|
||||
location.z = y;
|
||||
coverage = texturesGetLayerCoverage(_preview_layer, &_renderer, location, this->scaling);
|
||||
return QColor::fromRgbF(coverage, coverage, coverage, 1.0);
|
||||
|
|
|
@ -35,7 +35,7 @@ protected:
|
|||
{
|
||||
double height;
|
||||
|
||||
height = _renderer.terrain->getHeight(&_renderer, x, -y);
|
||||
height = _renderer.terrain->getHeight(&_renderer, x, -y, 1);
|
||||
if (height > _definition.height)
|
||||
{
|
||||
return colorToQColor(terrainGetPreviewColor(&_renderer, x, -y, scaling));
|
||||
|
|
|
@ -151,10 +151,10 @@ void WidgetHeightMap::timerEvent(QTimerEvent*)
|
|||
double brush_strength;
|
||||
TerrainBrush brush;
|
||||
|
||||
brush.relative_x = (_brush_x + 40.0) / 80.0;
|
||||
brush.relative_z = (_brush_z + 40.0) / 80.0;
|
||||
brush.hard_radius = _brush_size * (1.0 - _brush_smoothing) / 80.0;
|
||||
brush.smoothed_size = _brush_size * _brush_smoothing / 80.0;
|
||||
brush.relative_x = _brush_x;
|
||||
brush.relative_z = _brush_z;
|
||||
brush.hard_radius = _brush_size * (1.0 - _brush_smoothing);
|
||||
brush.smoothed_size = _brush_size * _brush_smoothing;
|
||||
brush.total_radius = brush.hard_radius + brush.smoothed_size;
|
||||
|
||||
brush_strength = _brush_strength * duration / 0.1;
|
||||
|
@ -363,10 +363,10 @@ void WidgetHeightMap::updateVertexInfo()
|
|||
{
|
||||
_VertexInfo* vertex = _vertices + z * rx + x;
|
||||
|
||||
vertex->point.x = 80.0 * (double)x / (double)(rx - 1) - 40.0;
|
||||
vertex->point.z = 80.0 * (double)z / (double)(rz - 1) - 40.0;
|
||||
vertex->point.x = (double)x;
|
||||
vertex->point.z = (double)z;
|
||||
|
||||
vertex->point.y = _renderer.terrain->getHeight(&_renderer, vertex->point.x, vertex->point.z);
|
||||
vertex->point.y = terrainGetGridHeight(_terrain, x, z, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ void cameraValidateDefinition(CameraDefinition* definition, int check_above)
|
|||
waterDeleteDefinition(&water);
|
||||
|
||||
renderer = sceneryCreateStandardRenderer();
|
||||
terrain_height = renderer.terrain->getHeight(&renderer, definition->location.x, definition->location.z) + 0.5;
|
||||
terrain_height = renderer.terrain->getHeight(&renderer, definition->location.x, definition->location.z, 1) + 0.5;
|
||||
rendererDelete(&renderer);
|
||||
|
||||
if (definition->location.y < water_height || definition->location.y < terrain_height)
|
||||
|
|
|
@ -84,25 +84,29 @@ StandardDefinition TerrainDefinitionClass = {
|
|||
};
|
||||
|
||||
/******************** Binding ********************/
|
||||
static double _fakeGetHeight(Renderer* renderer, double x, double z)
|
||||
static double _fakeGetHeight(Renderer* renderer, double x, double z, int with_painting)
|
||||
{
|
||||
UNUSED(renderer);
|
||||
UNUSED(x);
|
||||
UNUSED(z);
|
||||
UNUSED(with_painting);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static double _getHeight(Renderer* renderer, double x, double z)
|
||||
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;
|
||||
|
||||
double height = noiseGet2DTotal(definition->_height_noise, x, z);
|
||||
/* TODO Apply paintings */
|
||||
if (!with_painting || !terrainHeightmapGetHeight(definition->height_map, x, z, &height))
|
||||
{
|
||||
height = noiseGet2DTotal(definition->_height_noise, x, z);
|
||||
}
|
||||
|
||||
return height * definition->height;
|
||||
return height * definition->height * definition->scaling;
|
||||
}
|
||||
|
||||
static Color _fakeGetFinalColor(Renderer* renderer, Vector3 location, double precision)
|
||||
|
@ -123,7 +127,7 @@ static Color _getFinalColor(Renderer* renderer, Vector3 location, double precisi
|
|||
return color;
|
||||
}
|
||||
|
||||
RayCastingResult _fakeCastRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
||||
static RayCastingResult _fakeCastRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
||||
{
|
||||
UNUSED(renderer);
|
||||
UNUSED(start);
|
||||
|
@ -134,7 +138,7 @@ RayCastingResult _fakeCastRay(Renderer* renderer, Vector3 start, Vector3 directi
|
|||
return result;
|
||||
}
|
||||
|
||||
RayCastingResult _castRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
||||
static RayCastingResult _castRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
||||
{
|
||||
RayCastingResult result;
|
||||
TerrainDefinition* definition = renderer->terrain->definition;
|
||||
|
@ -145,7 +149,7 @@ RayCastingResult _castRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
|||
inc_factor = (double)renderer->render_quality;
|
||||
inc_base = 1.0;
|
||||
inc_value = inc_base / inc_factor;
|
||||
lastdiff = start.y - _getHeight(renderer, start.x, start.z);
|
||||
lastdiff = start.y - _getHeight(renderer, start.x, start.z, 1);
|
||||
|
||||
length = 0.0;
|
||||
do
|
||||
|
@ -153,14 +157,14 @@ RayCastingResult _castRay(Renderer* renderer, Vector3 start, Vector3 direction)
|
|||
inc_vector = v3Scale(direction, inc_value);
|
||||
length += v3Norm(inc_vector);
|
||||
start = v3Add(start, inc_vector);
|
||||
height = _getHeight(renderer, start.x, start.z);
|
||||
height = _getHeight(renderer, start.x, start.z, 1);
|
||||
diff = start.y - height;
|
||||
if (diff < 0.0)
|
||||
{
|
||||
if (fabs(diff - lastdiff) > 0.00001)
|
||||
{
|
||||
start = v3Add(start, v3Scale(inc_vector, -diff / (diff - lastdiff)));
|
||||
start.y = _getHeight(renderer, start.x, start.z);
|
||||
start.y = _getHeight(renderer, start.x, start.z, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -236,7 +240,7 @@ static LightDefinition _alterLight(Renderer* renderer, LightDefinition* light, V
|
|||
inc_vector = v3Scale(direction_to_light, inc_value);
|
||||
length += v3Norm(inc_vector);
|
||||
location = v3Add(location, inc_vector);
|
||||
height = _getHeight(renderer, location.x, location.z);
|
||||
height = _getHeight(renderer, location.x, location.z, 1);
|
||||
diff = location.y - height;
|
||||
if (diff < 0.0)
|
||||
{
|
||||
|
@ -279,6 +283,19 @@ static LightDefinition _alterLight(Renderer* renderer, LightDefinition* light, V
|
|||
}
|
||||
}
|
||||
|
||||
/******************** Public tools ********************/
|
||||
double terrainGetGridHeight(TerrainDefinition* definition, int x, int z, int with_painting)
|
||||
{
|
||||
double height;
|
||||
|
||||
if (!with_painting || !terrainHeightmapGetHeight(definition->height_map, (double)x, (double)z, &height))
|
||||
{
|
||||
height = noiseGet2DTotal(definition->_height_noise, (double)x, (double)z);
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
/******************** Renderer ********************/
|
||||
static TerrainRenderer* _createRenderer()
|
||||
{
|
||||
|
|
|
@ -71,13 +71,13 @@ void terrainHeightmapCopy(TerrainHeightMap* source, TerrainHeightMap* destinatio
|
|||
{
|
||||
destination->fixed_data[i].xstart = source->fixed_data[i].xstart;
|
||||
destination->fixed_data[i].xsize = source->fixed_data[i].xsize;
|
||||
destination->fixed_data[i].ystart = source->fixed_data[i].ystart;
|
||||
destination->fixed_data[i].ysize = source->fixed_data[i].ysize;
|
||||
if (destination->fixed_data[i].xsize * destination->fixed_data[i].ysize > 0)
|
||||
destination->fixed_data[i].zstart = source->fixed_data[i].zstart;
|
||||
destination->fixed_data[i].zsize = source->fixed_data[i].zsize;
|
||||
if (destination->fixed_data[i].xsize * destination->fixed_data[i].zsize > 0)
|
||||
{
|
||||
destination->fixed_data[i].data = realloc(destination->fixed_data[i].data, sizeof(double) * destination->fixed_data[i].xsize * destination->fixed_data[i].ysize);
|
||||
destination->fixed_data[i].data = realloc(destination->fixed_data[i].data, sizeof(double) * destination->fixed_data[i].xsize * destination->fixed_data[i].zsize);
|
||||
}
|
||||
memcpy(destination->fixed_data[i].data, source->fixed_data[i].data, sizeof(double) * destination->fixed_data[i].xsize * destination->fixed_data[i].ysize);
|
||||
memcpy(destination->fixed_data[i].data, source->fixed_data[i].data, sizeof(double) * destination->fixed_data[i].xsize * destination->fixed_data[i].zsize);
|
||||
}
|
||||
|
||||
destination->floating_used = 0;
|
||||
|
@ -92,9 +92,9 @@ void terrainHeightmapSave(PackStream* stream, TerrainHeightMap* heightmap)
|
|||
{
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].xstart);
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].xsize);
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].ystart);
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].ysize);
|
||||
for (j = 0; j < heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].ysize; j++)
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].zstart);
|
||||
packWriteInt(stream, &heightmap->fixed_data[i].zsize);
|
||||
for (j = 0; j < heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].zsize; j++)
|
||||
{
|
||||
packWriteDouble(stream, &heightmap->fixed_data[i].data[j]);
|
||||
}
|
||||
|
@ -112,13 +112,13 @@ void terrainHeightmapLoad(PackStream* stream, TerrainHeightMap* heightmap)
|
|||
{
|
||||
packReadInt(stream, &heightmap->fixed_data[i].xstart);
|
||||
packReadInt(stream, &heightmap->fixed_data[i].xsize);
|
||||
packReadInt(stream, &heightmap->fixed_data[i].ystart);
|
||||
packReadInt(stream, &heightmap->fixed_data[i].ysize);
|
||||
if (heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].ysize > 0)
|
||||
packReadInt(stream, &heightmap->fixed_data[i].zstart);
|
||||
packReadInt(stream, &heightmap->fixed_data[i].zsize);
|
||||
if (heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].zsize > 0)
|
||||
{
|
||||
heightmap->fixed_data[i].data = realloc(heightmap->fixed_data[i].data, sizeof(double) * heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].ysize);
|
||||
heightmap->fixed_data[i].data = realloc(heightmap->fixed_data[i].data, sizeof(double) * heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].zsize);
|
||||
}
|
||||
for (j = 0; j < heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].ysize; j++)
|
||||
for (j = 0; j < heightmap->fixed_data[i].xsize * heightmap->fixed_data[i].zsize; j++)
|
||||
{
|
||||
packReadDouble(stream, &heightmap->fixed_data[i].data[j]);
|
||||
}
|
||||
|
@ -127,32 +127,90 @@ void terrainHeightmapLoad(PackStream* stream, TerrainHeightMap* heightmap)
|
|||
heightmap->floating_used = 0;
|
||||
}
|
||||
|
||||
static inline int _checkDataHit(TerrainHeightMapData* data, double x, double z, double* result)
|
||||
{
|
||||
if (x > (double)data->xstart && x < (double)(data->xstart + data->xsize) && z > (double)data->zstart && z < (double)(data->zstart + data->zsize))
|
||||
{
|
||||
/* TODO Get interpolated value */
|
||||
*result = 0.0;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int terrainHeightmapGetHeight(TerrainHeightMap* heightmap, double x, double z, double* result)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < heightmap->fixed_count; i++)
|
||||
{
|
||||
if (_checkDataHit(heightmap->fixed_data + i, x, z, result))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (heightmap->floating_used && _checkDataHit(&heightmap->floating_data, x, z, result))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _prepareBrushStroke(TerrainHeightMap* heightmap, TerrainBrush* brush)
|
||||
{
|
||||
double cx = brush->relative_x / TERRAIN_HEIGHTMAP_DETAIL;
|
||||
double cz = brush->relative_z / TERRAIN_HEIGHTMAP_DETAIL;
|
||||
double cx = brush->relative_x;
|
||||
double cz = brush->relative_z;
|
||||
double s = brush->smoothed_size + brush->hard_radius;
|
||||
double sx = s / TERRAIN_HEIGHTMAP_DETAIL;
|
||||
double sz = s / TERRAIN_HEIGHTMAP_DETAIL;
|
||||
double sx = s;
|
||||
double sz = s;
|
||||
|
||||
int x1 = (int)floor(cx - sx);
|
||||
int x2 = (int)ceil(cx + sx);
|
||||
int z1 = (int)floor(cz - sz);
|
||||
int z2 = (int)ceil(cz + sz);
|
||||
|
||||
/* TODO Prepare floating data */
|
||||
/* Prepare floating data */
|
||||
if (heightmap->floating_used)
|
||||
{
|
||||
/* Grow floating area */
|
||||
/* TODO */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Init flaoting area */
|
||||
heightmap->floating_used = 1;
|
||||
heightmap->floating_data.xstart = x1;
|
||||
heightmap->floating_data.xsize = x2 - x1 + 1;
|
||||
heightmap->floating_data.zstart = z1;
|
||||
heightmap->floating_data.zsize = z2 - z1 + 1;
|
||||
|
||||
size_t new_size;
|
||||
new_size = sizeof(double) * heightmap->floating_data.xsize * heightmap->floating_data.zsize;
|
||||
heightmap->floating_data.data = realloc(heightmap->floating_data.data, new_size);
|
||||
memset(heightmap->floating_data.data, 0, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
void terrainBrushElevation(TerrainHeightMap* heightmap, TerrainBrush* brush, double value)
|
||||
{
|
||||
_prepareBrushStroke(heightmap, brush);
|
||||
}
|
||||
|
||||
void terrainBrushSmooth(TerrainHeightMap* heightmap, TerrainBrush* brush, double value)
|
||||
{
|
||||
_prepareBrushStroke(heightmap, brush);
|
||||
}
|
||||
|
||||
void terrainBrushAddNoise(TerrainHeightMap* heightmap, TerrainBrush* brush, NoiseGenerator* generator, double value)
|
||||
{
|
||||
_prepareBrushStroke(heightmap, brush);
|
||||
}
|
||||
|
||||
void terrainBrushReset(TerrainHeightMap* heightmap, TerrainBrush* brush, double value)
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
void terrainAutoPreset(TerrainDefinition* definition, TerrainPreset preset)
|
||||
{
|
||||
int resolution = 8;
|
||||
switch (preset)
|
||||
{
|
||||
case TERRAIN_PRESET_STANDARD:
|
||||
noiseClearLevels(definition->_height_noise);
|
||||
noiseAddLevelsSimple(definition->_height_noise, 8, 12.8, 12.8); /* Detail = 0.1 */
|
||||
noiseAddLevelsSimple(definition->_height_noise, resolution, pow(2.0, resolution - 1), 25.0);
|
||||
noiseSetFunctionParams(definition->_height_noise, NOISE_FUNCTION_SIMPLEX, 0.0);
|
||||
definition->height = 2.0;
|
||||
definition->scaling = 10.0;
|
||||
definition->scaling = 1.0;
|
||||
definition->height = 1.0;
|
||||
definition->shadow_smoothing = 0.03;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -88,7 +88,7 @@ Color terrainGetPreviewColor(Renderer* renderer, double x, double z, double deta
|
|||
Vector3 point;
|
||||
|
||||
point.x = x;
|
||||
point.y = renderer->terrain->getHeight(renderer, x, z);
|
||||
point.y = renderer->terrain->getHeight(renderer, x, z, 1);
|
||||
point.z = z;
|
||||
|
||||
return renderer->terrain->getFinalColor(renderer, point, detail);
|
||||
|
|
|
@ -3,14 +3,12 @@
|
|||
|
||||
#include "public.h"
|
||||
|
||||
#define TERRAIN_HEIGHTMAP_DETAIL 0.1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int xstart;
|
||||
int ystart;
|
||||
int zstart;
|
||||
int xsize;
|
||||
int ysize;
|
||||
int zsize;
|
||||
double* data;
|
||||
} TerrainHeightMapData;
|
||||
|
||||
|
@ -27,5 +25,6 @@ void terrainHeightmapDelete(TerrainHeightMap* heightmap);
|
|||
void terrainHeightmapCopy(TerrainHeightMap* source, TerrainHeightMap* destination);
|
||||
void terrainHeightmapSave(PackStream* stream, TerrainHeightMap* heightmap);
|
||||
void terrainHeightmapLoad(PackStream* stream, TerrainHeightMap* heightmap);
|
||||
int terrainHeightmapGetHeight(TerrainHeightMap* heightmap, double x, double z, double* result);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,7 +30,7 @@ typedef struct
|
|||
double _max_height;
|
||||
} TerrainDefinition;
|
||||
|
||||
typedef double (*FuncTerrainGetHeight)(Renderer* renderer, double x, double z);
|
||||
typedef double (*FuncTerrainGetHeight)(Renderer* renderer, double x, double z, int with_painting);
|
||||
typedef Color (*FuncTerrainGetFinalColor)(Renderer* renderer, Vector3 location, double precision);
|
||||
|
||||
typedef struct
|
||||
|
@ -50,6 +50,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);
|
||||
|
||||
Renderer terrainCreatePreviewRenderer();
|
||||
Color terrainGetPreviewColor(Renderer* renderer, double x, double z, double detail);
|
||||
|
|
|
@ -13,7 +13,7 @@ static inline Vector3 _getPoint(TerrainDefinition* definition, Renderer* rendere
|
|||
Vector3 result;
|
||||
|
||||
result.x = x;
|
||||
result.y = renderer->terrain->getHeight(renderer, x, z);
|
||||
result.y = renderer->terrain->getHeight(renderer, x, z, 1);
|
||||
result.z = z;
|
||||
|
||||
return result;
|
||||
|
|
|
@ -178,25 +178,25 @@ static inline TextureResult _getTerrainResult(Renderer* renderer, double x, doub
|
|||
|
||||
center.x = x;
|
||||
center.z = z;
|
||||
center.y = renderer->terrain->getHeight(renderer, center.x, center.z);
|
||||
center.y = renderer->terrain->getHeight(renderer, center.x, center.z, 1);
|
||||
|
||||
east.x = x + detail;
|
||||
east.z = z;
|
||||
east.y = renderer->terrain->getHeight(renderer, east.x, east.z);
|
||||
east.y = renderer->terrain->getHeight(renderer, east.x, east.z, 1);
|
||||
|
||||
south.x = x;
|
||||
south.z = z + detail;
|
||||
south.y = renderer->terrain->getHeight(renderer, south.x, south.z);
|
||||
south.y = renderer->terrain->getHeight(renderer, south.x, south.z, 1);
|
||||
|
||||
if (renderer->render_quality > 5)
|
||||
{
|
||||
west.x = x - detail;
|
||||
west.z = z;
|
||||
west.y = renderer->terrain->getHeight(renderer, west.x, west.z);
|
||||
west.y = renderer->terrain->getHeight(renderer, west.x, west.z, 1);
|
||||
|
||||
north.x = x;
|
||||
north.z = z - detail;
|
||||
north.y = renderer->terrain->getHeight(renderer, north.x, north.z);
|
||||
north.y = renderer->terrain->getHeight(renderer, north.x, north.z, 1);
|
||||
|
||||
result.normal = _getNormal4(center, north, east, south, west);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue