mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-28 00:00:03 +02:00
add axis inversion + terrace step fix + manual size input store
This commit is contained in:
@@ -274,22 +274,27 @@ static void insert_floor_ceil_brush( scene::Node& entity,
|
||||
|
||||
static void insert_wall_brush( scene::Node& entity,
|
||||
double x_bl, double x_tl, double x_br, double x_tr,
|
||||
double gy, double gmx_y, double gz, double gmx_z,
|
||||
double outer_x, double limit_x,
|
||||
double gu, double gmx_u, double gz, double gmx_z,
|
||||
double outer, double limit,
|
||||
const char* top_tex, const char* caulk,
|
||||
bool is_left, bool alt_dir ){
|
||||
bool is_left, bool alt_dir, Axis axis ){
|
||||
// Wall geometry is floor/ceiling rotated 90°.
|
||||
// Coordinate mapping: floor(fx,fy,fz) → world(fz, fx, fy)
|
||||
// gy→fx, gz→fy, gmx_y→fmx, gmx_z→fmy, x_*→fz_*, outer_x/limit_x→fmin_z/fsolid_top
|
||||
// Left wall: inner surface faces +X (like floor), cap at outer_x (min_z side).
|
||||
// Right wall: inner surface faces −X (like ceiling), cap at outer_x (solid_top side).
|
||||
// For a Y-running tunnel (walls at fixed X, left/right), coordinate mapping:
|
||||
// floor(fx,fy,fz) → world(fz, fx, fy) — gu→fx, gz→fy, gmx_u→fmx, gmx_z→fmy,
|
||||
// x_*→fz_*, outer/limit→fmin_z/fsolid_top. This is a proper 90° rotation, so
|
||||
// face winding carries over unchanged.
|
||||
// For an X-running tunnel (walls at fixed Y, near/far), the same swap
|
||||
// (world(fx, fz, fy)) is a mirror image of the above rather than a rotation,
|
||||
// so v1/v2 are swapped per face to keep normals pointing outward.
|
||||
// Left/near wall: inner surface faces away from outer (min_z side).
|
||||
// Right/far wall: inner surface faces away from outer (solid_top side).
|
||||
_QERFaceData face;
|
||||
const bool is_ceiling = !is_left;
|
||||
const double min_z = is_left ? outer_x : limit_x;
|
||||
const double solid_top = is_left ? limit_x : outer_x;
|
||||
const double min_z = is_left ? outer : limit;
|
||||
const double solid_top = is_left ? limit : outer;
|
||||
|
||||
// Aliases to match insert_floor_ceil_brush variable names exactly.
|
||||
const double x = gy, y = gz, mx = gmx_y, my = gmx_z;
|
||||
const double x = gu, y = gz, mx = gmx_u, my = gmx_z;
|
||||
const double z_bl = x_bl, z_tl = x_tl, z_br = x_br, z_tr = x_tr;
|
||||
|
||||
// fw: emit a face using floor coordinate order but with world axes permuted.
|
||||
@@ -297,7 +302,10 @@ static void insert_wall_brush( scene::Node& entity,
|
||||
double fx1, double fy1, double fz1,
|
||||
double fx2, double fy2, double fz2,
|
||||
const char* shader ){
|
||||
fill_face( face, fz0, fx0, fy0, fz1, fx1, fy1, fz2, fx2, fy2, shader );
|
||||
if ( axis == Axis::Y )
|
||||
fill_face( face, fz0, fx0, fy0, fz1, fx1, fy1, fz2, fx2, fy2, shader );
|
||||
else
|
||||
fill_face( face, fx0, fz0, fy0, fx2, fz2, fy2, fx1, fz1, fy1, shader );
|
||||
};
|
||||
|
||||
if ( !alt_dir ) {
|
||||
@@ -366,7 +374,7 @@ static void insert_wall_brush( scene::Node& entity,
|
||||
|
||||
void build_tunnel_brushes( const BrushData& target, double step_x, double step_y,
|
||||
const TunnelMaps& maps, const char* top_texture,
|
||||
double cave_height, double slope_height ){
|
||||
double cave_height, double slope_height, Axis axis ){
|
||||
// Undo is started by the caller so deletion + generation form one step.
|
||||
scene::Node& floor_entity = create_func_group();
|
||||
scene::Node& ceil_entity = create_func_group();
|
||||
@@ -420,48 +428,56 @@ void build_tunnel_brushes( const BrushData& target, double step_x, double step_y
|
||||
}
|
||||
}
|
||||
|
||||
// Walls — must match the range generated by terrain_engine.
|
||||
// Walls — must match the range generated by terrain_engine. Grid runs along
|
||||
// Y (left/right walls at fixed X) for a Y-running tunnel, or along X
|
||||
// (near/far walls at fixed Y) for an X-running tunnel.
|
||||
// slope_height may be negative (downward slope), so clamp accordingly.
|
||||
double wall_min_z = target.max_z + std::min( 0.0, slope_height );
|
||||
double wall_max_z = wall_min_z + cave_height + std::abs( slope_height );
|
||||
double step_z = maps.step_z;
|
||||
double outer_left = target.min_x - step_x;
|
||||
double outer_right = target.max_x + step_x;
|
||||
double limit_x = ( target.min_x + target.max_x ) / 2.0;
|
||||
|
||||
int gy_index = 0;
|
||||
for ( double gy = target.min_y; gy < target.max_y - 0.01; gy += step_y, ++gy_index ) {
|
||||
const bool along_y = ( axis == Axis::Y );
|
||||
const double along_min = along_y ? target.min_y : target.min_x;
|
||||
const double along_max = along_y ? target.max_y : target.max_x;
|
||||
const double along_step = along_y ? step_y : step_x;
|
||||
const double outer_lo = along_y ? target.min_x - step_x : target.min_y - step_y;
|
||||
const double outer_hi = along_y ? target.max_x + step_x : target.max_y + step_y;
|
||||
const double limit = along_y ? ( target.min_x + target.max_x ) / 2.0
|
||||
: ( target.min_y + target.max_y ) / 2.0;
|
||||
|
||||
int gu_index = 0;
|
||||
for ( double gu = along_min; gu < along_max - 0.01; gu += along_step, ++gu_index ) {
|
||||
int gz_index = 0;
|
||||
for ( double gz = wall_min_z; gz < wall_max_z - 0.01; gz += step_z, ++gz_index ) {
|
||||
double gmx_y = gy + step_y < target.max_y ? gy + step_y : target.max_y;
|
||||
double gmx_z = gz + step_z < wall_max_z ? gz + step_z : wall_max_z;
|
||||
double gmx_u = gu + along_step < along_max ? gu + along_step : along_max;
|
||||
double gmx_z = gz + step_z < wall_max_z ? gz + step_z : wall_max_z;
|
||||
|
||||
auto safe_wall = [&]( const HeightMap& m, double kx, double ky, double fallback ) -> double {
|
||||
auto it = m.find( { r2( kx ), r2( ky ) } );
|
||||
auto safe_wall = [&]( const HeightMap& m, double ku, double kz, double fallback ) -> double {
|
||||
auto it = m.find( { r2( ku ), r2( kz ) } );
|
||||
if ( it == m.end() ) {
|
||||
globalErrorStream() << "TerrainGenerator: wall map missing key ("
|
||||
<< kx << ", " << ky << ") - using fallback\n";
|
||||
<< ku << ", " << kz << ") - using fallback\n";
|
||||
return fallback;
|
||||
}
|
||||
return it->second;
|
||||
};
|
||||
|
||||
double lx_bl = safe_wall( maps.left_wall_map, gy, gz, limit_x );
|
||||
double lx_tl = safe_wall( maps.left_wall_map, gy, gmx_z, limit_x );
|
||||
double lx_br = safe_wall( maps.left_wall_map, gmx_y, gz, limit_x );
|
||||
double lx_tr = safe_wall( maps.left_wall_map, gmx_y, gmx_z, limit_x );
|
||||
double lx_bl = safe_wall( maps.left_wall_map, gu, gz, limit );
|
||||
double lx_tl = safe_wall( maps.left_wall_map, gu, gmx_z, limit );
|
||||
double lx_br = safe_wall( maps.left_wall_map, gmx_u, gz, limit );
|
||||
double lx_tr = safe_wall( maps.left_wall_map, gmx_u, gmx_z, limit );
|
||||
|
||||
double rx_bl = safe_wall( maps.right_wall_map, gy, gz, limit_x );
|
||||
double rx_tl = safe_wall( maps.right_wall_map, gy, gmx_z, limit_x );
|
||||
double rx_br = safe_wall( maps.right_wall_map, gmx_y, gz, limit_x );
|
||||
double rx_tr = safe_wall( maps.right_wall_map, gmx_y, gmx_z, limit_x );
|
||||
double rx_bl = safe_wall( maps.right_wall_map, gu, gz, limit );
|
||||
double rx_tl = safe_wall( maps.right_wall_map, gu, gmx_z, limit );
|
||||
double rx_br = safe_wall( maps.right_wall_map, gmx_u, gz, limit );
|
||||
double rx_tr = safe_wall( maps.right_wall_map, gmx_u, gmx_z, limit );
|
||||
|
||||
bool alt_dir = ( ( gy_index + gz_index ) % 2 ) != 0;
|
||||
bool alt_dir = ( ( gu_index + gz_index ) % 2 ) != 0;
|
||||
|
||||
insert_wall_brush( lwall_entity, lx_bl, lx_tl, lx_br, lx_tr,
|
||||
gy, gmx_y, gz, gmx_z, outer_left, limit_x, top_texture, caulk, true, alt_dir );
|
||||
gu, gmx_u, gz, gmx_z, outer_lo, limit, top_texture, caulk, true, alt_dir, axis );
|
||||
insert_wall_brush( rwall_entity, rx_bl, rx_tl, rx_br, rx_tr,
|
||||
gy, gmx_y, gz, gmx_z, outer_right, limit_x, top_texture, caulk, false, alt_dir );
|
||||
gu, gmx_u, gz, gmx_z, outer_hi, limit, top_texture, caulk, false, alt_dir, axis );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,4 +11,4 @@ void build_terrain_brushes( const BrushData& target, double step_x, double step_
|
||||
|
||||
void build_tunnel_brushes( const BrushData& target, double step_x, double step_y,
|
||||
const TunnelMaps& maps, const char* top_texture,
|
||||
double cave_height, double slope_height );
|
||||
double cave_height, double slope_height, Axis axis );
|
||||
|
||||
@@ -67,7 +67,8 @@ void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y
|
||||
HeightMap generate_height_map( const BrushData& target, double step_x, double step_y,
|
||||
ShapeType shape_type, double shape_height,
|
||||
double variance, double frequency,
|
||||
NoiseType noise_type, double terrace_step ){
|
||||
NoiseType noise_type, double terrace_step,
|
||||
Axis axis ){
|
||||
HeightMap height_map;
|
||||
|
||||
double seed_x = random_double() * 10000.0;
|
||||
@@ -78,6 +79,9 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
double nx = target.width_x > 0 ? ( x - target.min_x ) / target.width_x : 0.0;
|
||||
double ny = target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0;
|
||||
|
||||
// Position along the axis Slope / Ridge / Valley run along.
|
||||
double na = ( axis == Axis::X ) ? nx : ny;
|
||||
|
||||
double center_dist = std::min( 1.0, std::sqrt(
|
||||
( nx - 0.5 ) * ( nx - 0.5 ) + ( ny - 0.5 ) * ( ny - 0.5 ) ) / 0.5 );
|
||||
|
||||
@@ -92,12 +96,12 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
break;
|
||||
}
|
||||
case ShapeType::Ridge: {
|
||||
double dist = std::min( 1.0, std::abs( nx - 0.5 ) / 0.5 );
|
||||
double dist = std::min( 1.0, std::abs( na - 0.5 ) / 0.5 );
|
||||
base_z = shape_height * 0.5 * ( 1.0 + std::cos( dist * std::numbers::pi ) );
|
||||
break;
|
||||
}
|
||||
case ShapeType::Slope: {
|
||||
base_z = shape_height * nx;
|
||||
base_z = shape_height * na;
|
||||
break;
|
||||
}
|
||||
case ShapeType::Volcano: {
|
||||
@@ -108,7 +112,7 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
break;
|
||||
}
|
||||
case ShapeType::Valley: {
|
||||
double dist = std::min( 1.0, std::abs( nx - 0.5 ) / 0.5 );
|
||||
double dist = std::min( 1.0, std::abs( na - 0.5 ) / 0.5 );
|
||||
base_z = shape_height * 0.5 * ( 1.0 - std::cos( dist * std::numbers::pi ) );
|
||||
break;
|
||||
}
|
||||
@@ -116,6 +120,14 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
break;
|
||||
}
|
||||
|
||||
// Terrace the clean shape height first, before noise is mixed in, so
|
||||
// terrace bands stay flat strips instead of being broken up by
|
||||
// per-vertex noise jitter. Noise is then layered on top as surface
|
||||
// detail on the (possibly stepped) base height.
|
||||
double shape_z = target.max_z + base_z;
|
||||
if ( shape_type != ShapeType::Flat && terrace_step > 0.0 )
|
||||
shape_z = std::floor( shape_z / terrace_step ) * terrace_step;
|
||||
|
||||
double noise_z = 0.0;
|
||||
if ( variance > 0.0 ) {
|
||||
if ( noise_type == NoiseType::Random ) {
|
||||
@@ -127,9 +139,7 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
}
|
||||
}
|
||||
|
||||
double final_z = target.max_z + base_z + noise_z;
|
||||
if ( shape_type != ShapeType::Flat && terrace_step > 0.0 )
|
||||
final_z = std::floor( final_z / terrace_step ) * terrace_step;
|
||||
double final_z = shape_z + noise_z;
|
||||
|
||||
height_map[{ round2( x ), round2( y ) }] = std::round( final_z );
|
||||
}
|
||||
@@ -145,7 +155,8 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
|
||||
TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x, double step_y,
|
||||
double cave_height, double slope_height,
|
||||
double variance, double frequency,
|
||||
NoiseType noise_type, double terrace_step ){
|
||||
NoiseType noise_type, double terrace_step,
|
||||
Axis axis ){
|
||||
TunnelMaps result;
|
||||
|
||||
double seed_floor_x = random_double() * 10000.0;
|
||||
@@ -155,15 +166,37 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
double seed_wall_l = random_double() * 10000.0;
|
||||
double seed_wall_r = random_double() * 10000.0;
|
||||
|
||||
double center_x = ( target.min_x + target.max_x ) / 2.0;
|
||||
double half_width = target.width_x / 2.0;
|
||||
// Cross-section (rounding) axis: X for a Y-running tunnel, Y for an
|
||||
// X-running tunnel. Reproduces the original formulas exactly when axis == Y.
|
||||
const bool along_y = ( axis == Axis::Y );
|
||||
const double center_cs = along_y ? ( target.min_x + target.max_x ) / 2.0
|
||||
: ( target.min_y + target.max_y ) / 2.0;
|
||||
const double half_cs = along_y ? target.width_x / 2.0
|
||||
: target.length_y / 2.0;
|
||||
|
||||
// Floor and ceiling
|
||||
for ( double x = target.min_x; x <= target.max_x + 0.01; x += step_x ) {
|
||||
for ( double y = target.min_y; y <= target.max_y + 0.01; y += step_y ) {
|
||||
double t = half_width > 0 ? std::min( 1.0, std::abs( x - center_x ) / half_width ) : 0.0;
|
||||
double cs_pos = along_y ? x : y;
|
||||
double t = half_cs > 0 ? std::min( 1.0, std::abs( cs_pos - center_cs ) / half_cs ) : 0.0;
|
||||
double blend = 1.0 - std::sqrt( std::max( 0.0, 1.0 - t * t ) );
|
||||
|
||||
double along_pos = along_y
|
||||
? ( target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0 )
|
||||
: ( target.width_x > 0 ? ( x - target.min_x ) / target.width_x : 0.0 );
|
||||
double base_z = target.max_z + slope_height * along_pos;
|
||||
double floor_base = base_z + blend * ( cave_height * 0.25 );
|
||||
double ceil_base = base_z + cave_height - blend * ( cave_height * 0.25 );
|
||||
|
||||
// Terrace the clean floor/ceiling first, before noise is mixed in, so
|
||||
// terrace bands stay flat strips instead of being broken up by
|
||||
// per-vertex noise jitter (see the standard-terrain heightmap for the
|
||||
// same reasoning).
|
||||
if ( terrace_step > 0.0 ) {
|
||||
floor_base = std::floor( floor_base / terrace_step ) * terrace_step;
|
||||
ceil_base = std::ceil( ceil_base / terrace_step ) * terrace_step;
|
||||
}
|
||||
|
||||
double floor_noise = 0.0, ceil_noise = 0.0;
|
||||
if ( variance > 0.0 ) {
|
||||
if ( noise_type == NoiseType::Random ) {
|
||||
@@ -177,10 +210,8 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
}
|
||||
}
|
||||
|
||||
double ny = target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0;
|
||||
double base_z = target.max_z + slope_height * ny;
|
||||
double floor_z = base_z + blend * ( cave_height * 0.25 ) + floor_noise;
|
||||
double ceil_z = base_z + cave_height - blend * ( cave_height * 0.25 ) - ceil_noise;
|
||||
double floor_z = floor_base + floor_noise;
|
||||
double ceil_z = ceil_base - ceil_noise;
|
||||
|
||||
if ( floor_z > ceil_z ) {
|
||||
double mid = ( floor_z + ceil_z ) / 2.0;
|
||||
@@ -188,11 +219,6 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
ceil_z = mid;
|
||||
}
|
||||
|
||||
if ( terrace_step > 0.0 ) {
|
||||
floor_z = std::floor( floor_z / terrace_step ) * terrace_step;
|
||||
ceil_z = std::ceil( ceil_z / terrace_step ) * terrace_step;
|
||||
}
|
||||
|
||||
result.floor_map[ { round2( x ), round2( y ) }] = std::round( floor_z );
|
||||
result.ceiling_map[ { round2( x ), round2( y ) }] = std::round( ceil_z );
|
||||
}
|
||||
@@ -207,10 +233,18 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
double wall_max_z = wall_min_z + total_wall_height;
|
||||
result.step_z = step_z;
|
||||
|
||||
// Left and right walls — grid over (Y, Z)
|
||||
for ( double y = target.min_y; y <= target.max_y + 0.01; y += step_y ) {
|
||||
// Walls — grid over (along-tunnel coordinate, Z). For a Y-running tunnel
|
||||
// this is (Y, Z) with walls at fixed X (left/right); for an X-running
|
||||
// tunnel it's (X, Z) with walls at fixed Y (near/far).
|
||||
double along_min = along_y ? target.min_y : target.min_x;
|
||||
double along_max = along_y ? target.max_y : target.max_x;
|
||||
double along_step = along_y ? step_y : step_x;
|
||||
double wall_lo = along_y ? target.min_x : target.min_y;
|
||||
double wall_hi = along_y ? target.max_x : target.max_y;
|
||||
|
||||
for ( double u = along_min; u <= along_max + 0.01; u += along_step ) {
|
||||
for ( double z = wall_min_z; z <= wall_max_z + 0.01; z += step_z ) {
|
||||
double ry = round2( y );
|
||||
double ru = round2( u );
|
||||
double rz = round2( z );
|
||||
|
||||
double wall_noise = 0.0;
|
||||
@@ -219,10 +253,10 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
wall_noise = random_double() * variance;
|
||||
} else {
|
||||
wall_noise = std::abs( sample_noise( noise_type,
|
||||
( y + seed_wall_l ) * frequency, ( z + seed_wall_l ) * frequency ) ) * variance;
|
||||
( u + seed_wall_l ) * frequency, ( z + seed_wall_l ) * frequency ) ) * variance;
|
||||
}
|
||||
}
|
||||
result.left_wall_map[{ ry, rz }] = std::round( target.min_x + wall_noise );
|
||||
result.left_wall_map[{ ru, rz }] = std::round( wall_lo + wall_noise );
|
||||
|
||||
wall_noise = 0.0;
|
||||
if ( variance > 0.0 ) {
|
||||
@@ -230,10 +264,10 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
|
||||
wall_noise = random_double() * variance;
|
||||
} else {
|
||||
wall_noise = std::abs( sample_noise( noise_type,
|
||||
( y + seed_wall_r ) * frequency, ( z + seed_wall_r ) * frequency ) ) * variance;
|
||||
( u + seed_wall_r ) * frequency, ( z + seed_wall_r ) * frequency ) ) * variance;
|
||||
}
|
||||
}
|
||||
result.right_wall_map[{ ry, rz }] = std::round( target.max_x - wall_noise );
|
||||
result.right_wall_map[{ ru, rz }] = std::round( wall_hi - wall_noise );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,13 @@ enum class NoiseType {
|
||||
Random = 2
|
||||
};
|
||||
|
||||
// Direction Slope / Ridge / Valley run along. Ignored by radially symmetric
|
||||
// shapes (Hill, Crater, Volcano) and by Tunnel/SlopeTunnel, which run along Y.
|
||||
enum class Axis {
|
||||
X = 0,
|
||||
Y = 1
|
||||
};
|
||||
|
||||
BrushData make_manual_brush_data( double width, double length, double height );
|
||||
|
||||
void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y );
|
||||
@@ -43,9 +50,11 @@ void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y
|
||||
HeightMap generate_height_map( const BrushData& target, double step_x, double step_y,
|
||||
ShapeType shape_type, double shape_height,
|
||||
double variance, double frequency,
|
||||
NoiseType noise_type, double terrace_step );
|
||||
NoiseType noise_type, double terrace_step,
|
||||
Axis axis );
|
||||
|
||||
TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x, double step_y,
|
||||
double cave_height, double slope_height,
|
||||
double variance, double frequency,
|
||||
NoiseType noise_type, double terrace_step );
|
||||
NoiseType noise_type, double terrace_step,
|
||||
Axis axis );
|
||||
|
||||
@@ -140,14 +140,28 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
form->addRow( "Length (Y):", sel_l_label );
|
||||
form->addRow( "Height (Z):", sel_h_label );
|
||||
|
||||
// Manual size inputs (shown in Manual Size mode)
|
||||
auto *manual_w_spin = new SpinBox( 64, 131072, 1024, 0, 64 );
|
||||
auto *manual_l_spin = new SpinBox( 64, 131072, 1024, 0, 64 );
|
||||
auto *manual_h_spin = new SpinBox( 64, 131072, 64, 0, 64 );
|
||||
// Manual size inputs (shown in Manual Size mode). Remembers the last
|
||||
// values used across dialog open/close within this editor session, so
|
||||
// repeated manual-size generation doesn't reset to hardcoded defaults
|
||||
// every time the dialog is reopened.
|
||||
constexpr double DEFAULT_MANUAL_W = 1024.0, DEFAULT_MANUAL_L = 1024.0, DEFAULT_MANUAL_H = 64.0;
|
||||
static double s_manual_w = DEFAULT_MANUAL_W, s_manual_l = DEFAULT_MANUAL_L, s_manual_h = DEFAULT_MANUAL_H;
|
||||
auto *manual_w_spin = new SpinBox( 64, 131072, s_manual_w, 0, 64 );
|
||||
auto *manual_l_spin = new SpinBox( 64, 131072, s_manual_l, 0, 64 );
|
||||
auto *manual_h_spin = new SpinBox( 64, 131072, s_manual_h, 0, 64 );
|
||||
form->addRow( "Width (X):", manual_w_spin );
|
||||
form->addRow( "Length (Y):", manual_l_spin );
|
||||
form->addRow( "Height (Z):", manual_h_spin );
|
||||
|
||||
// Reset the remembered manual size back to the original hardcoded default.
|
||||
auto *manual_reset_btn = new QPushButton( "Reset to Default" );
|
||||
QObject::connect( manual_reset_btn, &QPushButton::clicked, [&](){
|
||||
manual_w_spin->setValue( DEFAULT_MANUAL_W );
|
||||
manual_l_spin->setValue( DEFAULT_MANUAL_L );
|
||||
manual_h_spin->setValue( DEFAULT_MANUAL_H );
|
||||
} );
|
||||
form->addRow( "", manual_reset_btn );
|
||||
|
||||
// Sub-square size: preset combo + Advanced checkbox in one row
|
||||
auto *sq_widget = new QWidget;
|
||||
auto *sq_hbox = new QHBoxLayout( sq_widget );
|
||||
@@ -181,6 +195,16 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
shape_combo->setCurrentIndex( 0 ); // Flat
|
||||
form->addRow( "Base Shape:", shape_combo );
|
||||
|
||||
// Axis — meaningful for the directional shapes (Slope, Ridge, Valley) and
|
||||
// for tunnels (which way the tunnel runs). Radially symmetric shapes
|
||||
// (Hill, Crater, Volcano) ignore it. Default differs per shape (see
|
||||
// update_shape below) to match each shape's original hardcoded axis.
|
||||
auto *axis_combo = new ComboBox;
|
||||
axis_combo->addItem( "X Axis", (int)Axis::X );
|
||||
axis_combo->addItem( "Y Axis", (int)Axis::Y );
|
||||
axis_combo->setCurrentIndex( 0 );
|
||||
form->addRow( "Axis:", axis_combo );
|
||||
|
||||
// Shape height — editable spinbox (manual mode or non-slope shapes).
|
||||
// For Slope / Slope Tunnel in Use Selection mode, replaced by a
|
||||
// read-only label derived from the selection brush's Z extent.
|
||||
@@ -369,6 +393,7 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
const double step_x = advanced ? step_x_spin->value() : sq_combo->currentData().toInt();
|
||||
const double step_y = advanced ? step_y_spin->value() : sq_combo->currentData().toInt();
|
||||
const ShapeType shape = (ShapeType)shape_combo->currentData().toInt();
|
||||
const Axis axis = (Axis)axis_combo->currentData().toInt();
|
||||
const NoiseType noise = (NoiseType)noise_combo->currentData().toInt();
|
||||
const double tun_height = tunnel_height_spin->value();
|
||||
const double variance = variance_spin->value();
|
||||
@@ -507,12 +532,12 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
const double cave_height = ( shape == ShapeType::SlopeTunnel ) ? tun_height : shape_height;
|
||||
const double slope_height = ( shape == ShapeType::SlopeTunnel ) ? shape_height : 0;
|
||||
const double tunnel_terrace = ( shape == ShapeType::SlopeTunnel ) ? terrace : 0.0;
|
||||
auto maps = generate_tunnel_height_maps( target, step_x, step_y, cave_height, slope_height, variance, frequency, noise, tunnel_terrace );
|
||||
build_tunnel_brushes( target, step_x, step_y, maps, texture, cave_height, slope_height );
|
||||
auto maps = generate_tunnel_height_maps( target, step_x, step_y, cave_height, slope_height, variance, frequency, noise, tunnel_terrace, axis );
|
||||
build_tunnel_brushes( target, step_x, step_y, maps, texture, cave_height, slope_height, axis );
|
||||
}
|
||||
else {
|
||||
bool split_diagonally = ( variance > 0 || shape != ShapeType::Flat );
|
||||
auto height_map = generate_height_map( target, step_x, step_y, shape, shape_height, variance, frequency, noise, terrace );
|
||||
auto height_map = generate_height_map( target, step_x, step_y, shape, shape_height, variance, frequency, noise, terrace, axis );
|
||||
build_terrain_brushes( target, step_x, step_y, height_map, texture, split_diagonally );
|
||||
}
|
||||
|
||||
@@ -576,9 +601,10 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
set_row_visible( sel_w_label, use_sel );
|
||||
set_row_visible( sel_l_label, use_sel );
|
||||
set_row_visible( sel_h_label, use_sel );
|
||||
set_row_visible( manual_w_spin, !use_sel );
|
||||
set_row_visible( manual_l_spin, !use_sel );
|
||||
set_row_visible( manual_h_spin, !use_sel );
|
||||
set_row_visible( manual_w_spin, !use_sel );
|
||||
set_row_visible( manual_l_spin, !use_sel );
|
||||
set_row_visible( manual_h_spin, !use_sel );
|
||||
set_row_visible( manual_reset_btn, !use_sel );
|
||||
};
|
||||
|
||||
// Advanced sub-square toggle
|
||||
@@ -593,7 +619,17 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
const ShapeType st = (ShapeType)shape_combo->itemData( idx ).toInt();
|
||||
const bool is_flat = ( st == ShapeType::Flat );
|
||||
const bool is_slope_tunnel = ( st == ShapeType::SlopeTunnel );
|
||||
const bool is_tunnel_shape = ( st == ShapeType::Tunnel || st == ShapeType::SlopeTunnel );
|
||||
const bool has_axis = ( st == ShapeType::Slope || st == ShapeType::Ridge || st == ShapeType::Valley
|
||||
|| is_tunnel_shape );
|
||||
|
||||
set_row_visible( axis_combo, has_axis );
|
||||
if ( has_axis ) {
|
||||
// Match each shape's original hardcoded axis: Slope/Ridge/Valley
|
||||
// ran along X, tunnels ran along Y. Re-applied on every shape
|
||||
// switch, same as the slope-height auto-fill below.
|
||||
axis_combo->setCurrentIndex( is_tunnel_shape ? (int)Axis::Y : (int)Axis::X );
|
||||
}
|
||||
set_row_visible( shape_height_spin, !is_flat );
|
||||
if ( !is_flat ) {
|
||||
if ( auto *lbl = qobject_cast<QLabel*>( form->labelForField( shape_height_spin ) ) )
|
||||
@@ -640,6 +676,9 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
|
||||
QObject::connect( &dialog, &QDialog::finished, &loop, &QEventLoop::quit );
|
||||
loop.exec();
|
||||
}
|
||||
s_manual_w = manual_w_spin->value();
|
||||
s_manual_l = manual_l_spin->value();
|
||||
s_manual_h = manual_h_spin->value();
|
||||
s_active_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user