#ifndef CULTMATH_HLSL #define CULTMATH_HLSL // CultMath HLSL mirror. // // HLSL already owns the float2/float3/float4 type names and most primitive // intrinsics. This include names the CultMath semantic surface explicitly so // shader code, C# wrappers, and Rust/native parity fixtures can share the same // small vocabulary without local helper drift. static const float CULTMATH_PI = 3.14159265358979323846; static const float CULTMATH_TAU = 6.28318530717958647692; static const float CULTMATH_HALF_PI = 1.57079632679489661923; static const float CULTMATH_NORMALIZE_EPSILON = 1.0e-20; float cultmath_radians(float degrees_value) { return degrees_value * (CULTMATH_PI / 180.0); } float cultmath_degrees(float radians_value) { return radians_value * (180.0 / CULTMATH_PI); } float cultmath_frac(float value) { return value - floor(value); } float2 cultmath_frac(float2 value) { return value - floor(value); } float3 cultmath_frac(float3 value) { return value - floor(value); } float4 cultmath_frac(float4 value) { return value - floor(value); } float cultmath_clamp(float value, float minimum, float maximum) { return min(max(value, minimum), maximum); } float2 cultmath_clamp(float2 value, float2 minimum, float2 maximum) { return min(max(value, minimum), maximum); } float3 cultmath_clamp(float3 value, float3 minimum, float3 maximum) { return min(max(value, minimum), maximum); } float4 cultmath_clamp(float4 value, float4 minimum, float4 maximum) { return min(max(value, minimum), maximum); } float cultmath_saturate(float value) { return cultmath_clamp(value, 0.0, 1.0); } float2 cultmath_saturate(float2 value) { return cultmath_clamp(value, float2(0.0, 0.0), float2(1.0, 1.0)); } float3 cultmath_saturate(float3 value) { return cultmath_clamp(value, float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0)); } float4 cultmath_saturate(float4 value) { return cultmath_clamp(value, float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0)); } float cultmath_lerp(float start, float end, float amount) { return start + (end - start) * amount; } float2 cultmath_lerp(float2 start, float2 end, float2 amount) { return start + (end - start) * amount; } float3 cultmath_lerp(float3 start, float3 end, float3 amount) { return start + (end - start) * amount; } float4 cultmath_lerp(float4 start, float4 end, float4 amount) { return start + (end - start) * amount; } float cultmath_step(float edge, float value) { return value < edge ? 0.0 : 1.0; } float2 cultmath_step(float2 edge, float2 value) { return float2(cultmath_step(edge.x, value.x), cultmath_step(edge.y, value.y)); } float3 cultmath_step(float3 edge, float3 value) { return float3(cultmath_step(edge.x, value.x), cultmath_step(edge.y, value.y), cultmath_step(edge.z, value.z)); } float4 cultmath_step(float4 edge, float4 value) { return float4(cultmath_step(edge.x, value.x), cultmath_step(edge.y, value.y), cultmath_step(edge.z, value.z), cultmath_step(edge.w, value.w)); } float cultmath_smoothstep(float minimum, float maximum, float value) { float t = cultmath_saturate((value - minimum) / (maximum - minimum)); return t * t * (3.0 - 2.0 * t); } float2 cultmath_smoothstep(float2 minimum, float2 maximum, float2 value) { float2 t = cultmath_saturate((value - minimum) / (maximum - minimum)); return t * t * (float2(3.0, 3.0) - float2(2.0, 2.0) * t); } float3 cultmath_smoothstep(float3 minimum, float3 maximum, float3 value) { float3 t = cultmath_saturate((value - minimum) / (maximum - minimum)); return t * t * (float3(3.0, 3.0, 3.0) - float3(2.0, 2.0, 2.0) * t); } float4 cultmath_smoothstep(float4 minimum, float4 maximum, float4 value) { float4 t = cultmath_saturate((value - minimum) / (maximum - minimum)); return t * t * (float4(3.0, 3.0, 3.0, 3.0) - float4(2.0, 2.0, 2.0, 2.0) * t); } float cultmath_smoothstep01(float value) { return cultmath_smoothstep(0.0, 1.0, value); } float cultmath_smootherstep(float value) { value = cultmath_saturate(value); return value * value * value * (value * (value * 6.0 - 15.0) + 10.0); } float2 cultmath_simplex_mod289(float2 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } float3 cultmath_simplex_mod289(float3 value) { return value - floor(value * (1.0 / 289.0)) * 289.0; } float3 cultmath_simplex_permute(float3 value) { return cultmath_simplex_mod289(((value * 34.0) + 1.0) * value); } float cultmath_simplex_noise(float2 value) { const float4 c = float4( 0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); float2 i = floor(value + dot(value, c.yy)); float2 x0 = value - i + dot(i, c.xx); float2 i1 = x0.x > x0.y ? float2(1.0, 0.0) : float2(0.0, 1.0); float4 x12 = float4(x0.x + c.x - i1.x, x0.y + c.x - i1.y, x0.x + c.z, x0.y + c.z); i = cultmath_simplex_mod289(i); float3 p = cultmath_simplex_permute( cultmath_simplex_permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); float3 m = max(0.5 - float3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); m *= m; m *= m; float3 x = 2.0 * frac(p * c.w) - 1.0; float3 h = abs(x) - 0.5; float3 ox = floor(x + 0.5); float3 a0 = x - ox; m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); float3 g = float3( a0.x * x0.x + h.x * x0.y, a0.y * x12.x + h.y * x12.y, a0.z * x12.z + h.z * x12.w); return 130.0 * dot(m, g); } float cultmath_lengthsq(float2 value) { return dot(value, value); } float cultmath_lengthsq(float3 value) { return dot(value, value); } float cultmath_lengthsq(float4 value) { return dot(value, value); } float cultmath_distance(float2 left, float2 right) { return length(left - right); } float cultmath_distance(float3 left, float3 right) { return length(left - right); } float cultmath_distance(float4 left, float4 right) { return length(left - right); } float2 cultmath_normalize(float2 value) { return value / max(length(value), CULTMATH_NORMALIZE_EPSILON); } float3 cultmath_normalize(float3 value) { return value / max(length(value), CULTMATH_NORMALIZE_EPSILON); } float4 cultmath_normalize(float4 value) { return value / max(length(value), CULTMATH_NORMALIZE_EPSILON); } float2 cultmath_reflect(float2 incident, float2 normal_value) { return incident - 2.0 * dot(normal_value, incident) * normal_value; } float3 cultmath_reflect(float3 incident, float3 normal_value) { return incident - 2.0 * dot(normal_value, incident) * normal_value; } float2 cultmath_rotate(float2 value, float radians_value) { float s = sin(radians_value); float c = cos(radians_value); return float2(value.x * c - value.y * s, value.x * s + value.y * c); } float2 cultmath_rotate_degrees(float2 value, float degrees_value) { return cultmath_rotate(value, cultmath_radians(degrees_value)); } float cultmath_csum(float2 value) { return value.x + value.y; } float cultmath_csum(float3 value) { return value.x + value.y + value.z; } float cultmath_csum(float4 value) { return value.x + value.y + value.z + value.w; } float cultmath_decay(float source, float lambda_value, float dt) { return source * exp(-lambda_value * dt); } float2 cultmath_decay(float2 source, float lambda_value, float dt) { return source * exp(-lambda_value * dt); } float3 cultmath_decay(float3 source, float lambda_value, float dt) { return source * exp(-lambda_value * dt); } float4 cultmath_decay(float4 source, float lambda_value, float dt) { return source * exp(-lambda_value * dt); } float cultmath_damp(float start, float end, float lambda_value, float dt) { return cultmath_lerp(start, end, 1.0 - exp(-lambda_value * dt)); } float2 cultmath_damp(float2 start, float2 end, float lambda_value, float dt) { return cultmath_lerp(start, end, 1.0 - exp(-lambda_value * dt)); } float3 cultmath_damp(float3 start, float3 end, float lambda_value, float dt) { return cultmath_lerp(start, end, 1.0 - exp(-lambda_value * dt)); } float4 cultmath_damp(float4 start, float4 end, float lambda_value, float dt) { return cultmath_lerp(start, end, 1.0 - exp(-lambda_value * dt)); } float cultmath_catmullrom(float p0, float p1, float p2, float p3, float t) { float t2 = t * t; float t3 = t2 * t; return 0.5 * ((2.0 * p1) + (p2 - p0) * t + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); } float2 cultmath_catmullrom(float2 p0, float2 p1, float2 p2, float2 p3, float t) { float t2 = t * t; float t3 = t2 * t; return 0.5 * ((2.0 * p1) + (p2 - p0) * t + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); } float3 cultmath_catmullrom(float3 p0, float3 p1, float3 p2, float3 p3, float t) { float t2 = t * t; float t3 = t2 * t; return 0.5 * ((2.0 * p1) + (p2 - p0) * t + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); } float4 cultmath_catmullrom(float4 p0, float4 p1, float4 p2, float4 p3, float t) { float t2 = t * t; float t3 = t2 * t; return 0.5 * ((2.0 * p1) + (p2 - p0) * t + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); } float cultmath_quadratic_bezier(float p0, float p1, float p2, float t) { t = cultmath_saturate(t); float inv = 1.0 - t; return inv * inv * p0 + 2.0 * inv * t * p1 + t * t * p2; } float3 cultmath_quadratic_bezier(float3 p0, float3 p1, float3 p2, float t) { t = cultmath_saturate(t); float inv = 1.0 - t; return inv * inv * p0 + 2.0 * inv * t * p1 + t * t * p2; } float cultmath_cubic_bezier(float p0, float p1, float p2, float p3, float t) { t = cultmath_saturate(t); float inv = 1.0 - t; return inv * inv * inv * p0 + 3.0 * inv * inv * t * p1 + 3.0 * inv * t * t * p2 + t * t * t * p3; } float3 cultmath_cubic_bezier(float3 p0, float3 p1, float3 p2, float3 p3, float t) { t = cultmath_saturate(t); float inv = 1.0 - t; return inv * inv * inv * p0 + 3.0 * inv * inv * t * p1 + 3.0 * inv * t * t * p2 + t * t * t * p3; } float cultmath_hash(float value) { return cultmath_frac(sin(value) * 43758.5453); } float cultmath_hash(float2 value) { return cultmath_hash(dot(value, float2(127.1, 311.7))); } float cultmath_hash(float3 value) { return cultmath_hash(dot(value, float3(127.1, 311.7, 74.7))); } float cultmath_value_noise(float2 position) { float2 cell = floor(position); float2 local = cultmath_frac(position); float2 u = local * local * (float2(3.0, 3.0) - float2(2.0, 2.0) * local); float a = cultmath_hash(cell); float b = cultmath_hash(cell + float2(1.0, 0.0)); float c = cultmath_hash(cell + float2(0.0, 1.0)); float d = cultmath_hash(cell + float2(1.0, 1.0)); return cultmath_lerp(cultmath_lerp(a, b, u.x), cultmath_lerp(c, d, u.x), u.y); } float cultmath_value_noise_bicubic(float2 position) { float2 cell = floor(position); float2 local = cultmath_frac(position); float y0 = cultmath_catmullrom(cultmath_hash(cell + float2(-1.0, -1.0)), cultmath_hash(cell + float2(0.0, -1.0)), cultmath_hash(cell + float2(1.0, -1.0)), cultmath_hash(cell + float2(2.0, -1.0)), local.x); float y1 = cultmath_catmullrom(cultmath_hash(cell + float2(-1.0, 0.0)), cultmath_hash(cell + float2(0.0, 0.0)), cultmath_hash(cell + float2(1.0, 0.0)), cultmath_hash(cell + float2(2.0, 0.0)), local.x); float y2 = cultmath_catmullrom(cultmath_hash(cell + float2(-1.0, 1.0)), cultmath_hash(cell + float2(0.0, 1.0)), cultmath_hash(cell + float2(1.0, 1.0)), cultmath_hash(cell + float2(2.0, 1.0)), local.x); float y3 = cultmath_catmullrom(cultmath_hash(cell + float2(-1.0, 2.0)), cultmath_hash(cell + float2(0.0, 2.0)), cultmath_hash(cell + float2(1.0, 2.0)), cultmath_hash(cell + float2(2.0, 2.0)), local.x); return cultmath_catmullrom(y0, y1, y2, y3, local.y); } float cultmath_value_noise_texture(Texture2D noise_texture, SamplerState noise_sampler, float2 uv, float scale) { return noise_texture.SampleLevel(noise_sampler, uv * scale, 0.0).r; } float cultmath_value_noise_texture_bicubic(Texture2D noise_texture, SamplerState noise_sampler, float2 uv, float scale, float2 texel_size) { float2 position = uv * scale; float2 cell = floor(position); float2 local = cultmath_frac(position); float2 base_uv = cell / scale; float row0 = cultmath_catmullrom( noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(-1.0, -1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(0.0, -1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(1.0, -1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(2.0, -1.0), 0.0).r, local.x); float row1 = cultmath_catmullrom( noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(-1.0, 0.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(0.0, 0.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(1.0, 0.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(2.0, 0.0), 0.0).r, local.x); float row2 = cultmath_catmullrom( noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(-1.0, 1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(0.0, 1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(1.0, 1.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(2.0, 1.0), 0.0).r, local.x); float row3 = cultmath_catmullrom( noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(-1.0, 2.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(0.0, 2.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(1.0, 2.0), 0.0).r, noise_texture.SampleLevel(noise_sampler, base_uv + texel_size * float2(2.0, 2.0), 0.0).r, local.x); return cultmath_catmullrom(row0, row1, row2, row3, local.y); } #include "AdvancedErosionFilter.hlsl" #include "Planetary.hlsl" struct CultMathSphericalErosionParameters { float frequency; float amplitude; float lacunarity; float gain; float slope_strength; float detail; float seed; int octaves; }; struct CultMathSphericalErosionSample { float height_offset; float ridge; float gully; float3 gradient; }; float cultmath_spherical_cell_wave(float3 position, float3 flow, float3 cell, float seed) { float3 pivot = cell + float3(cultmath_hash(cell + seed + 11.7), cultmath_hash(cell.yzx + seed + 37.1), cultmath_hash(cell.zyx + seed + 73.9)); return dot(position - pivot, flow) * 6.28318530718 + cultmath_hash(cell + seed + 101.3) * 6.28318530718; } float cultmath_spherical_directional_wave(float3 position, float3 flow, float seed) { float3 cell = floor(position); float3 local = cultmath_frac(position); float3 weights = local * local * (3.0 - 2.0 * local); float z0 = cultmath_lerp( cultmath_lerp(cultmath_spherical_cell_wave(position, flow, cell + float3(0,0,0), seed), cultmath_spherical_cell_wave(position, flow, cell + float3(1,0,0), seed), weights.x), cultmath_lerp(cultmath_spherical_cell_wave(position, flow, cell + float3(0,1,0), seed), cultmath_spherical_cell_wave(position, flow, cell + float3(1,1,0), seed), weights.x), weights.y); float z1 = cultmath_lerp( cultmath_lerp(cultmath_spherical_cell_wave(position, flow, cell + float3(0,0,1), seed), cultmath_spherical_cell_wave(position, flow, cell + float3(1,0,1), seed), weights.x), cultmath_lerp(cultmath_spherical_cell_wave(position, flow, cell + float3(0,1,1), seed), cultmath_spherical_cell_wave(position, flow, cell + float3(1,1,1), seed), weights.x), weights.y); return cultmath_lerp(z0, z1, weights.z); } float3 cultmath_spherical_stable_tangent(float3 position) { float3 axis = abs(position.z) < 0.8 ? float3(0,0,1) : float3(0,1,0); return cultmath_normalize(cross(axis, position)); } CultMathSphericalErosionSample cultmath_spherical_erosion(float3 unit_position, float base_height, float3 base_gradient, CultMathSphericalErosionParameters p) { float3 position = cultmath_normalize(unit_position); float3 gradient = base_gradient - position * dot(base_gradient, position); float slope = length(gradient); float3 flow = slope > 1.0e-6 ? -gradient / slope : cultmath_spherical_stable_tangent(position); float frequency = max(p.frequency, 0.001); float amplitude = p.amplitude; float combi_mask = 1.0 - pow(1.0 - cultmath_saturate(slope * p.slope_strength), 2.0); float fade_target = cultmath_saturate(base_height * 0.5 + 0.5) * 2.0 - 1.0; CultMathSphericalErosionSample result = (CultMathSphericalErosionSample)0; [loop] for (int octave = 0; octave < min(max(p.octaves, 0), 8); octave++) { float phase = cultmath_spherical_directional_wave(position * frequency, flow, p.seed + octave * 19.19); float wave = cos(phase); float wave_slope = sin(phase); float faded = cultmath_lerp(fade_target, wave, combi_mask); result.height_offset += faded * amplitude; result.ridge = max(result.ridge, cultmath_saturate(faded)); result.gully = max(result.gully, cultmath_saturate(-faded)); float3 across = cultmath_normalize(cross(position, flow)); gradient += across * (sign(wave_slope) * amplitude * frequency * combi_mask); gradient -= position * dot(gradient, position); slope = length(gradient); flow = slope > 1.0e-6 ? -gradient / slope : cultmath_spherical_stable_tangent(position); float new_mask = 1.0 - pow(1.0 - cultmath_saturate(abs(wave_slope) * 1.35), 2.0); combi_mask = (1.0 - pow(1.0 - cultmath_saturate(combi_mask), max(p.detail, 0.001))) * new_mask; fade_target = faded; frequency *= max(p.lacunarity, 1.01); amplitude *= cultmath_saturate(p.gain); } result.gradient = gradient; return result; } float3 cultmath_planetary_radial_refinement_step( float3 position, float3 ray_direction, float3 center, float target_radius, float minimum_derivative, float maximum_correction) { float3 local=position-center; float radius=length(local); float3 radial=local/max(radius,0.0001); float error=radius-target_radius; float derivative=dot(ray_direction,radial); float safe_minimum=max(minimum_derivative,0.0001); float safe_derivative=abs(derivative)>safe_minimum?derivative:(derivative<0?-safe_minimum:safe_minimum); float correction=clamp(-error/safe_derivative,-abs(maximum_correction),abs(maximum_correction)); return position+ray_direction*correction; } #endif