need more noise features
This commit is contained in:
72
main.cpp
72
main.cpp
@@ -72,13 +72,13 @@ bool generateGradientImage(const std::string& filename, int width = 512, int hei
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate terrain simulation image
|
// Generate terrain simulation image
|
||||||
bool generateTerrainImage(const std::string& filename, int width = 512, int height = 512) {
|
bool generateTerrainImage(const std::string& filename, int width = 512, int height = 512, uint32_t seed = 42, float scale = 4.0f, int octaves = 4,
|
||||||
|
float persistence = 0.5f, float lacunarity = 2.0f, float waterlevel = 0.3f, float elevation = 1.0f) {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
|
|
||||||
static Sim2 sim(width, height);
|
Sim2 sim(width, height, seed, scale, octaves, persistence, lacunarity, waterlevel, elevation);
|
||||||
|
sim.generateTerrain();
|
||||||
// Randomize seed for variety
|
// Randomize seed for variety
|
||||||
sim.randomizeSeed();
|
|
||||||
|
|
||||||
// Render to RGB image
|
// Render to RGB image
|
||||||
std::vector<uint8_t> imageData = sim.renderToRGB(width, height);
|
std::vector<uint8_t> imageData = sim.renderToRGB(width, height);
|
||||||
@@ -171,6 +171,70 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
SimpleHTTPServer server(port, webRoot);
|
SimpleHTTPServer server(port, webRoot);
|
||||||
|
|
||||||
|
// Add parameter setting endpoint
|
||||||
|
server.addRoute("/api/set-parameters", [webRoot](const std::string& method, const std::string& body) {
|
||||||
|
if (method == "POST") {
|
||||||
|
try {
|
||||||
|
// Parse JSON parameters
|
||||||
|
// Simple JSON parsing - in a real application you'd use a proper JSON library
|
||||||
|
float scale = 4.0f;
|
||||||
|
int octaves = 4;
|
||||||
|
float persistence = 0.5f;
|
||||||
|
float lacunarity = 2.0f;
|
||||||
|
float elevation = 1.0f;
|
||||||
|
float waterLevel = 0.3f;
|
||||||
|
uint32_t seed = 42;
|
||||||
|
|
||||||
|
// Extract parameters from JSON (simplified parsing)
|
||||||
|
if (body.find("\"scale\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"scale\":") + 8;
|
||||||
|
scale = std::stof(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"octaves\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"octaves\":") + 10;
|
||||||
|
octaves = std::stoi(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"persistence\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"persistence\":") + 14;
|
||||||
|
persistence = std::stof(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"lacunarity\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"lacunarity\":") + 13;
|
||||||
|
lacunarity = std::stof(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"elevation\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"elevation\":") + 12;
|
||||||
|
elevation = std::stof(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"waterLevel\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"waterLevel\":") + 13;
|
||||||
|
waterLevel = std::stof(body.substr(pos));
|
||||||
|
}
|
||||||
|
if (body.find("\"seed\"") != std::string::npos) {
|
||||||
|
size_t pos = body.find("\"seed\":") + 7;
|
||||||
|
seed = std::stoul(body.substr(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create NEW instance each time (remove 'static')
|
||||||
|
Sim2 sim(512, 512, seed, scale, octaves, persistence, lacunarity, waterLevel, elevation);
|
||||||
|
|
||||||
|
// Regenerate and save
|
||||||
|
std::vector<uint8_t> imageData = sim.renderToRGB(512, 512);
|
||||||
|
bool success = generateTerrainImage(webRoot + "/output/display.jxl", 512, 512, seed, scale, octaves, persistence, lacunarity, waterLevel, elevation);
|
||||||
|
//bool success = JXLWriter::saveJXL(webRoot + "/output/display.jxl", imageData, 512, 512);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return std::make_pair(200, std::basic_string("{\"status\":\"success\"}"));
|
||||||
|
} else {
|
||||||
|
return std::make_pair(500, std::basic_string("{\"error\":\"Failed to generate terrain\"}"));
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return std::make_pair(400, std::basic_string("{\"error\":\"Invalid parameters\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_pair(405, std::basic_string("{\"error\":\"Method Not Allowed\"}"));
|
||||||
|
});
|
||||||
|
|
||||||
// Add timing stats endpoint
|
// Add timing stats endpoint
|
||||||
server.addRoute("/api/timing-stats", [](const std::string& method, const std::string& body) {
|
server.addRoute("/api/timing-stats", [](const std::string& method, const std::string& body) {
|
||||||
if (method == "GET") {
|
if (method == "GET") {
|
||||||
|
|||||||
@@ -32,14 +32,15 @@ private:
|
|||||||
Vec4 waterColor;
|
Vec4 waterColor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Sim2(int width = 512, int height = 512, uint32_t seed = 42)
|
Sim2(int width = 512, int height = 512, uint32_t seed = 42, float scale = 4.0f, int octaves = 4,
|
||||||
: gridWidth(width), gridHeight(height), scale(4.0f), octaves(4),
|
float persistence = 0.5f, float lacunarity = 2.0f, float waterlevel = 0.3f, float elevation = 1.0f)
|
||||||
persistence(0.5f), lacunarity(2.0f), seed(seed), offset(0, 0),
|
: gridWidth(width), gridHeight(height), scale(scale), octaves(octaves),
|
||||||
elevationMultiplier(1.0f), waterLevel(0.3f),
|
persistence(persistence), lacunarity(lacunarity), seed(seed), offset(0, 0),
|
||||||
|
elevationMultiplier(elevation), waterLevel(waterlevel),
|
||||||
landColor(0.2f, 0.8f, 0.2f, 1.0f), // Green
|
landColor(0.2f, 0.8f, 0.2f, 1.0f), // Green
|
||||||
waterColor(0.2f, 0.3f, 0.8f, 1.0f) // Blue
|
waterColor(0.2f, 0.3f, 0.8f, 1.0f) // Blue
|
||||||
{
|
{
|
||||||
noiseGenerator = std::make_unique<Noise2>(seed,Noise2::WORLEY,Noise2::PRECOMPUTED);
|
noiseGenerator = std::make_unique<Noise2>(seed,Noise2::PERLIN,Noise2::PRECOMPUTED);
|
||||||
generateTerrain();
|
generateTerrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
204
util/noise/pnoise.hpp
Normal file
204
util/noise/pnoise.hpp
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
|
||||||
|
#ifndef PERLIN_NOISE_HPP
|
||||||
|
#define PERLIN_NOISE_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class PerlinNoise {
|
||||||
|
private:
|
||||||
|
std::vector<int> permutation;
|
||||||
|
|
||||||
|
// Fade function as defined by Ken Perlin
|
||||||
|
static double fade(double t) {
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linear interpolation
|
||||||
|
static double lerp(double t, double a, double b) {
|
||||||
|
return a + t * (b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gradient function
|
||||||
|
static double grad(int hash, double x, double y, double z) {
|
||||||
|
int h = hash & 15;
|
||||||
|
double u = h < 8 ? x : y;
|
||||||
|
double v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
|
||||||
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor with optional seed
|
||||||
|
PerlinNoise(unsigned int seed = 0) {
|
||||||
|
permutation.resize(256);
|
||||||
|
// Initialize with values 0-255
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
permutation[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle using the seed
|
||||||
|
std::shuffle(permutation.begin(), permutation.end(), std::default_random_engine(seed));
|
||||||
|
|
||||||
|
// Duplicate the permutation vector
|
||||||
|
permutation.insert(permutation.end(), permutation.begin(), permutation.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1D Perlin noise
|
||||||
|
double noise(double x) const {
|
||||||
|
return noise(x, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2D Perlin noise
|
||||||
|
double noise(double x, double y) const {
|
||||||
|
return noise(x, y, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3D Perlin noise (main implementation)
|
||||||
|
double noise(double x, double y, double z) const {
|
||||||
|
// Find unit cube that contains the point
|
||||||
|
int X = (int)floor(x) & 255;
|
||||||
|
int Y = (int)floor(y) & 255;
|
||||||
|
int Z = (int)floor(z) & 255;
|
||||||
|
|
||||||
|
// Find relative x, y, z of point in cube
|
||||||
|
x -= floor(x);
|
||||||
|
y -= floor(y);
|
||||||
|
z -= floor(z);
|
||||||
|
|
||||||
|
// Compute fade curves for x, y, z
|
||||||
|
double u = fade(x);
|
||||||
|
double v = fade(y);
|
||||||
|
double w = fade(z);
|
||||||
|
|
||||||
|
// Hash coordinates of the 8 cube corners
|
||||||
|
int A = permutation[X] + Y;
|
||||||
|
int AA = permutation[A] + Z;
|
||||||
|
int AB = permutation[A + 1] + Z;
|
||||||
|
int B = permutation[X + 1] + Y;
|
||||||
|
int BA = permutation[B] + Z;
|
||||||
|
int BB = permutation[B + 1] + Z;
|
||||||
|
|
||||||
|
// Add blended results from 8 corners of cube
|
||||||
|
double res = lerp(w, lerp(v, lerp(u, grad(permutation[AA], x, y, z),
|
||||||
|
grad(permutation[BA], x - 1, y, z)),
|
||||||
|
lerp(u, grad(permutation[AB], x, y - 1, z),
|
||||||
|
grad(permutation[BB], x - 1, y - 1, z))),
|
||||||
|
lerp(v, lerp(u, grad(permutation[AA + 1], x, y, z - 1),
|
||||||
|
grad(permutation[BA + 1], x - 1, y, z - 1)),
|
||||||
|
lerp(u, grad(permutation[AB + 1], x, y - 1, z - 1),
|
||||||
|
grad(permutation[BB + 1], x - 1, y - 1, z - 1))));
|
||||||
|
return (res + 1.0) / 2.0; // Normalize to [0,1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fractal Brownian Motion (fBm) - multiple octaves of noise
|
||||||
|
double fractal(size_t octaves, double x, double y = 0.0, double z = 0.0) const {
|
||||||
|
double value = 0.0;
|
||||||
|
double amplitude = 1.0;
|
||||||
|
double frequency = 1.0;
|
||||||
|
double maxValue = 0.0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; ++i) {
|
||||||
|
value += amplitude * noise(x * frequency, y * frequency, z * frequency);
|
||||||
|
maxValue += amplitude;
|
||||||
|
amplitude *= 0.5;
|
||||||
|
frequency *= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value / maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turbulence - absolute value of noise for more dramatic effects
|
||||||
|
double turbulence(size_t octaves, double x, double y = 0.0, double z = 0.0) const {
|
||||||
|
double value = 0.0;
|
||||||
|
double amplitude = 1.0;
|
||||||
|
double frequency = 1.0;
|
||||||
|
double maxValue = 0.0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; ++i) {
|
||||||
|
value += amplitude * std::abs(noise(x * frequency, y * frequency, z * frequency));
|
||||||
|
maxValue += amplitude;
|
||||||
|
amplitude *= 0.5;
|
||||||
|
frequency *= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value / maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ridged multi-fractal - creates ridge-like patterns
|
||||||
|
double ridgedMultiFractal(size_t octaves, double x, double y = 0.0, double z = 0.0,
|
||||||
|
double lacunarity = 2.0, double gain = 0.5, double offset = 1.0) const {
|
||||||
|
double value = 0.0;
|
||||||
|
double amplitude = 1.0;
|
||||||
|
double frequency = 1.0;
|
||||||
|
double prev = 1.0;
|
||||||
|
double weight;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < octaves; ++i) {
|
||||||
|
double signal = offset - std::abs(noise(x * frequency, y * frequency, z * frequency));
|
||||||
|
signal *= signal;
|
||||||
|
signal *= prev;
|
||||||
|
|
||||||
|
weight = std::clamp(signal * gain, 0.0, 1.0);
|
||||||
|
value += signal * amplitude;
|
||||||
|
prev = weight;
|
||||||
|
amplitude *= weight;
|
||||||
|
frequency *= lacunarity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility functions for common noise operations
|
||||||
|
namespace PerlinUtils {
|
||||||
|
// Create a 1D noise array
|
||||||
|
static std::vector<double> generate1DNoise(int width, double scale = 1.0, unsigned int seed = 0) {
|
||||||
|
PerlinNoise pn(seed);
|
||||||
|
std::vector<double> result(width);
|
||||||
|
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
result[x] = pn.noise(x * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a 2D noise array
|
||||||
|
static std::vector<std::vector<double>> generate2DNoise(int width, int height,
|
||||||
|
double scale = 1.0, unsigned int seed = 0) {
|
||||||
|
PerlinNoise pn(seed);
|
||||||
|
std::vector<std::vector<double>> result(height, std::vector<double>(width));
|
||||||
|
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
result[y][x] = pn.noise(x * scale, y * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a 3D noise array
|
||||||
|
static std::vector<std::vector<std::vector<double>>> generate3DNoise(int width, int height, int depth,
|
||||||
|
double scale = 1.0, unsigned int seed = 0) {
|
||||||
|
PerlinNoise pn(seed);
|
||||||
|
std::vector<std::vector<std::vector<double>>> result(
|
||||||
|
depth, std::vector<std::vector<double>>(
|
||||||
|
height, std::vector<double>(width)));
|
||||||
|
|
||||||
|
for (int z = 0; z < depth; ++z) {
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
result[z][y][x] = pn.noise(x * scale, y * scale, z * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -34,40 +34,6 @@ public:
|
|||||||
PRECOMPUTED
|
PRECOMPUTED
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
std::mt19937 rng;
|
|
||||||
std::uniform_real_distribution<float> dist;
|
|
||||||
|
|
||||||
// Precomputed gradient directions for 8 directions
|
|
||||||
static constexpr std::array<Grad, 8> grads = {
|
|
||||||
Grad{1.0f, 0.0f},
|
|
||||||
Grad{0.707f, 0.707f},
|
|
||||||
Grad{0.0f, 1.0f},
|
|
||||||
Grad{-0.707f, 0.707f},
|
|
||||||
Grad{-1.0f, 0.0f},
|
|
||||||
Grad{-0.707f, -0.707f},
|
|
||||||
Grad{0.0f, -1.0f},
|
|
||||||
Grad{0.707f, -0.707f}
|
|
||||||
};
|
|
||||||
|
|
||||||
NoiseType currentType;
|
|
||||||
GradientType gradType;
|
|
||||||
uint32_t currentSeed;
|
|
||||||
|
|
||||||
// Permutation table for Simplex noise
|
|
||||||
std::array<int, 512> perm;
|
|
||||||
|
|
||||||
// For Worley noise
|
|
||||||
std::vector<Vec2> featurePoints;
|
|
||||||
|
|
||||||
// For Gabor noise
|
|
||||||
float gaborFrequency;
|
|
||||||
float gaborBandwidth;
|
|
||||||
|
|
||||||
// For wavelet noise
|
|
||||||
std::vector<float> waveletCoefficients;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Noise2(uint32_t seed = 0, NoiseType type = PERLIN, GradientType gradType = PRECOMPUTED) :
|
Noise2(uint32_t seed = 0, NoiseType type = PERLIN, GradientType gradType = PRECOMPUTED) :
|
||||||
rng(seed), dist(0.0f, 1.0f), currentType(type), gradType(gradType),
|
rng(seed), dist(0.0f, 1.0f), currentType(type), gradType(gradType),
|
||||||
currentSeed(seed), gaborFrequency(4.0f), gaborBandwidth(0.5f)
|
currentSeed(seed), gaborFrequency(4.0f), gaborBandwidth(0.5f)
|
||||||
@@ -384,12 +350,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate terrain-like noise (useful for heightmaps)
|
// Generate terrain-like noise (useful for heightmaps)
|
||||||
Grid2 generateTerrainNoise(int width, int height,
|
Grid2 generateTerrainNoise(int width, int height, float scale = 1.0f, int octaves = 4,
|
||||||
float scale = 1.0f,
|
float persistence = 0.5f, uint32_t seed = 0, const Vec2& offset = Vec2(0, 0)) {
|
||||||
int octaves = 4,
|
|
||||||
float persistence = 0.5f,
|
|
||||||
uint32_t seed = 0,
|
|
||||||
const Vec2& offset = Vec2(0, 0)) {
|
|
||||||
if (seed != 0) setSeed(seed);
|
if (seed != 0) setSeed(seed);
|
||||||
|
|
||||||
Grid2 grid(width * height);
|
Grid2 grid(width * height);
|
||||||
@@ -453,6 +415,38 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::mt19937 rng;
|
||||||
|
std::uniform_real_distribution<float> dist;
|
||||||
|
|
||||||
|
// Precomputed gradient directions for 8 directions
|
||||||
|
static constexpr std::array<Grad, 8> grads = {
|
||||||
|
Grad{1.0f, 0.0f},
|
||||||
|
Grad{0.707f, 0.707f},
|
||||||
|
Grad{0.0f, 1.0f},
|
||||||
|
Grad{-0.707f, 0.707f},
|
||||||
|
Grad{-1.0f, 0.0f},
|
||||||
|
Grad{-0.707f, -0.707f},
|
||||||
|
Grad{0.0f, -1.0f},
|
||||||
|
Grad{0.707f, -0.707f}
|
||||||
|
};
|
||||||
|
|
||||||
|
NoiseType currentType;
|
||||||
|
GradientType gradType;
|
||||||
|
uint32_t currentSeed;
|
||||||
|
|
||||||
|
// Permutation table for Simplex noise
|
||||||
|
std::array<int, 512> perm;
|
||||||
|
|
||||||
|
// For Worley noise
|
||||||
|
std::vector<Vec2> featurePoints;
|
||||||
|
|
||||||
|
// For Gabor noise
|
||||||
|
float gaborFrequency;
|
||||||
|
float gaborBandwidth;
|
||||||
|
|
||||||
|
// For wavelet noise
|
||||||
|
std::vector<float> waveletCoefficients;
|
||||||
|
|
||||||
// Initialize permutation table for Simplex noise
|
// Initialize permutation table for Simplex noise
|
||||||
void initializePermutationTable(uint32_t seed) {
|
void initializePermutationTable(uint32_t seed) {
|
||||||
std::mt19937 localRng(seed);
|
std::mt19937 localRng(seed);
|
||||||
|
|||||||
@@ -16,6 +16,53 @@
|
|||||||
<button onclick="switchMode()" id="switchModeBtn" style="display: none;">Switch Mode</button>
|
<button onclick="switchMode()" id="switchModeBtn" style="display: none;">Switch Mode</button>
|
||||||
<button onclick="showStats()" id="statsBtn">Show Performance Stats</button>
|
<button onclick="showStats()" id="statsBtn">Show Performance Stats</button>
|
||||||
<button onclick="clearStats()" id="clearStatsBtn">Clear Stats</button>
|
<button onclick="clearStats()" id="clearStatsBtn">Clear Stats</button>
|
||||||
|
<button onclick="toggleParameters()" id="paramsBtn">Show Parameters</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Parameter Control Panel -->
|
||||||
|
<div id="parameterPanel" class="parameter-panel" style="display: none;">
|
||||||
|
<h3>Terrain Parameters</h3>
|
||||||
|
<div class="param-grid">
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="scale">Scale:</label>
|
||||||
|
<input type="range" id="scale" min="0.1" max="20" step="0.1" value="4.0">
|
||||||
|
<span id="scaleValue">4.0</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="octaves">Octaves:</label>
|
||||||
|
<input type="range" id="octaves" min="1" max="8" step="1" value="4">
|
||||||
|
<span id="octavesValue">4</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="persistence">Persistence:</label>
|
||||||
|
<input type="range" id="persistence" min="0" max="1" step="0.05" value="0.5">
|
||||||
|
<span id="persistenceValue">0.5</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="lacunarity">Lacunarity:</label>
|
||||||
|
<input type="range" id="lacunarity" min="1" max="4" step="0.1" value="2.0">
|
||||||
|
<span id="lacunarityValue">2.0</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="elevation">Elevation Multiplier:</label>
|
||||||
|
<input type="range" id="elevation" min="0.1" max="3" step="0.1" value="1.0">
|
||||||
|
<span id="elevationValue">1.0</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="waterLevel">Water Level:</label>
|
||||||
|
<input type="range" id="waterLevel" min="0" max="1" step="0.05" value="0.3">
|
||||||
|
<span id="waterLevelValue">0.3</span>
|
||||||
|
</div>
|
||||||
|
<div class="param-group">
|
||||||
|
<label for="seed">Seed:</label>
|
||||||
|
<input type="number" id="seed" value="42" min="0" max="999999">
|
||||||
|
<button onclick="randomizeSeed()">Random</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="param-actions">
|
||||||
|
<button onclick="applyParameters()">Apply Parameters</button>
|
||||||
|
<button onclick="resetParameters()">Reset to Default</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="image-container">
|
<div class="image-container">
|
||||||
|
|||||||
105
web/script.js
105
web/script.js
@@ -216,3 +216,108 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
refreshImage();
|
refreshImage();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function toggleParameters() {
|
||||||
|
const panel = document.getElementById('parameterPanel');
|
||||||
|
const btn = document.getElementById('paramsBtn');
|
||||||
|
|
||||||
|
if (panel.style.display === 'none') {
|
||||||
|
panel.style.display = 'block';
|
||||||
|
btn.textContent = 'Hide Parameters';
|
||||||
|
loadCurrentParameters();
|
||||||
|
} else {
|
||||||
|
panel.style.display = 'none';
|
||||||
|
btn.textContent = 'Show Parameters';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCurrentParameters() {
|
||||||
|
// This would ideally fetch current parameters from the server
|
||||||
|
// For now, we'll just initialize the sliders with their current values
|
||||||
|
setupSlider('scale', 'scaleValue', 4.0);
|
||||||
|
setupSlider('octaves', 'octavesValue', 4);
|
||||||
|
setupSlider('persistence', 'persistenceValue', 0.5);
|
||||||
|
setupSlider('lacunarity', 'lacunarityValue', 2.0);
|
||||||
|
setupSlider('elevation', 'elevationValue', 1.0);
|
||||||
|
setupSlider('waterLevel', 'waterLevelValue', 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupSlider(sliderId, valueId, defaultValue) {
|
||||||
|
const slider = document.getElementById(sliderId);
|
||||||
|
const value = document.getElementById(valueId);
|
||||||
|
|
||||||
|
slider.value = defaultValue;
|
||||||
|
value.textContent = defaultValue;
|
||||||
|
|
||||||
|
slider.addEventListener('input', function() {
|
||||||
|
value.textContent = this.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyParameters() {
|
||||||
|
const params = {
|
||||||
|
scale: parseFloat(document.getElementById('scale').value),
|
||||||
|
octaves: parseInt(document.getElementById('octaves').value),
|
||||||
|
persistence: parseFloat(document.getElementById('persistence').value),
|
||||||
|
lacunarity: parseFloat(document.getElementById('lacunarity').value),
|
||||||
|
elevation: parseFloat(document.getElementById('elevation').value),
|
||||||
|
waterLevel: parseFloat(document.getElementById('waterLevel').value),
|
||||||
|
seed: parseInt(document.getElementById('seed').value)
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Sending parameters:", params); // Debug log
|
||||||
|
|
||||||
|
fetch('/api/set-parameters', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log("Server response:", data); // Debug log
|
||||||
|
if (data.status === 'success') {
|
||||||
|
updateStatus('Parameters applied successfully');
|
||||||
|
if (currentMode === 'terrain') {
|
||||||
|
refreshTerrain();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateStatus('Error applying parameters', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error applying parameters:', error);
|
||||||
|
updateStatus('Error applying parameters', 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetParameters() {
|
||||||
|
document.getElementById('scale').value = 4.0;
|
||||||
|
document.getElementById('scaleValue').textContent = '4.0';
|
||||||
|
|
||||||
|
document.getElementById('octaves').value = 4;
|
||||||
|
document.getElementById('octavesValue').textContent = '4';
|
||||||
|
|
||||||
|
document.getElementById('persistence').value = 0.5;
|
||||||
|
document.getElementById('persistenceValue').textContent = '0.5';
|
||||||
|
|
||||||
|
document.getElementById('lacunarity').value = 2.0;
|
||||||
|
document.getElementById('lacunarityValue').textContent = '2.0';
|
||||||
|
|
||||||
|
document.getElementById('elevation').value = 1.0;
|
||||||
|
document.getElementById('elevationValue').textContent = '1.0';
|
||||||
|
|
||||||
|
document.getElementById('waterLevel').value = 0.3;
|
||||||
|
document.getElementById('waterLevelValue').textContent = '0.3';
|
||||||
|
|
||||||
|
document.getElementById('seed').value = 42;
|
||||||
|
|
||||||
|
updateStatus('Parameters reset to defaults');
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomizeSeed() {
|
||||||
|
const newSeed = Math.floor(Math.random() * 1000000);
|
||||||
|
document.getElementById('seed').value = newSeed;
|
||||||
|
updateStatus('Random seed generated: ' + newSeed);
|
||||||
|
}
|
||||||
@@ -114,3 +114,59 @@ button.danger:hover {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parameter-panel {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-group label {
|
||||||
|
min-width: 150px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-group input[type="range"] {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-group input[type="number"] {
|
||||||
|
width: 80px;
|
||||||
|
padding: 5px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-group span {
|
||||||
|
min-width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.param-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user