added click mouse to cycle 1d graph to 2d render and then 2d smoothed render
This commit is contained in:
216
src/main.cpp
216
src/main.cpp
@@ -6,10 +6,10 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "ReNoise/perlin.hpp"
|
||||
#include "ReUnirand/unirand.hpp"
|
||||
|
||||
#include "ReNoise/perlin.hpp" // Contains generatePerlinNoise1D, smoothNoise1D, generatePerlinNoise2D, smoothNoise2D
|
||||
#include "ReUnirand/unirand.hpp"
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
#define WIDTH 800
|
||||
@@ -18,24 +18,27 @@ using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
/**
|
||||
* @brief Maps a noise value in [-1, 1] to a y-coordinate on the window.
|
||||
* +1 is mapped to the top, -1 to the bottom.
|
||||
* +1 is at the top, -1 at the bottom.
|
||||
*/
|
||||
float mapNoiseToY(float noiseValue) {
|
||||
return HEIGHT - ((noiseValue + 1.0f) / 2.0f * HEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A window class that displays the Perlin noise graphs.
|
||||
* @brief A window class that displays Perlin noise in three modes.
|
||||
*
|
||||
* This class generates a raw Perlin noise array and a smoothed version.
|
||||
* It then plots each sample as a dot and joins consecutive dots with a line,
|
||||
* so that the resulting graph appears as connected lines.
|
||||
* Raw noise is drawn in black; smoothed noise in gray.
|
||||
* Mode 0: 1D graph – raw noise (red) and smoothed noise (blue) drawn as connected dots.
|
||||
* Mode 1: 2D raw noise image – the noise field is rendered as a grayscale image.
|
||||
* Mode 2: 2D smoothed noise image – the smoothed noise field is rendered as a grayscale image.
|
||||
*
|
||||
* Pressing the space bar or left mouse button cycles through the modes.
|
||||
*/
|
||||
class PerlinNoiseWindow : public ReWindow::OpenGLWindow {
|
||||
public:
|
||||
PerlinNoiseWindow(const std::string &title, int width, int height)
|
||||
: ReWindow::OpenGLWindow(title, width, height, 2, 1)
|
||||
: ReWindow::OpenGLWindow(title, width, height, 2, 1),
|
||||
renderMode(0), toggleLocked(false),
|
||||
noise2DWidth(256), noise2DHeight(256)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,76 +49,72 @@ public:
|
||||
// Create a render target with a white background.
|
||||
render_target = new RenderTarget({ WIDTH, HEIGHT }, Colors::White);
|
||||
|
||||
// Generate the noise arrays.
|
||||
const int noiseResolution = 128;
|
||||
ReUnirand::MarsagliaUniRng rng;
|
||||
try {
|
||||
rng.rinit(170);
|
||||
} catch (const std::exception &e) {
|
||||
ReWindow::Logger::Error("Error initializing RNG: " + std::string(e.what()));
|
||||
exit(0);
|
||||
// Generate 1D noise arrays.
|
||||
const int noiseResolution = 256;
|
||||
{
|
||||
ReUnirand::MarsagliaUniRng rng1;
|
||||
try {
|
||||
rng1.rinit(170);
|
||||
} catch (const std::exception &e) {
|
||||
ReWindow::Logger::Error("Error initializing RNG (1D): " + std::string(e.what()));
|
||||
exit(0);
|
||||
}
|
||||
rawNoise = ReNoise::generatePerlinNoise1D(noiseResolution, rng1);
|
||||
smoothedNoise1D = ReNoise::smoothNoise1D(rawNoise, 3);
|
||||
// Precompute drawing points for 1D noise.
|
||||
rawPoints.clear();
|
||||
smoothPoints.clear();
|
||||
float xSpacing = static_cast<float>(WIDTH) / (noiseResolution - 1);
|
||||
for (int i = 0; i < noiseResolution; ++i) {
|
||||
float x = i * xSpacing;
|
||||
float yRaw = mapNoiseToY(rawNoise[i]);
|
||||
float ySmooth = mapNoiseToY(smoothedNoise1D[i]);
|
||||
rawPoints.push_back(Vector2(x, yRaw));
|
||||
smoothPoints.push_back(Vector2(x, ySmooth));
|
||||
}
|
||||
}
|
||||
rawNoise = ReNoise::generatePerlinNoise1D(noiseResolution, rng);
|
||||
smoothedNoise = ReNoise::smoothNoise(rawNoise, 3);
|
||||
|
||||
// Precompute drawing points, evenly spaced along the x-axis.
|
||||
rawPoints.clear();
|
||||
smoothPoints.clear();
|
||||
float xSpacing = static_cast<float>(WIDTH) / (noiseResolution - 1);
|
||||
for (int i = 0; i < noiseResolution; ++i) {
|
||||
float x = i * xSpacing;
|
||||
float yRaw = mapNoiseToY(rawNoise[i]);
|
||||
float ySmooth = mapNoiseToY(smoothedNoise[i]);
|
||||
rawPoints.push_back(Vector2(x, yRaw));
|
||||
smoothPoints.push_back(Vector2(x, ySmooth));
|
||||
// Generate 2D noise arrays.
|
||||
{
|
||||
ReUnirand::MarsagliaUniRng rng2;
|
||||
try {
|
||||
rng2.rinit(170);
|
||||
} catch (const std::exception &e) {
|
||||
ReWindow::Logger::Error("Error initializing RNG (2D): " + std::string(e.what()));
|
||||
exit(0);
|
||||
}
|
||||
noise2D = ReNoise::generatePerlinNoise2D(noise2DWidth, noise2DHeight, rng2);
|
||||
noise2D_smoothed = ReNoise::smoothNoise2D(noise2D, noise2DWidth, noise2DHeight, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void OnRefresh(float elapsed) override {
|
||||
|
||||
handleKeyboardInput();
|
||||
|
||||
handleInput();
|
||||
JGL::Update({ WIDTH, HEIGHT });
|
||||
|
||||
// Begin drawing to the render target.
|
||||
J2D::Begin(render_target, true);
|
||||
// (Assuming the "true" parameter clears the target as in your ReWalker code.)
|
||||
|
||||
// Draw raw Perlin noise graph:
|
||||
// First, join the dots with line segments.
|
||||
for (size_t i = 0; i < rawPoints.size() - 1; ++i) {
|
||||
J2D::DrawLine(Colors::Red, rawPoints[i], rawPoints[i + 1], 2.0f);
|
||||
}
|
||||
// Then draw the individual dots.
|
||||
for (const auto &pt : rawPoints) {
|
||||
J2D::DrawPoint(Colors::Red, pt, DOT_SIZE);
|
||||
}
|
||||
|
||||
// Draw smoothed noise graph:
|
||||
// Join the dots with line segments.
|
||||
for (size_t i = 0; i < smoothPoints.size() - 1; ++i) {
|
||||
J2D::DrawLine(Colors::Blue, smoothPoints[i], smoothPoints[i + 1], 2.0f);
|
||||
}
|
||||
// Then draw the dots.
|
||||
for (const auto &pt : smoothPoints) {
|
||||
J2D::DrawPoint(Colors::Blue, pt, DOT_SIZE);
|
||||
// Render based on the current mode.
|
||||
switch (renderMode) {
|
||||
case 0:
|
||||
render1D();
|
||||
break;
|
||||
case 1:
|
||||
render2DRaw();
|
||||
break;
|
||||
case 2:
|
||||
render2DSmoothed();
|
||||
break;
|
||||
default:
|
||||
render1D();
|
||||
break;
|
||||
}
|
||||
J2D::End();
|
||||
|
||||
// Render the target to the window.
|
||||
J2D::Begin(nullptr, true);
|
||||
J2D::DrawRenderTarget(render_target, { 0, 0 });
|
||||
J2D::End();
|
||||
|
||||
SwapBuffers();
|
||||
PollEvents(); // Process window events.
|
||||
}
|
||||
|
||||
void handleKeyboardInput() {
|
||||
if (IsKeyDown(Keys::Escape)) {
|
||||
Close();
|
||||
}
|
||||
|
||||
PollEvents();
|
||||
}
|
||||
|
||||
~PerlinNoiseWindow() override {
|
||||
@@ -124,14 +123,99 @@ public:
|
||||
|
||||
private:
|
||||
RenderTarget* render_target = nullptr;
|
||||
// 1D noise data:
|
||||
std::vector<float> rawNoise;
|
||||
std::vector<float> smoothedNoise;
|
||||
std::vector<float> smoothedNoise1D;
|
||||
std::vector<Vector2> rawPoints;
|
||||
std::vector<Vector2> smoothPoints;
|
||||
// 2D noise data:
|
||||
std::vector<float> noise2D;
|
||||
std::vector<float> noise2D_smoothed;
|
||||
int noise2DWidth, noise2DHeight;
|
||||
|
||||
// Render mode: 0 = 1D graph, 1 = 2D raw, 2 = 2D smoothed.
|
||||
int renderMode;
|
||||
bool toggleLocked;
|
||||
|
||||
/**
|
||||
* @brief Renders the 1D noise graphs as connected dots.
|
||||
*/
|
||||
void render1D() {
|
||||
// Draw raw 1D noise in red.
|
||||
for (size_t i = 0; i < rawPoints.size() - 1; ++i) {
|
||||
J2D::DrawLine(Colors::Red, rawPoints[i], rawPoints[i + 1], 2.0f);
|
||||
}
|
||||
for (const auto &pt : rawPoints) {
|
||||
J2D::DrawPoint(Colors::Red, pt, DOT_SIZE);
|
||||
}
|
||||
// Draw smoothed 1D noise in blue.
|
||||
for (size_t i = 0; i < smoothPoints.size() - 1; ++i) {
|
||||
J2D::DrawLine(Colors::Blue, smoothPoints[i], smoothPoints[i + 1], 2.0f);
|
||||
}
|
||||
for (const auto &pt : smoothPoints) {
|
||||
J2D::DrawPoint(Colors::Blue, pt, DOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Renders the 2D raw noise field as a grayscale image.
|
||||
*/
|
||||
void render2DRaw() {
|
||||
float cellWidth = static_cast<float>(WIDTH) / noise2DWidth;
|
||||
float cellHeight = static_cast<float>(HEIGHT) / noise2DHeight;
|
||||
for (int j = 0; j < noise2DHeight; ++j) {
|
||||
for (int i = 0; i < noise2DWidth; ++i) {
|
||||
float value = noise2D[j * noise2DWidth + i];
|
||||
unsigned char gray = static_cast<unsigned char>(((value + 1.0f) / 2.0f) * 255);
|
||||
Color4 cellColor(gray, gray, gray, 255);
|
||||
Vector2 pos(i * cellWidth, j * cellHeight);
|
||||
Vector2 size(cellWidth, cellHeight);
|
||||
J2D::FillRect(cellColor, pos, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Renders the 2D smoothed noise field as a grayscale image.
|
||||
*/
|
||||
void render2DSmoothed() {
|
||||
float cellWidth = static_cast<float>(WIDTH) / noise2DWidth;
|
||||
float cellHeight = static_cast<float>(HEIGHT) / noise2DHeight;
|
||||
for (int j = 0; j < noise2DHeight; ++j) {
|
||||
for (int i = 0; i < noise2DWidth; ++i) {
|
||||
float value = noise2D_smoothed[j * noise2DWidth + i];
|
||||
unsigned char gray = static_cast<unsigned char>(((value + 1.0f) / 2.0f) * 255);
|
||||
Color4 cellColor(gray, gray, gray, 255);
|
||||
Vector2 pos(i * cellWidth, j * cellHeight);
|
||||
Vector2 size(cellWidth, cellHeight);
|
||||
J2D::FillRect(cellColor, pos, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles keyboard and mouse input to cycle through render modes.
|
||||
*
|
||||
* Pressing the space bar or left mouse button cycles the renderMode value.
|
||||
* A simple lock prevents repeated toggling while the key/button is held down.
|
||||
*/
|
||||
void handleInput() {
|
||||
if (IsKeyDown(Keys::Space) || IsMouseButtonDown(MouseButtons::Left)) {
|
||||
if (!toggleLocked) {
|
||||
renderMode = (renderMode + 1) % 3;
|
||||
toggleLocked = true;
|
||||
}
|
||||
} else {
|
||||
toggleLocked = false;
|
||||
}
|
||||
if (IsKeyDown(Keys::Escape)) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
auto window = std::make_unique<PerlinNoiseWindow>("Perlin Noise Graph", WIDTH, HEIGHT);
|
||||
auto window = std::make_unique<PerlinNoiseWindow>("Perlin Noise Render", WIDTH, HEIGHT);
|
||||
if (!window->Open())
|
||||
return -1;
|
||||
|
||||
|
Reference in New Issue
Block a user