From 0cb71a479916fc1ea3801729036f964bb25cef1b Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Fri, 16 Jan 2026 14:45:55 -0500 Subject: [PATCH] added sphere and noise documentation --- tests/g3test2.cpp | 188 ++++++++++++++++++++++++++++++++++++++--- util/noise/pnoise2.hpp | 111 +++++++++++++++++++++++- 2 files changed, 285 insertions(+), 14 deletions(-) diff --git a/tests/g3test2.cpp b/tests/g3test2.cpp index 4519047..c31c0d1 100644 --- a/tests/g3test2.cpp +++ b/tests/g3test2.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../util/grid/grid3.hpp" #include "../util/grid/g3_serialization.hpp" @@ -42,6 +43,23 @@ std::atomic recordingFramesRemaining{0}; std::vector recordedFrames; std::mutex recordingMutex; +// Sphere generation parameters +struct SphereConfig { + float centerX = 256.0f; + float centerY = 256.0f; + float centerZ = 32.0f; + float radius = 30.0f; + uint8_t r = 0; + uint8_t g = 255; + uint8_t b = 0; + uint8_t a = 255; + bool fillInside = true; + bool outlineOnly = false; + float outlineThickness = 1.0f; +}; + +SphereConfig sphereConfig; + struct Shared { std::mutex mutex; VoxelGrid grid; @@ -74,9 +92,7 @@ void setup(defaults config, VoxelGrid& grid) { uint8_t g = config.noise.permute(Vec3f(static_cast(x) * gValw, static_cast(y) * gValh, static_cast(z) * gVald)) * 255; uint8_t b = config.noise.permute(Vec3f(static_cast(x) * bValw, static_cast(y) * bValh, static_cast(z) * bVald)) * 255; uint8_t a = config.noise.permute(Vec3f(static_cast(x) * aValw, static_cast(y) * aValh, static_cast(z) * aVald)) * 255; - //Vec4ui8 noisecolor = config.noise.permuteColor(Vec3f( static_cast(x) / 64, static_cast(y) / 64, static_cast(z) / 64)); if (a > threshold) { - //std::cout << "setting a position" << std::endl; grid.set(Vec3i(x, y, z), true, Vec3ui8(r,g,b)); } } @@ -87,6 +103,67 @@ void setup(defaults config, VoxelGrid& grid) { grid.printStats(); } +void createGreenSphere(defaults config, VoxelGrid& grid) { + grid.resize(config.gridWidth, config.gridHeight, config.gridDepth); + std::cout << "Creating green sphere of size " << config.gridWidth << "x" << config.gridHeight << "x" << config.gridDepth << std::endl; + + float radiusSq = sphereConfig.radius * sphereConfig.radius; + float outlineInnerRadiusSq = (sphereConfig.radius - sphereConfig.outlineThickness) * + (sphereConfig.radius - sphereConfig.outlineThickness); + float outlineOuterRadiusSq = (sphereConfig.radius + sphereConfig.outlineThickness) * + (sphereConfig.radius + sphereConfig.outlineThickness); + + int progressStep = std::max(1, config.gridDepth / 10); + + for (int z = 0; z < config.gridDepth; ++z) { + if (z % progressStep == 0) { + std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl; + } + + for (int y = 0; y < config.gridHeight; ++y) { + for (int x = 0; x < config.gridWidth; ++x) { + // Calculate distance from sphere center + float dx = x - sphereConfig.centerX; + float dy = y - sphereConfig.centerY; + float dz = z - sphereConfig.centerZ; + float distSq = dx*dx + dy*dy + dz*dz; + + bool shouldSet = false; + + if (sphereConfig.outlineOnly) { + // Only create outline (shell) + if (distSq >= outlineInnerRadiusSq && distSq <= outlineOuterRadiusSq) { + shouldSet = true; + } + } else if (sphereConfig.fillInside) { + // Fill entire sphere + if (distSq <= radiusSq) { + shouldSet = true; + } + } else { + // Hollow sphere (just the surface) + if (distSq <= radiusSq && distSq >= (radiusSq - sphereConfig.radius * 0.5f)) { + shouldSet = true; + } + } + + if (shouldSet) { + grid.set(Vec3i(x, y, z), true, + Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b)); + } + } + } + } + + std::cout << "Green sphere generation complete!" << std::endl; + std::cout << "Sphere center: (" << sphereConfig.centerX << ", " + << sphereConfig.centerY << ", " << sphereConfig.centerZ << ")" << std::endl; + std::cout << "Sphere radius: " << sphereConfig.radius << std::endl; + + grid.serializeToFile("output/sphere_grid.ygg3"); + grid.printStats(); +} + void livePreview(VoxelGrid& grid, defaults& config, const Camera& cam) { std::lock_guard lock(PreviewMutex); updatePreview = true; @@ -148,10 +225,7 @@ void stopAndSaveAVI(defaults& config, const std::string& filename) { now.time_since_epoch()).count(); std::string finalFilename = "output/recording_" + std::to_string(timestamp) + ".avi"; - std::cout << "Saving AVI with " << recordedFrames.size() << " frames..." << std::endl; - - // Save using the new streaming method bool success = AVIWriter::saveAVIFromCompressedFrames(finalFilename, recordedFrames, config.outWidth, config.outHeight, config.fps); if (success) { @@ -167,7 +241,7 @@ void stopAndSaveAVI(defaults& config, const std::string& filename) { recordingFramesRemaining = 0; } -void saveSlices(defaults& config, VoxelGrid& grid) { +void saveSlices(const defaults& config, VoxelGrid& grid) { std::vector frames = grid.genSlices(frame::colormap::RGB); for (int i = 0; i < frames.size(); i++) { std::string filename = "output/slices/" + std::to_string(i) + ".bmp"; @@ -281,6 +355,13 @@ int main() { float autoRotationAngle = 0.0f; // Accumulated rotation angle Vec3f initialUpDir = Vec3f(0, 1, 0); // Initial up direction + + // After your existing initialization code, add sphere parameter initialization: + sphereConfig.centerX = config.gridWidth / 2.0f; + sphereConfig.centerY = config.gridHeight / 2.0f; + sphereConfig.centerZ = config.gridDepth / 2.0f; + sphereConfig.radius = std::min(config.gridWidth, std::min(config.gridHeight, config.gridDepth)) / 4.0f; + // Variables for framerate limiting const double targetFrameTime = 1.0 / config.fps; // 30 FPS double lastFrameTime = glfwGetTime(); @@ -348,14 +429,28 @@ int main() { camX = config.gridWidth / 2.0f; camY = config.gridHeight / 2.0f; camZ = config.gridDepth / 2.0f; - //camYaw = 0.0f; - //camPitch = 0.0f; // Update camera position cam.posfor.origin = Vec3f(camX, camY, camZ); cam.posfor.direction = Vec3f(camvX, camvY, camvZ); - // cam.rotateYaw(camYaw); - // cam.rotatePitch(camPitch); + + savePreview(grid, config, cam); + cameraMoved = true; + } + + // Add the new green sphere button + if (ImGui::Button("Create Green Sphere")) { + createGreenSphere(config, grid); + gridInitialized = true; + + // Reset camera to center of grid + camX = config.gridWidth / 2.0f; + camY = config.gridHeight / 2.0f; + camZ = config.gridDepth / 2.0f; + + // Update camera position + cam.posfor.origin = Vec3f(camX, camY, camZ); + cam.posfor.direction = Vec3f(camvX, camvY, camvZ); savePreview(grid, config, cam); cameraMoved = true; @@ -371,7 +466,7 @@ int main() { if (!isRecordingAVI) { ImGui::InputInt("Frames to Record", &recordingDurationFrames, 30, 300); - recordingDurationFrames = std::max(30, recordingDurationFrames); // Minimum 1 second at 30fps + recordingDurationFrames = std::max(30, recordingDurationFrames); if (ImGui::Button("Start AVI Recording")) { startAVIRecording(recordingDurationFrames); @@ -388,6 +483,7 @@ int main() { } } + // Display camera controls if (gridInitialized) { ImGui::Separator(); @@ -452,7 +548,7 @@ int main() { cam.posfor.origin.z); // ImGui::Text("Yaw: %.2f°, Pitch: %.2f°", camYaw, camPitch); } - + ImGui::End(); } @@ -607,6 +703,74 @@ int main() { ImGui::End(); } + // Add a new window for sphere configuration + { + ImGui::Begin("Sphere Configuration"); + + if (ImGui::CollapsingHeader("Sphere Properties", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Text("Sphere Center:"); + ImGui::SliderFloat("Center X", &sphereConfig.centerX, 0.0f, static_cast(config.gridWidth)); + ImGui::SliderFloat("Center Y", &sphereConfig.centerY, 0.0f, static_cast(config.gridHeight)); + ImGui::SliderFloat("Center Z", &sphereConfig.centerZ, 0.0f, static_cast(config.gridDepth)); + + ImGui::Separator(); + ImGui::Text("Sphere Size:"); + float maxRadius = std::min(std::min(config.gridWidth, config.gridHeight), config.gridDepth) / 2.0f; + ImGui::SliderFloat("Radius", &sphereConfig.radius, 5.0f, maxRadius); + + ImGui::Separator(); + ImGui::Text("Sphere Style:"); + ImGui::Checkbox("Fill Inside", &sphereConfig.fillInside); + if (!sphereConfig.fillInside) { + ImGui::Checkbox("Outline Only", &sphereConfig.outlineOnly); + if (sphereConfig.outlineOnly) { + ImGui::SliderFloat("Outline Thickness", &sphereConfig.outlineThickness, 0.5f, 10.0f); + } + } + + ImGui::Separator(); + ImGui::Text("Sphere Color:"); + float color[3] = {sphereConfig.r, sphereConfig.g, sphereConfig.b}; + if (ImGui::ColorEdit3("Color", color)) { + sphereConfig.r = static_cast(color[0] * 255); + sphereConfig.g = static_cast(color[1] * 255); + sphereConfig.b = static_cast(color[2] * 255); + } + + // Preview color + ImGui::SameLine(); + ImVec4 previewColor = ImVec4(sphereConfig.r/255.0f, sphereConfig.g/255.0f, sphereConfig.b/255.0f, 1.0f); + ImGui::ColorButton("##preview", previewColor, ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20)); + + // Quick color presets + if (ImGui::Button("Green")) { + sphereConfig.r = 0; + sphereConfig.g = 255; + sphereConfig.b = 0; + } + ImGui::SameLine(); + if (ImGui::Button("Blue")) { + sphereConfig.r = 0; + sphereConfig.g = 0; + sphereConfig.b = 255; + } + ImGui::SameLine(); + if (ImGui::Button("Red")) { + sphereConfig.r = 255; + sphereConfig.g = 0; + sphereConfig.b = 0; + } + ImGui::SameLine(); + if (ImGui::Button("Random")) { + sphereConfig.r = rand() % 256; + sphereConfig.g = rand() % 256; + sphereConfig.b = rand() % 256; + } + } + + ImGui::End(); + } + // Handle AVI recording start request if (recordingRequested) { isRecordingAVI = true; diff --git a/util/noise/pnoise2.hpp b/util/noise/pnoise2.hpp index c12c6e2..27f1fe9 100644 --- a/util/noise/pnoise2.hpp +++ b/util/noise/pnoise2.hpp @@ -16,14 +16,28 @@ private: std::vector permutation; std::default_random_engine rng; + /// @brief Linear interpolation between two values + /// @param t Interpolation factor [0,1] + /// @param a1 First value + /// @param a2 Second value + /// @return Interpolated value between a1 and a2 + /// @note Changing interpolation method affects noise smoothness float lerp(float t, float a1, float a2) { return a1 + t * (a2 - a1); } + /// @brief Fade function for smooth interpolation + /// @param t Input parameter + /// @return Smoothed t value using 6t^5 - 15t^4 + 10t^3 + /// @note Critical for gradient continuity; changes affect noise smoothness static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } + /// @brief Get constant gradient vector for 2D Perlin noise + /// @param v Hash value (0-3) + /// @return One of four 2D gradient vectors + /// @note Changing vectors affects noise pattern orientation Vec2f GetConstantVector(int v) { int h = v & 3; if (h == 0) return Vec2f(1,1); @@ -32,6 +46,10 @@ private: else return Vec2f(1,-1); } + /// @brief Get constant gradient vector for 3D Perlin noise + /// @param v Hash value (0-7) + /// @return One of eight 3D gradient vectors + /// @note Vector selection affects 3D noise patterns Vec3ui8 GetConstantVector3(int v) { int h = v & 7; if (h == 0) return Vec3ui8(1,1,1); @@ -44,6 +62,13 @@ private: else return Vec3ui8(1,-1, -1); } + /// @brief Gradient function for 2D/3D Perlin noise + /// @param hash Hash value for gradient selection + /// @param x X coordinate + /// @param y Y coordinate + /// @param z Z coordinate (default 0 for 2D) + /// @return Dot product of gradient vector and distance vector + /// @note Core of Perlin noise; changes affect basic noise character static double grad(int hash, double x, double y, double z = 0.0) { int h = hash & 15; double u = h < 8 ? x : y; @@ -51,6 +76,8 @@ private: return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } + /// @brief Initialize permutation table with shuffled values + /// @note Called on construction; changing seed or shuffle affects all noise patterns void initializePermutation() { permutation.clear(); std::vector permutationt; @@ -63,38 +90,82 @@ private: permutation.insert(permutation.end(), permutationt.begin(), permutationt.end()); } + /// @brief Normalize noise value from [-1,1] to [0,1] + /// @param point Input coordinate + /// @return Normalized noise value in [0,1] range + /// @note Useful for texture generation; changes affect output range float normalizedNoise(const Vec2& point) { return (permute(point) + 1.0f) * 0.5f; } + /// @brief Map value from one range to another + /// @param value Input value + /// @param inMin Original range minimum + /// @param inMax Original range maximum + /// @param outMin Target range minimum + /// @param outMax Target range maximum + /// @return Value mapped to new range + /// @note Useful for post-processing; changes affect output scaling float mapRange(float value, float inMin, float inMax, float outMin, float outMax) { return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } + /// @brief Blend two noise values + /// @param noise1 First noise value + /// @param noise2 Second noise value + /// @param blendFactor Blending factor [0,1] + /// @return Blended noise value + /// @note Changes affect multi-layer noise combinations float blendNoises(float noise1, float noise2, float blendFactor) { return lerp(noise1, noise2, blendFactor); } + /// @brief Add two noise values with clamping + /// @param noise1 First noise value + /// @param noise2 Second noise value + /// @return Sum clamped to [-1,1] + /// @note Clamping prevents overflow; changes affect combined noise magnitude float addNoises(float noise1, float noise2) { return std::clamp(noise1 + noise2, -1.0f, 1.0f); } + /// @brief Multiply two noise values + /// @param noise1 First noise value + /// @param noise2 Second noise value + /// @return Product of noise values + /// @note Creates modulation effects; changes affect combined noise character float multiplyNoises(float noise1, float noise2) { return noise1 * noise2; } + /// @brief Hash function for 2D coordinates + /// @param x X coordinate integer + /// @param y Y coordinate integer + /// @return Hash value in [-1,1] range + /// @note Core of value noise; changes affect random distribution float hash(int x, int y) { return (permutation[(x + permutation[y & 255]) & 255] / 255.0f) * 2.0f - 1.0f; } + public: + /// @brief Default constructor with random seed + /// @note Uses random_device for seed; different runs produce different noise PNoise2() : rng(std::random_device{}()) { initializePermutation(); } + /// @brief Constructor with specified seed + /// @param seed Random seed value + /// @note Same seed produces identical noise patterns across runs PNoise2(unsigned int seed) : rng(seed) { initializePermutation(); } + /// @brief Generate 2D Perlin noise at given point + /// @tparam T Coordinate type (float, double, etc.) + /// @param point 2D coordinate + /// @return Noise value in [-1,1] range + /// @note Core 2D noise function; changes affect all 2D noise outputs template float permute(Vec2 point) { TIME_FUNCTION; @@ -132,6 +203,11 @@ public: return retval; } + /// @brief Generate 3D Perlin noise at given point + /// @tparam T Coordinate type (float, double, etc.) + /// @param point 3D coordinate + /// @return Noise value in [-1,1] range + /// @note Core 3D noise function; changes affect all 3D noise outputs template float permute(Vec3 point) { TIME_FUNCTION; @@ -190,6 +266,10 @@ public: return retval; } + /// @brief Generate 2D value noise (simpler alternative to Perlin) + /// @param point 2D coordinate + /// @return Noise value in [-1,1] range + /// @note Different character than Perlin; changes affect value-based textures float valueNoise(const Vec2& point) { int xi = (int)std::floor(point.x); int yi = (int)std::floor(point.y); @@ -218,6 +298,10 @@ public: return lerp(nx0, nx1, sy); } + /// @brief Generate RGBA color from 3D noise with offset channels + /// @param point 3D coordinate + /// @return Vec4ui8 containing RGBA noise values + /// @note Each channel uses different offset; changes affect color patterns Vec4ui8 permuteColor(const Vec3& point) { TIME_FUNCTION; float noiseR = permute(point); @@ -240,6 +324,14 @@ public: return Vec4ui8(r, g, b, a); } + /// @brief Generate fractal (octave) noise for natural-looking patterns + /// @tparam T Coordinate type + /// @param point Input coordinate + /// @param octaves Number of noise layers + /// @param persistence Amplitude multiplier per octave + /// @param lacunarity Frequency multiplier per octave + /// @return Combined noise value + /// @note Parameters control noise character: octaves=detail, persistence=roughness, lacunarity=frequency change template float fractalNoise(const Vec2& point, int octaves, float persistence, float lacunarity) { float total = 0.0f; @@ -256,6 +348,12 @@ public: return total / maxV; } + /// @brief Generate turbulence noise (absolute value of octaves) + /// @tparam T Coordinate type + /// @param point Input coordinate + /// @param octaves Number of noise layers + /// @return Turbulence noise value + /// @note Creates swirling, turbulent patterns; changes affect visual complexity template float turbulence(const Vec2& point, int octaves) { float value = 0.0f; @@ -267,6 +365,12 @@ public: return value; } + /// @brief Generate ridged (ridged multifractal) noise + /// @param point Input coordinate + /// @param octaves Number of noise layers + /// @param offset Weighting offset for ridge formation + /// @return Ridged noise value + /// @note Creates sharp ridge-like patterns; offset controls ridge prominence float ridgedNoise(const Vec2& point, int octaves, float offset = 1.0f) { float result = 0.f; float weight = 1.f; @@ -284,6 +388,11 @@ public: return result; } + /// @brief Generate billow (cloud-like) noise + /// @param point Input coordinate + /// @param octaves Number of noise layers + /// @return Billow noise value + /// @note Creates soft, billowy patterns like clouds float billowNoise(const Vec2& point, int octaves) { float value = 0.0f; float amplitude = 1.0f; @@ -297,8 +406,6 @@ public: return value; } - - }; #endif \ No newline at end of file