added sphere and noise documentation

This commit is contained in:
Yggdrasil75
2026-01-16 14:45:55 -05:00
parent acba629774
commit 0cb71a4799
2 changed files with 285 additions and 14 deletions

View File

@@ -4,6 +4,7 @@
#include <thread>
#include <atomic>
#include <mutex>
#include <cmath>
#include "../util/grid/grid3.hpp"
#include "../util/grid/g3_serialization.hpp"
@@ -42,6 +43,23 @@ std::atomic<int> recordingFramesRemaining{0};
std::vector<frame> 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<float>(x) * gValw, static_cast<float>(y) * gValh, static_cast<float>(z) * gVald)) * 255;
uint8_t b = config.noise.permute(Vec3f(static_cast<float>(x) * bValw, static_cast<float>(y) * bValh, static_cast<float>(z) * bVald)) * 255;
uint8_t a = config.noise.permute(Vec3f(static_cast<float>(x) * aValw, static_cast<float>(y) * aValh, static_cast<float>(z) * aVald)) * 255;
//Vec4ui8 noisecolor = config.noise.permuteColor(Vec3f( static_cast<float>(x) / 64, static_cast<float>(y) / 64, static_cast<float>(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<std::mutex> 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<frame> 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<float>(config.gridWidth));
ImGui::SliderFloat("Center Y", &sphereConfig.centerY, 0.0f, static_cast<float>(config.gridHeight));
ImGui::SliderFloat("Center Z", &sphereConfig.centerZ, 0.0f, static_cast<float>(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<uint8_t>(color[0] * 255);
sphereConfig.g = static_cast<uint8_t>(color[1] * 255);
sphereConfig.b = static_cast<uint8_t>(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;

View File

@@ -16,14 +16,28 @@ private:
std::vector<int> 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<int> 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<float>& 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<typename T>
float permute(Vec2<T> 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<typename T>
float permute(Vec3<T> 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<float>& 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<float>& 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<typename T>
float fractalNoise(const Vec2<T>& 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<typename T>
float turbulence(const Vec2<T>& 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<float>& 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<float>& point, int octaves) {
float value = 0.0f;
float amplitude = 1.0f;
@@ -297,8 +406,6 @@ public:
return value;
}
};
#endif