a lot of noise stuff
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "../util/grid/grid3eigen.hpp"
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
#include "../util/output/bmpwriter.hpp"
|
#include "../util/output/bmpwriter.hpp"
|
||||||
@@ -54,6 +55,43 @@ struct stardefaults {
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class NoiseType {
|
||||||
|
Perlin = 0,
|
||||||
|
Value = 1,
|
||||||
|
Fractal = 2,
|
||||||
|
Turbulence = 3,
|
||||||
|
Ridged = 4,
|
||||||
|
Billow = 5,
|
||||||
|
WhiteNoise = 6,
|
||||||
|
WorleyNoise = 7,
|
||||||
|
VoronoiNoise = 8,
|
||||||
|
CrystalNoise = 9,
|
||||||
|
DomainWarp = 10,
|
||||||
|
CurlNoise = 11
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoisePreviewState {
|
||||||
|
int width = 512;
|
||||||
|
int height = 512;
|
||||||
|
NoiseType currentType = NoiseType::Perlin;
|
||||||
|
NoiseType currentSubType = NoiseType::Perlin;
|
||||||
|
|
||||||
|
int seed = 1337;
|
||||||
|
float scale = 0.02f;
|
||||||
|
int octaves = 4;
|
||||||
|
float persistence = 0.5f;
|
||||||
|
float lacunarity = 2.0f;
|
||||||
|
float ridgeOffset = 1.0f;
|
||||||
|
float strength = 2.0f;
|
||||||
|
float substrength = 2.0f;
|
||||||
|
|
||||||
|
// Visualization
|
||||||
|
float offset[2] = {0.0f, 0.0f}; // Panning
|
||||||
|
GLuint textureId = 0;
|
||||||
|
std::vector<uint8_t> pixelBuffer;
|
||||||
|
bool needsUpdate = true;
|
||||||
|
};
|
||||||
|
|
||||||
std::mutex PreviewMutex;
|
std::mutex PreviewMutex;
|
||||||
GLuint textu = 0;
|
GLuint textu = 0;
|
||||||
bool textureInitialized = false;
|
bool textureInitialized = false;
|
||||||
@@ -72,10 +110,135 @@ bool firstFrameMeasured = false;
|
|||||||
|
|
||||||
// Stats update timer
|
// Stats update timer
|
||||||
std::chrono::steady_clock::time_point lastStatsUpdate;
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
const std::chrono::seconds STATS_UPDATE_INTERVAL(60); // Update stats once per minute
|
const std::chrono::seconds STATS_UPDATE_INTERVAL(60);
|
||||||
std::string cachedStats;
|
std::string cachedStats;
|
||||||
bool statsNeedUpdate = true;
|
bool statsNeedUpdate = true;
|
||||||
|
|
||||||
|
// Helper to generate the 2D noise texture
|
||||||
|
void updateNoiseTexture(NoisePreviewState& state) {
|
||||||
|
glGenTextures(1, &state.textureId);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, state.textureId);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
state.pixelBuffer.resize(state.width * state.height * 3);
|
||||||
|
|
||||||
|
// Create a local noise generator with current seed
|
||||||
|
PNoise2 generator(state.seed);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for (int y = 0; y < state.height; ++y) {
|
||||||
|
for (int x = 0; x < state.width; ++x) {
|
||||||
|
float nx = (x + state.offset[0]) * state.scale;
|
||||||
|
float ny = (y + state.offset[1]) * state.scale;
|
||||||
|
Eigen::Vector2f point(nx, ny);
|
||||||
|
|
||||||
|
float val = 0.0f;
|
||||||
|
|
||||||
|
// Call specific PNoise2 method
|
||||||
|
switch (state.currentType) {
|
||||||
|
case NoiseType::Perlin:
|
||||||
|
val = generator.permute(point); // [-1, 1]
|
||||||
|
break;
|
||||||
|
case NoiseType::Value:
|
||||||
|
val = generator.valueNoise(point); // [-1, 1]
|
||||||
|
break;
|
||||||
|
case NoiseType::Fractal:
|
||||||
|
val = generator.fractalNoise(point, state.octaves, state.persistence, state.lacunarity);
|
||||||
|
break;
|
||||||
|
case NoiseType::Turbulence:
|
||||||
|
val = generator.turbulence(point, state.octaves); // [0, unbounded usually]
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Ridged:
|
||||||
|
val = generator.ridgedNoise(point, state.octaves, state.ridgeOffset);
|
||||||
|
// Ridged output can be large, scale down slightly for preview
|
||||||
|
val = (val * 0.5f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Billow:
|
||||||
|
val = generator.billowNoise(point, state.octaves);
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::WhiteNoise:
|
||||||
|
val = generator.whiteNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::WorleyNoise:
|
||||||
|
val = generator.worleyNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::VoronoiNoise:
|
||||||
|
val = generator.voronoiNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::CrystalNoise:
|
||||||
|
val = generator.crystalNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::DomainWarp:
|
||||||
|
val = generator.domainWarp(point, state.strength);
|
||||||
|
break;
|
||||||
|
case NoiseType::CurlNoise:
|
||||||
|
Eigen::Vector2f flow = generator.curlNoise(point);
|
||||||
|
flow = point + (flow * state.strength);
|
||||||
|
switch (state.currentSubType) {
|
||||||
|
case NoiseType::Perlin:
|
||||||
|
val = generator.permute(flow); // [-1, 1]
|
||||||
|
break;
|
||||||
|
case NoiseType::Value:
|
||||||
|
val = generator.valueNoise(flow); // [-1, 1]
|
||||||
|
break;
|
||||||
|
case NoiseType::Fractal:
|
||||||
|
val = generator.fractalNoise(flow, state.octaves, state.persistence, state.lacunarity);
|
||||||
|
break;
|
||||||
|
case NoiseType::Turbulence:
|
||||||
|
val = generator.turbulence(flow, state.octaves); // [0, unbounded usually]
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Ridged:
|
||||||
|
val = generator.ridgedNoise(flow, state.octaves, state.ridgeOffset);
|
||||||
|
// Ridged output can be large, scale down slightly for preview
|
||||||
|
val = (val * 0.5f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Billow:
|
||||||
|
val = generator.billowNoise(flow, state.octaves);
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::WhiteNoise:
|
||||||
|
val = generator.whiteNoise(flow);
|
||||||
|
break;
|
||||||
|
case NoiseType::WorleyNoise:
|
||||||
|
val = generator.worleyNoise(flow);
|
||||||
|
break;
|
||||||
|
case NoiseType::VoronoiNoise:
|
||||||
|
val = generator.voronoiNoise(flow);
|
||||||
|
break;
|
||||||
|
case NoiseType::CrystalNoise:
|
||||||
|
val = generator.crystalNoise(flow);
|
||||||
|
break;
|
||||||
|
case NoiseType::DomainWarp:
|
||||||
|
val = generator.domainWarp(flow, state.substrength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float norm = (val + 1.0f) * 0.5f;
|
||||||
|
norm = std::clamp(norm, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
uint8_t color = static_cast<uint8_t>(norm * 255);
|
||||||
|
|
||||||
|
int idx = (y * state.width + x) * 3;
|
||||||
|
state.pixelBuffer[idx] = color;
|
||||||
|
state.pixelBuffer[idx+1] = color;
|
||||||
|
state.pixelBuffer[idx+2] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, state.textureId);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, state.width, state.height,
|
||||||
|
0, GL_RGB, GL_UNSIGNED_BYTE, state.pixelBuffer.data());
|
||||||
|
|
||||||
|
state.needsUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
||||||
if (!grid.empty()) grid.clear();
|
if (!grid.empty()) grid.clear();
|
||||||
|
|
||||||
@@ -181,7 +344,6 @@ void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
|||||||
std::lock_guard<std::mutex> lock(PreviewMutex);
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
updatePreview = true;
|
updatePreview = true;
|
||||||
|
|
||||||
// Measure render time
|
|
||||||
auto renderStart = std::chrono::high_resolution_clock::now();
|
auto renderStart = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
frame currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
frame currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
||||||
@@ -189,7 +351,6 @@ void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
|||||||
auto renderEnd = std::chrono::high_resolution_clock::now();
|
auto renderEnd = std::chrono::high_resolution_clock::now();
|
||||||
renderFrameTime = std::chrono::duration<double>(renderEnd - renderStart).count();
|
renderFrameTime = std::chrono::duration<double>(renderEnd - renderStart).count();
|
||||||
|
|
||||||
// Update FPS calculations
|
|
||||||
if (!firstFrameMeasured) {
|
if (!firstFrameMeasured) {
|
||||||
renderFrameTimes.resize(FRAME_HISTORY_SIZE, renderFrameTime);
|
renderFrameTimes.resize(FRAME_HISTORY_SIZE, renderFrameTime);
|
||||||
firstFrameMeasured = true;
|
firstFrameMeasured = true;
|
||||||
@@ -198,7 +359,6 @@ void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
|||||||
renderFrameTimes[frameHistoryIndex] = renderFrameTime;
|
renderFrameTimes[frameHistoryIndex] = renderFrameTime;
|
||||||
frameHistoryIndex = (frameHistoryIndex + 1) % FRAME_HISTORY_SIZE;
|
frameHistoryIndex = (frameHistoryIndex + 1) % FRAME_HISTORY_SIZE;
|
||||||
|
|
||||||
// Calculate average frame time and FPS
|
|
||||||
avgRenderFrameTime = 0.0;
|
avgRenderFrameTime = 0.0;
|
||||||
int validFrames = 0;
|
int validFrames = 0;
|
||||||
for (int i = 0; i < FRAME_HISTORY_SIZE; i++) {
|
for (int i = 0; i < FRAME_HISTORY_SIZE; i++) {
|
||||||
@@ -217,7 +377,6 @@ void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
|||||||
updateStatsCache(grid);
|
updateStatsCache(grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update texture
|
|
||||||
if (textu == 0) {
|
if (textu == 0) {
|
||||||
glGenTextures(1, &textu);
|
glGenTextures(1, &textu);
|
||||||
}
|
}
|
||||||
@@ -303,8 +462,6 @@ int main() {
|
|||||||
#endif
|
#endif
|
||||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||||
|
|
||||||
bool show_demo_window = true;
|
|
||||||
bool show_another_window = false;
|
|
||||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
defaults config;
|
defaults config;
|
||||||
@@ -317,6 +474,10 @@ int main() {
|
|||||||
spheredefaults sphereConf;
|
spheredefaults sphereConf;
|
||||||
stardefaults starConf;
|
stardefaults starConf;
|
||||||
|
|
||||||
|
// Initialize Noise Preview State
|
||||||
|
NoisePreviewState noiseState;
|
||||||
|
updateNoiseTexture(noiseState); // Initial generation
|
||||||
|
|
||||||
sphereConf.centerX = ghalf;
|
sphereConf.centerX = ghalf;
|
||||||
sphereConf.centerY = ghalf;
|
sphereConf.centerY = ghalf;
|
||||||
sphereConf.centerZ = ghalf;
|
sphereConf.centerZ = ghalf;
|
||||||
@@ -343,17 +504,15 @@ int main() {
|
|||||||
float camspeed = 50;
|
float camspeed = 50;
|
||||||
Camera cam(PointType(400, 400, 400), PointType(0,0,1), PointType(0,1,0), 80, camspeed);
|
Camera cam(PointType(400, 400, 400), PointType(0,0,1), PointType(0,1,0), 80, camspeed);
|
||||||
|
|
||||||
// Keyboard state tracking
|
|
||||||
std::map<int, bool> keyStates;
|
std::map<int, bool> keyStates;
|
||||||
bool mouseCaptured = false;
|
bool mouseCaptured = false;
|
||||||
double lastMouseX = 0, lastMouseY = 0;
|
double lastMouseX = 0, lastMouseY = 0;
|
||||||
float deltaTime = 0.016f;
|
float deltaTime = 0.016f;
|
||||||
|
|
||||||
// Initialize render frame times vector
|
|
||||||
renderFrameTimes.resize(FRAME_HISTORY_SIZE, 0.0);
|
renderFrameTimes.resize(FRAME_HISTORY_SIZE, 0.0);
|
||||||
|
|
||||||
lastStatsUpdate = std::chrono::steady_clock::now();
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
statsNeedUpdate = true;
|
statsNeedUpdate = true;
|
||||||
|
bool worldPreview = false;
|
||||||
|
|
||||||
if (grid.load("output/Treegrid.yggs")) {
|
if (grid.load("output/Treegrid.yggs")) {
|
||||||
gridInitialized = true;
|
gridInitialized = true;
|
||||||
@@ -423,7 +582,6 @@ int main() {
|
|||||||
camvZ = cam.direction[2];
|
camvZ = cam.direction[2];
|
||||||
camspeed = cam.movementSpeed;
|
camspeed = cam.movementSpeed;
|
||||||
|
|
||||||
// Start the Dear ImGui frame
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
@@ -438,16 +596,12 @@ int main() {
|
|||||||
sphereConf.centerY = pos[1];
|
sphereConf.centerY = pos[1];
|
||||||
sphereConf.centerZ = pos[2];
|
sphereConf.centerZ = pos[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
||||||
|
|
||||||
// Replaced traditional voxel sizing with Point Count logic
|
|
||||||
ImGui::DragInt("Point Count", &sphereConf.numPoints, 100, 100, 200000);
|
ImGui::DragInt("Point Count", &sphereConf.numPoints, 100, 100, 200000);
|
||||||
ImGui::DragFloat("Density (Overlap)", &sphereConf.voxelSize, 0.05f, 0.1f, 5.0f);
|
ImGui::DragFloat("Density (Overlap)", &sphereConf.voxelSize, 0.05f, 0.1f, 5.0f);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("Multiplies calculated point size. >1.0 ensures solid surface.");
|
ImGui::SetTooltip("Multiplies calculated point size. >1.0 ensures solid surface.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::ColorEdit3("Color", sphereConf.color);
|
ImGui::ColorEdit3("Color", sphereConf.color);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -494,7 +648,9 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ImGui::Begin("Preview");
|
ImGui::Begin("Planet Preview");
|
||||||
|
if (ImGui::Checkbox("update Preview", &worldPreview)) if (gridInitialized) livePreview(grid, config, cam);
|
||||||
|
|
||||||
if (gridInitialized && textureInitialized) {
|
if (gridInitialized && textureInitialized) {
|
||||||
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
||||||
} else if (gridInitialized) {
|
} else if (gridInitialized) {
|
||||||
@@ -503,8 +659,6 @@ int main() {
|
|||||||
ImGui::Text("No grid generated");
|
ImGui::Text("No grid generated");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::Text("Render Performance:");
|
ImGui::Text("Render Performance:");
|
||||||
if (renderFPS > 0) {
|
if (renderFPS > 0) {
|
||||||
// Color code based on FPS
|
// Color code based on FPS
|
||||||
@@ -525,23 +679,16 @@ int main() {
|
|||||||
|
|
||||||
// Show latest frame time
|
// Show latest frame time
|
||||||
ImGui::Text("Latest: %.1f ms", renderFrameTime * 1000.0);
|
ImGui::Text("Latest: %.1f ms", renderFrameTime * 1000.0);
|
||||||
} else {
|
|
||||||
ImGui::Text("No render data yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Performance: %.1f FPS (%.1f ms)", renderFPS, avgRenderFrameTime * 1000.0);
|
||||||
|
|
||||||
if (gridInitialized) {
|
if (gridInitialized) {
|
||||||
// Show time since last update
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
auto timeSinceUpdate = std::chrono::duration_cast<std::chrono::seconds>(now - lastStatsUpdate);
|
if ((now - lastStatsUpdate) > STATS_UPDATE_INTERVAL) updateStatsCache(grid);
|
||||||
|
|
||||||
if (!(timeSinceUpdate < STATS_UPDATE_INTERVAL)) {
|
|
||||||
updateStatsCache(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display cached stats
|
|
||||||
ImGui::TextUnformatted(cachedStats.c_str());
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,7 +850,100 @@ int main() {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gridInitialized) livePreview(grid, config, cam);
|
{
|
||||||
|
ImGui::Begin("2D Noise Lab");
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
const char* items[] = { "Perlin", "Value", "Fractal (Octave)", "Turbulence", "Ridged Multifractal", "Billow", "White", "Worley", "Voronoi", "Crystal", "Domain Warp", "Curl" };
|
||||||
|
int currentItem = static_cast<int>(noiseState.currentType);
|
||||||
|
if (ImGui::Combo("Method", ¤tItem, items, IM_ARRAYSIZE(items))) {
|
||||||
|
noiseState.currentType = static_cast<NoiseType>(currentItem);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (noiseState.currentType == NoiseType::CurlNoise) {
|
||||||
|
int currentsubitem = static_cast<int>(noiseState.currentSubType);
|
||||||
|
if (ImGui::Combo("Sub Method", ¤tsubitem, items, IM_ARRAYSIZE(items))) {
|
||||||
|
noiseState.currentSubType = static_cast<NoiseType>(currentsubitem);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changed |= ImGui::InputInt("Seed", &noiseState.seed);
|
||||||
|
if (ImGui::Button("Randomize Seed")) {
|
||||||
|
noiseState.seed = rand();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Base Parameters");
|
||||||
|
changed |= ImGui::SliderFloat("Scale (Freq)", &noiseState.scale, 0.001f, 1.f, "%.4f");
|
||||||
|
changed |= ImGui::DragFloat2("Offset", noiseState.offset, 1.0f);
|
||||||
|
|
||||||
|
// Conditional parameters based on noise type
|
||||||
|
bool usesOctaves = (noiseState.currentType == NoiseType::Fractal ||
|
||||||
|
noiseState.currentType == NoiseType::Turbulence ||
|
||||||
|
noiseState.currentType == NoiseType::Ridged ||
|
||||||
|
noiseState.currentType == NoiseType::Billow);
|
||||||
|
|
||||||
|
if (usesOctaves) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Fractal Parameters");
|
||||||
|
changed |= ImGui::SliderInt("Octaves", &noiseState.octaves, 1, 10);
|
||||||
|
|
||||||
|
if (noiseState.currentType == NoiseType::Fractal) {
|
||||||
|
changed |= ImGui::SliderFloat("Persistence", &noiseState.persistence, 0.0f, 1.0f);
|
||||||
|
changed |= ImGui::SliderFloat("Lacunarity", &noiseState.lacunarity, 1.0f, 4.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noiseState.currentType == NoiseType::Ridged) {
|
||||||
|
ImGui::Separator();
|
||||||
|
changed |= ImGui::SliderFloat("Ridge Offset", &noiseState.ridgeOffset, 0.0f, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noiseState.currentType == NoiseType::DomainWarp || noiseState.currentType == NoiseType::CurlNoise) {
|
||||||
|
ImGui::Separator();
|
||||||
|
changed |= ImGui::SliderFloat("Strength", &noiseState.strength, 0.1f, 4.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noiseState.currentType == NoiseType::CurlNoise) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Sub Parameters");
|
||||||
|
bool usesSubOctaves = (noiseState.currentSubType == NoiseType::Fractal ||
|
||||||
|
noiseState.currentSubType == NoiseType::Turbulence ||
|
||||||
|
noiseState.currentSubType == NoiseType::Ridged ||
|
||||||
|
noiseState.currentSubType == NoiseType::Billow);
|
||||||
|
|
||||||
|
if (usesSubOctaves) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Fractal Parameters");
|
||||||
|
changed |= ImGui::SliderInt("Octaves (sub)", &noiseState.octaves, 1, 10);
|
||||||
|
|
||||||
|
if (noiseState.currentSubType == NoiseType::Fractal) {
|
||||||
|
changed |= ImGui::SliderFloat("Persistence (sub)", &noiseState.persistence, 0.0f, 1.0f);
|
||||||
|
changed |= ImGui::SliderFloat("Lacunarity (sub)", &noiseState.lacunarity, 1.0f, 4.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noiseState.currentSubType == NoiseType::Ridged) {
|
||||||
|
ImGui::Separator();
|
||||||
|
changed |= ImGui::SliderFloat("Ridge Offset (sub)", &noiseState.ridgeOffset, 0.0f, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noiseState.currentSubType == NoiseType::DomainWarp || noiseState.currentSubType == NoiseType::CurlNoise) {
|
||||||
|
ImGui::Separator();
|
||||||
|
changed |= ImGui::SliderFloat("Strength (sub)", &noiseState.substrength, 0.1f, 4.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Preview (%dx%d)", noiseState.width, noiseState.height);
|
||||||
|
// Display the generated texture
|
||||||
|
|
||||||
|
ImGui::Image((void*)(intptr_t)noiseState.textureId, ImVec2((float)noiseState.width, (float)noiseState.height));
|
||||||
|
updateNoiseTexture(noiseState);
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
int display_w, display_h;
|
int display_w, display_h;
|
||||||
@@ -729,6 +969,10 @@ int main() {
|
|||||||
glDeleteTextures(1, &textu);
|
glDeleteTextures(1, &textu);
|
||||||
textu = 0;
|
textu = 0;
|
||||||
}
|
}
|
||||||
|
if (noiseState.textureId != 0) {
|
||||||
|
glDeleteTextures(1, &noiseState.textureId);
|
||||||
|
noiseState.textureId = 0;
|
||||||
|
}
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|
||||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <limits>
|
||||||
#include "../../eigen/Eigen/Core"
|
#include "../../eigen/Eigen/Core"
|
||||||
#include "../timing_decorator.hpp"
|
#include "../timing_decorator.hpp"
|
||||||
|
|
||||||
@@ -173,6 +174,30 @@ private:
|
|||||||
return (permutation[(z + permutation[(y + permutation[x & 255]) & 255]) & 255] / 255.0f) * 2.0f - 1.0f;
|
return (permutation[(z + permutation[(y + permutation[x & 255]) & 255]) & 255] / 255.0f) * 2.0f - 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Pseudo-random vector for Worley noise (2D)
|
||||||
|
Vector2f hashVector(const Vector2f& gridPoint) {
|
||||||
|
int x = (int)gridPoint.x() & 255;
|
||||||
|
int y = (int)gridPoint.y() & 255;
|
||||||
|
// Generate pseudo-random float [0,1] for x and y offsets
|
||||||
|
float hx = permutation[(x + permutation[y]) & 255] / 255.0f;
|
||||||
|
float hy = permutation[(y + permutation[(x + 1) & 255]) & 255] / 255.0f;
|
||||||
|
return Vector2f(hx, hy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pseudo-random vector for Worley noise (3D)
|
||||||
|
Vector3f hashVector(const Vector3f& gridPoint) {
|
||||||
|
int x = (int)gridPoint.x() & 255;
|
||||||
|
int y = (int)gridPoint.y() & 255;
|
||||||
|
int z = (int)gridPoint.z() & 255;
|
||||||
|
|
||||||
|
int h_xy = permutation[(x + permutation[y]) & 255];
|
||||||
|
float hx = permutation[(h_xy + z) & 255] / 255.0f;
|
||||||
|
float hy = permutation[(h_xy + permutation[(z + 1) & 255]) & 255] / 255.0f;
|
||||||
|
float hz = permutation[(permutation[(x+1)&255] + permutation[(y+1)&255] + z) & 255] / 255.0f;
|
||||||
|
|
||||||
|
return Vector3f(hx, hy, hz);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// @brief Default constructor with random seed
|
/// @brief Default constructor with random seed
|
||||||
/// @note Uses random_device for seed; different runs produce different noise
|
/// @note Uses random_device for seed; different runs produce different noise
|
||||||
@@ -192,7 +217,7 @@ public:
|
|||||||
/// @return Noise value in [-1,1] range
|
/// @return Noise value in [-1,1] range
|
||||||
/// @note Core 2D noise function; changes affect all 2D noise outputs
|
/// @note Core 2D noise function; changes affect all 2D noise outputs
|
||||||
float permute(const Vector2f& point) {
|
float permute(const Vector2f& point) {
|
||||||
TIME_FUNCTION;
|
// TIME_FUNCTION;
|
||||||
float x = point.x();
|
float x = point.x();
|
||||||
float y = point.y();
|
float y = point.y();
|
||||||
int X = (int)floor(x);
|
int X = (int)floor(x);
|
||||||
@@ -231,7 +256,7 @@ public:
|
|||||||
/// @param point 3D coordinate
|
/// @param point 3D coordinate
|
||||||
/// @return Noise value in [-1,1] range
|
/// @return Noise value in [-1,1] range
|
||||||
float permute(const Vector3f& point) {
|
float permute(const Vector3f& point) {
|
||||||
TIME_FUNCTION;
|
// TIME_FUNCTION;
|
||||||
float x = point.x();
|
float x = point.x();
|
||||||
float y = point.y();
|
float y = point.y();
|
||||||
float z = point.z();
|
float z = point.z();
|
||||||
@@ -373,7 +398,7 @@ public:
|
|||||||
/// @return Vector4f containing RGBA noise values
|
/// @return Vector4f containing RGBA noise values
|
||||||
/// @note Each channel uses different offset; changes affect color patterns
|
/// @note Each channel uses different offset; changes affect color patterns
|
||||||
Vector4f permuteColor(const Vector3f& point) {
|
Vector4f permuteColor(const Vector3f& point) {
|
||||||
TIME_FUNCTION;
|
// TIME_FUNCTION;
|
||||||
float noiseR = permute(point);
|
float noiseR = permute(point);
|
||||||
float noiseG = permute(Vector3f(point + Vector3f(100.0f, 100.0f, 100.0f)));
|
float noiseG = permute(Vector3f(point + Vector3f(100.0f, 100.0f, 100.0f)));
|
||||||
float noiseB = permute(Vector3f(point + Vector3f(200.0f, 200.0f, 200.0f)));
|
float noiseB = permute(Vector3f(point + Vector3f(200.0f, 200.0f, 200.0f)));
|
||||||
@@ -548,6 +573,205 @@ public:
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Pure White Noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Random value [-1, 1] based solely on integer coordinate hashing
|
||||||
|
float whiteNoise(const Vector2f& point) {
|
||||||
|
return hash((int)floor(point.x()), (int)floor(point.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pure White Noise 3D
|
||||||
|
float whiteNoise(const Vector3f& point) {
|
||||||
|
return hash((int)floor(point.x()), (int)floor(point.y()), (int)floor(point.z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Worley (Cellular) Noise 2D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Distance to the nearest feature point [0, 1+]
|
||||||
|
/// @note Used for stone, water caustics, biological cells
|
||||||
|
float worleyNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float minDist = 1.0f;
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
// Get random point inside the neighbor cell
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
|
||||||
|
// Vector from current pixel to that point
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
|
||||||
|
float dist = diff.norm();
|
||||||
|
if (dist < minDist) minDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minDist; // Usually clamped or inverted for visuals
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Worley Noise 3D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Distance to nearest feature point
|
||||||
|
float worleyNoise(const Vector3f& point) {
|
||||||
|
Vector3f p = Vector3f(floor(point.x()), floor(point.y()), floor(point.z()));
|
||||||
|
Vector3f f = Vector3f(point.x() - p.x(), point.y() - p.y(), point.z() - p.z());
|
||||||
|
|
||||||
|
float minDist = 1.0f;
|
||||||
|
|
||||||
|
for (int z = -1; z <= 1; z++) {
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector3f neighbor(x, y, z);
|
||||||
|
Vector3f pointInCell = hashVector(Vector3f(p + neighbor));
|
||||||
|
Vector3f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.norm();
|
||||||
|
if (dist < minDist) minDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Voronoi Noise 2D (Cell ID)
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Random hash value [-1, 1] unique to the closest cell
|
||||||
|
float voronoiNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float minDist = 100.0f;
|
||||||
|
Vector2f cellID = p;
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.squaredNorm(); // Faster than norm
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
cellID = p + neighbor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash((int)cellID.x(), (int)cellID.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief "Crystals" Noise (Variant of Worley)
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return F2 - F1 (Distance to 2nd closest - Distance to closest)
|
||||||
|
/// @note Creates cell-like borders, cracks, or crystal facets
|
||||||
|
float crystalNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float d1 = 10.0f; // Closest
|
||||||
|
float d2 = 10.0f; // 2nd Closest
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.norm();
|
||||||
|
|
||||||
|
if (dist < d1) {
|
||||||
|
d2 = d1;
|
||||||
|
d1 = dist;
|
||||||
|
} else if (dist < d2) {
|
||||||
|
d2 = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d2 - d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Domain Warping
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param strength Magnitude of the warp
|
||||||
|
/// @return Warped Perlin noise value
|
||||||
|
/// @note Calculates noise(p + noise(p)) for marble/fluid effects
|
||||||
|
float domainWarp(const Vector2f& point, float strength = 1.0f) {
|
||||||
|
Vector2f q(
|
||||||
|
permute(point),
|
||||||
|
permute(Vector2f(point + Vector2f(5.2f, 1.3f)))
|
||||||
|
);
|
||||||
|
return permute(Vector2f(point + q * strength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief 3D Domain Warping
|
||||||
|
float domainWarp(const Vector3f& point, float strength = 1.0f) {
|
||||||
|
Vector3f q(
|
||||||
|
permute(point),
|
||||||
|
permute(Vector3f(point + Vector3f(5.2f, 1.3f, 2.8f))),
|
||||||
|
permute(Vector3f(point + Vector3f(1.1f, 8.4f, 5.5f)))
|
||||||
|
);
|
||||||
|
return permute(Vector3f(point + q * strength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Curl Noise 2D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Divergence-free vector field (useful for particle simulation)
|
||||||
|
/// @note Calculated via finite difference curl of a potential field
|
||||||
|
Vector2f curlNoise(const Vector2f& point) {
|
||||||
|
float e = 0.01f; // Epsilon
|
||||||
|
float n1 = permute(Vector2f(point + Vector2f(0, e)));
|
||||||
|
float n2 = permute(Vector2f(point + Vector2f(0, -e)));
|
||||||
|
float n3 = permute(Vector2f(point + Vector2f(e, 0)));
|
||||||
|
float n4 = permute(Vector2f(point + Vector2f(-e, 0)));
|
||||||
|
|
||||||
|
float dx = (n3 - n4) / (2.0f * e);
|
||||||
|
float dy = (n1 - n2) / (2.0f * e);
|
||||||
|
|
||||||
|
// Curl of scalar field in 2D is (d/dy, -d/dx)
|
||||||
|
return Vector2f(dy, -dx).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Curl Noise 3D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Divergence-free vector field
|
||||||
|
/// @note Uses 3 offsets of Perlin noise as Vector Potential
|
||||||
|
Vector3f curlNoise(const Vector3f& point) {
|
||||||
|
float e = 0.01f;
|
||||||
|
|
||||||
|
Vector3f dx(e, 0.0f, 0.0f);
|
||||||
|
Vector3f dy(0.0f, e, 0.0f);
|
||||||
|
Vector3f dz(0.0f, 0.0f, e);
|
||||||
|
|
||||||
|
// We need a vector potential (3 uncorrelated noise values)
|
||||||
|
// We reuse permuteColor's logic but keep it local to avoid overhead
|
||||||
|
auto potential = [&](const Vector3f& p) -> Vector3f {
|
||||||
|
return Vector3f(
|
||||||
|
permute(p),
|
||||||
|
permute(Vector3f(p + Vector3f(123.4f, 129.1f, 827.0f))),
|
||||||
|
permute(Vector3f(p + Vector3f(492.5f, 991.2f, 351.4f)))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector3f p_dx_p = potential(point + dx);
|
||||||
|
Vector3f p_dx_m = potential(point - dx);
|
||||||
|
Vector3f p_dy_p = potential(point + dy);
|
||||||
|
Vector3f p_dy_m = potential(point - dy);
|
||||||
|
Vector3f p_dz_p = potential(point + dz);
|
||||||
|
Vector3f p_dz_m = potential(point - dz);
|
||||||
|
|
||||||
|
// Finite difference
|
||||||
|
float dFz_dy = (p_dy_p.z() - p_dy_m.z()) / (2.0f * e);
|
||||||
|
float dFy_dz = (p_dz_p.y() - p_dz_m.y()) / (2.0f * e);
|
||||||
|
float dFx_dz = (p_dz_p.x() - p_dz_m.x()) / (2.0f * e);
|
||||||
|
float dFz_dx = (p_dx_p.z() - p_dx_m.z()) / (2.0f * e);
|
||||||
|
float dFy_dx = (p_dx_p.y() - p_dx_m.y()) / (2.0f * e);
|
||||||
|
float dFx_dy = (p_dy_p.x() - p_dy_m.x()) / (2.0f * e);
|
||||||
|
|
||||||
|
return Vector3f(
|
||||||
|
dFz_dy - dFy_dz,
|
||||||
|
dFx_dz - dFz_dx,
|
||||||
|
dFy_dx - dFx_dy
|
||||||
|
).normalized();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user