remove old junk
This commit is contained in:
@@ -1,221 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 1;
|
||||
};
|
||||
|
||||
const float PI4 = M_PI / 4.0f;
|
||||
const float PI43 = PI4 * 3.0f;
|
||||
|
||||
bool initializeGrid(Grid2& grid, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::cout << "Initializing grayscale grid..." << std::endl;
|
||||
|
||||
for (int y = 1; y < config.height; ++y) {
|
||||
for (int x = 1; x < config.width; ++x) {
|
||||
float gradient = (x + y) / float(config.width + config.height - 2);
|
||||
Vec2 position(static_cast<float>(x), static_cast<float>(y));
|
||||
Vec4 color(gradient, gradient, gradient, 1.0f);
|
||||
grid.addObject(position, color, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Grayscale grid created with " << config.width * config.height << " objects" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<std::vector<Vec2>, std::vector<Vec4>> generateSeedPoints(const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<Vec2> seedPoints;
|
||||
std::vector<Vec4> seedColors;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
seedPoints.emplace_back(xDist(gen), yDist(gen));
|
||||
seedColors.emplace_back(colorDist(gen), colorDist(gen), colorDist(gen), colorDist(gen));
|
||||
}
|
||||
|
||||
std::cout << "Generated " << config.numSeeds << " seed points for color propagation" << std::endl;
|
||||
return {seedPoints, seedColors};
|
||||
}
|
||||
|
||||
Vec4 calculateInfluencedColor(const Vec2& position, const Vec4& originalColor, float progress,
|
||||
const std::vector<Vec2>& seedPoints, const std::vector<Vec4>& seedColors,
|
||||
const AnimationConfig& config) {
|
||||
//TIME_FUNCTION;
|
||||
Vec4 newColor = originalColor;
|
||||
|
||||
float maxDistance = std::max(config.width, config.height) * 0.6f;
|
||||
|
||||
for (int s = 0; s < config.numSeeds; ++s) {
|
||||
float distance = position.distance(seedPoints[s]);
|
||||
float influence = std::max(0.0f, 1.0f - (distance / maxDistance));
|
||||
|
||||
Vec2 direction = position - seedPoints[s];
|
||||
float angle = std::atan2(direction.y, direction.x);
|
||||
auto SC = seedColors[s];
|
||||
// applyDirectionalColorInfluence(newColor, seedColors[s], influence, progress, angle);
|
||||
float absAngle = std::abs(angle);
|
||||
if (absAngle < PI4) { // Right - affect alpha
|
||||
newColor.a = std::fmod(newColor.a + SC.a * influence * progress, 1.0f);
|
||||
} else if (absAngle > PI43) { // Left - affect blue
|
||||
newColor.b = std::fmod(newColor.b + SC.b * influence * progress, 1.0f);
|
||||
} else if (angle > 0) { // Below - affect green
|
||||
newColor.g = std::fmod(newColor.g + SC.g * influence * progress, 1.0f);
|
||||
} else { // Above - affect red
|
||||
newColor.r = std::fmod(newColor.r + SC.r * influence * progress, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return newColor.clampColor();
|
||||
}
|
||||
|
||||
void updateColorsForFrame(Grid2& grid, float progress, const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
|
||||
grid.bulkUpdateColors([&](size_t id, const Vec2& pos, const Vec4& currentColor) {
|
||||
return calculateInfluencedColor(pos, currentColor, progress, seedPoints, seedColors, config);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> convertFrameToBGR(Grid2& grid, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
int frameWidth, frameHeight;
|
||||
std::vector<int> bgrData;
|
||||
grid.getGridRegionAsBGR(Vec2(0,0),Vec2(config.width,config.height), frameWidth, frameHeight, bgrData);
|
||||
//grid.getGridRegionAsRGB(0.0f,0.0f,512.0f,512.0f,frameWidth,frameHeight,rgbData);
|
||||
|
||||
std::vector<uint8_t> bgrFrame(frameWidth * frameHeight * 3);
|
||||
#pragma omp parallel for
|
||||
for (int i = 0; i < frameWidth * frameHeight; ++i) {
|
||||
int r = std::clamp(bgrData[i * 3 + 2], 0, 255);
|
||||
int g = std::clamp(bgrData[i * 3 + 1], 0, 255);
|
||||
int b = std::clamp(bgrData[i * 3], 0, 255);
|
||||
|
||||
bgrFrame[i * 3] = static_cast<uint8_t>(b);
|
||||
bgrFrame[i * 3 + 1] = static_cast<uint8_t>(g);
|
||||
bgrFrame[i * 3 + 2] = static_cast<uint8_t>(r);
|
||||
}
|
||||
|
||||
return bgrFrame;
|
||||
}
|
||||
|
||||
bool validateFrameSize(const std::vector<uint8_t>& frame, const AnimationConfig& config) {
|
||||
return frame.size() == config.width * config.height * 3;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint8_t>> createAnimationFrames(Grid2& grid,
|
||||
const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors,
|
||||
const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::vector<std::vector<uint8_t>> frames;
|
||||
|
||||
for (int frame = 0; frame < config.totalFrames; ++frame) {
|
||||
std::cout << "Processing frame " << frame + 1 << "/" << config.totalFrames << std::endl;
|
||||
|
||||
float progress = static_cast<float>(frame) / (config.totalFrames - 1);
|
||||
updateColorsForFrame(grid, progress, seedPoints, seedColors, config);
|
||||
|
||||
auto bgrFrame = convertFrameToBGR(grid, config);
|
||||
|
||||
// if (!validateFrameSize(bgrFrame, config)) {
|
||||
// std::cerr << "ERROR: Frame size mismatch in frame " << frame << std::endl;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
frames.push_back(bgrFrame);
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
void printSuccessMessage(const std::string& filename,
|
||||
const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors,
|
||||
const AnimationConfig& config) {
|
||||
std::cout << "\nSuccessfully saved chromatic transformation animation to: " << filename << std::endl;
|
||||
std::cout << "Video details:" << std::endl;
|
||||
std::cout << " - Dimensions: " << config.width << " x " << config.height << std::endl;
|
||||
std::cout << " - Frames: " << config.totalFrames << " ("
|
||||
<< config.totalFrames/config.fps << " seconds at " << config.fps << "fps)" << std::endl;
|
||||
std::cout << " - Seed points: " << config.numSeeds << std::endl;
|
||||
|
||||
std::cout << "\nSeed points used:" << std::endl;
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
std::cout << " Seed " << i + 1 << ": Position " << seedPoints[i]
|
||||
<< ", Color " << seedColors[i].toColorString() << std::endl;
|
||||
}
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
}
|
||||
|
||||
void printErrorMessage(const std::vector<std::vector<uint8_t>>& frames, const AnimationConfig& config) {
|
||||
std::cerr << "Failed to save AVI file!" << std::endl;
|
||||
std::cerr << "Debug info:" << std::endl;
|
||||
std::cerr << " - Frames count: " << frames.size() << std::endl;
|
||||
if (!frames.empty()) {
|
||||
std::cerr << " - First frame size: " << frames[0].size() << std::endl;
|
||||
std::cerr << " - Expected frame size: " << config.width * config.height * 3 << std::endl;
|
||||
}
|
||||
std::cerr << " - Width: " << config.width << ", Height: " << config.height << std::endl;
|
||||
}
|
||||
|
||||
bool saveAnimation(const std::vector<std::vector<uint8_t>>& frames, const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
std::cout << "Attempting to save AVI file: " << filename << std::endl;
|
||||
|
||||
bool success = AVIWriter::saveAVI(filename, frames, config.width, config.height, config.fps);
|
||||
|
||||
if (success) {
|
||||
printSuccessMessage(filename, seedPoints, seedColors, config);
|
||||
return true;
|
||||
} else {
|
||||
printErrorMessage(frames, config);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Creating chromatic transformation animation..." << std::endl;
|
||||
|
||||
// Configuration
|
||||
AnimationConfig config;
|
||||
|
||||
// Initialize grid
|
||||
Grid2 grid;
|
||||
if (!initializeGrid(grid, config)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Generate seed points
|
||||
auto [seedPoints, seedColors] = generateSeedPoints(config);
|
||||
|
||||
// Create animation frames
|
||||
auto frames = createAnimationFrames(grid, seedPoints, seedColors, config);
|
||||
|
||||
// Save animation
|
||||
if (!saveAnimation(frames, seedPoints, seedColors, config)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,531 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
#include "../imgui/imgui.h"
|
||||
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI = 3.1415
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::atomic<bool> isGenerating{false};
|
||||
std::future<void> generationFuture;
|
||||
|
||||
std::mutex previewMutex;
|
||||
std::atomic<bool> updatePreview{false};
|
||||
frame currentPreviewFrame;
|
||||
GLuint textu = 0;
|
||||
std::string previewText;
|
||||
|
||||
struct Shared {
|
||||
std::mutex mutex;
|
||||
Grid2 grid;
|
||||
bool hasNewFrame = false;
|
||||
int currentFrame = 0;
|
||||
};
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 8;
|
||||
int noisemod = 42;
|
||||
};
|
||||
|
||||
Grid2 setup(AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
Grid2 grid;
|
||||
std::vector<Vec2> pos;
|
||||
std::vector<Vec4> colors;
|
||||
std::vector<float> sizes;
|
||||
for (int y = 0; y < config.height; ++y) {
|
||||
for (int x = 0; x < config.width; ++x) {
|
||||
float gradient = (x + y) / float(config.width + config.height - 2);
|
||||
pos.push_back(Vec2(x,y));
|
||||
colors.push_back(Vec4(gradient, gradient, gradient, 1.0f));
|
||||
sizes.push_back(1.0f);
|
||||
}
|
||||
}
|
||||
grid.bulkAddObjects(pos,colors,sizes);
|
||||
return grid;
|
||||
}
|
||||
|
||||
void Preview(Grid2& grid) {
|
||||
TIME_FUNCTION;
|
||||
int width;
|
||||
int height;
|
||||
//std::vector<uint8_t> rgbData;
|
||||
|
||||
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
|
||||
std::cout << "Frame looks like: " << rgbData << std::endl;
|
||||
bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void livePreview(const Grid2& grid) {
|
||||
std::lock_guard<std::mutex> lock(previewMutex);
|
||||
|
||||
currentPreviewFrame = grid.getGridAsFrame(frame::colormap::RGBA);
|
||||
|
||||
glGenTextures(1, &textu);
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
|
||||
updatePreview = true;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> pickSeeds(Grid2 grid, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
Vec2 point(xDist(gen), yDist(gen));
|
||||
Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255);
|
||||
size_t id = grid.getOrCreatePositionVec(point, 0.0, true);
|
||||
grid.setColor(id, color);
|
||||
seeds.push_back(std::make_tuple(id,point, color));
|
||||
}
|
||||
return seeds;
|
||||
}
|
||||
|
||||
void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<size_t, Vec2, Vec4>>& seeds) {
|
||||
TIME_FUNCTION;
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
|
||||
|
||||
std::unordered_set<size_t> visitedThisFrame;
|
||||
for (const auto& seed : seeds) {
|
||||
visitedThisFrame.insert(std::get<0>(seed));
|
||||
}
|
||||
|
||||
//#pragma omp parallel for
|
||||
for (const std::tuple<size_t, Vec2, Vec4>& seed : seeds) {
|
||||
size_t id = std::get<0>(seed);
|
||||
Vec2 seedPOS = std::get<1>(seed);
|
||||
Vec4 seedColor = std::get<2>(seed);
|
||||
std::vector<size_t> neighbors = grid.getNeighbors(id);
|
||||
//grid.setSize(id, grid.getSize(id)+4);
|
||||
for (size_t neighbor : neighbors) {
|
||||
if (visitedThisFrame.count(neighbor)) {
|
||||
continue;
|
||||
}
|
||||
visitedThisFrame.insert(neighbor);
|
||||
|
||||
Vec2 neipos = grid.getPositionID(neighbor);
|
||||
Vec4 neighborColor = grid.getColor(neighbor);
|
||||
float distance = seedPOS.distance(neipos);
|
||||
float angle = seedPOS.directionTo(neipos);
|
||||
|
||||
float normalizedAngle = (angle + M_PI) / (2.0f * M_PI);
|
||||
float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI);
|
||||
blendFactor = std::clamp(blendFactor, 0.1f, 0.9f);
|
||||
|
||||
Vec4 newcolor = Vec4(
|
||||
seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor),
|
||||
seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor,
|
||||
seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)),
|
||||
1.0f
|
||||
);
|
||||
|
||||
newcolor = newcolor.clamp(0.0f, 1.0f);
|
||||
|
||||
grid.setColor(neighbor, newcolor);
|
||||
newseeds.emplace_back(neighbor, neipos, newcolor);
|
||||
}
|
||||
}
|
||||
seeds.clear();
|
||||
seeds.shrink_to_fit();
|
||||
seeds = std::move(newseeds);
|
||||
}
|
||||
|
||||
//bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
|
||||
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
|
||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
||||
|
||||
// Log compression statistics for all frames
|
||||
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
|
||||
size_t totalOriginalSize = 0;
|
||||
size_t totalCompressedSize = 0;
|
||||
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
totalOriginalSize += frames[i].getSourceSize();
|
||||
totalCompressedSize += frames[i].getTotalCompressedSize();
|
||||
}
|
||||
|
||||
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
|
||||
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
|
||||
|
||||
std::cout << "\n=== Overall Compression Summary ===" << std::endl;
|
||||
std::cout << "Total frames: " << frames.size() << std::endl;
|
||||
std::cout << "Compressed frames: " << frames.size() << std::endl;
|
||||
std::cout << "Total original size: " << totalOriginalSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Total compressed size: " << totalCompressedSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl;
|
||||
std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl;
|
||||
|
||||
std::filesystem::path dir = "output";
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
if (!std::filesystem::create_directories(dir)) {
|
||||
std::cout << "Failed to create output directory!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
|
||||
|
||||
if (success) {
|
||||
// Check if file actually exists
|
||||
if (std::filesystem::exists(filename)) {
|
||||
auto file_size = std::filesystem::file_size(filename);
|
||||
std::cout << "\nAVI file created successfully: " << filename
|
||||
<< " (" << file_size << " bytes, "
|
||||
<< std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Failed to save AVI file!" << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) {
|
||||
TIME_FUNCTION;
|
||||
isGenerating = true;
|
||||
try {
|
||||
Grid2 grid;
|
||||
if (gradnoise == 0) {
|
||||
grid = setup(config);
|
||||
} else if (gradnoise == 1) {
|
||||
grid = grid.noiseGenGrid(0,0,config.height, config.width, 0.01, 1.0, true, config.noisemod);
|
||||
}
|
||||
grid.setDefault(Vec4(0,0,0,0));
|
||||
{
|
||||
std:: lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = 0;
|
||||
}
|
||||
std::cout << "generated grid" << std::endl;
|
||||
Preview(grid);
|
||||
std::cout << "generated preview" << std::endl;
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid, config);
|
||||
std::vector<frame> frames;
|
||||
|
||||
for (int i = 0; i < config.totalFrames; ++i){
|
||||
// Check if we should stop the generation
|
||||
if (!isGenerating) {
|
||||
std::cout << "Generation cancelled at frame " << i << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
expandPixel(grid,config,seeds);
|
||||
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = i;
|
||||
|
||||
// Print compression info for this frame
|
||||
if (i % 10 == 0 ) {
|
||||
frame bgrframe;
|
||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||
bgrframe = grid.getGridAsFrame(frame::colormap::BGR);
|
||||
frames.push_back(bgrframe);
|
||||
//bgrframe.decompress();
|
||||
//BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe);
|
||||
bgrframe.compressFrameLZ78();
|
||||
//bgrframe.printCompressionStats();
|
||||
}
|
||||
}
|
||||
exportavi(frames,config);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "errored at: " << e.what() << std::endl;
|
||||
}
|
||||
isGenerating = false;
|
||||
}
|
||||
|
||||
// Function to cancel ongoing generation
|
||||
void cancelGeneration() {
|
||||
if (isGenerating) {
|
||||
isGenerating = false;
|
||||
// Wait for the thread to finish (with timeout to avoid hanging)
|
||||
if (generationFuture.valid()) {
|
||||
auto status = generationFuture.wait_for(std::chrono::milliseconds(100));
|
||||
if (status != std::future_status::ready) {
|
||||
std::cout << "Waiting for generation thread to finish..." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
int main() {
|
||||
//static bool window = true;
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(__APPLE__)
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||
#endif
|
||||
//ImGui::SetNextWindowSize(ImVec2(1110,667));
|
||||
//auto beg = ImGui::Begin("Gradient thing", &window);
|
||||
//if (beg) {
|
||||
// std::cout << "stuff breaks at 223" << std::endl;
|
||||
bool application_not_closed = true;
|
||||
//float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
|
||||
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr);
|
||||
if (window == nullptr)
|
||||
return 1;
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
//IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring.
|
||||
ImGui::CreateContext();
|
||||
// std::cout << "context created" << std::endl;
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
//style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
|
||||
//style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it.
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
||||
|
||||
// std::cout << "created glfw window" << std::endl;
|
||||
|
||||
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
static float f = 30.0f;
|
||||
static int i1 = 1024;
|
||||
static int i2 = 1024;
|
||||
static int i3 = 480;
|
||||
static int i4 = 8;
|
||||
static int noisemod = 42;
|
||||
static float fs = 1.0;
|
||||
|
||||
std::future<void> mainlogicthread;
|
||||
Shared state;
|
||||
Grid2 grid;
|
||||
AnimationConfig config;
|
||||
previewText = "Please generate";
|
||||
int gradnoise = true;
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
|
||||
ImGui::Begin("settings");
|
||||
|
||||
ImGui::SliderFloat("fps", &f, 20.0f, 60.0f);
|
||||
ImGui::SliderInt("width", &i1, 256, 4096);
|
||||
ImGui::SliderInt("height", &i2, 256, 4096);
|
||||
ImGui::SliderInt("frame count", &i3, 10, 5000);
|
||||
ImGui::SliderInt("number of Seeds", &i4, 0, 10);
|
||||
ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000);
|
||||
ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0);
|
||||
ImGui::RadioButton("Gradient", &gradnoise, 0);
|
||||
ImGui::RadioButton("Perlin Noise", &gradnoise, 1);
|
||||
|
||||
if (isGenerating) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Generate Animation")) {
|
||||
config = AnimationConfig(i1, i2, i3, f, i4, noisemod);
|
||||
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
|
||||
}
|
||||
|
||||
if (isGenerating && textu != 0) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else if (isGenerating) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
} else if (textu != 0){
|
||||
//ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else {
|
||||
ImGui::Text("No preview available");
|
||||
ImGui::Text("Start generation to see live preview");
|
||||
}
|
||||
//std::cout << "sleeping" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
//std::cout << "ending" << std::endl;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
// std::cout << "ending frame" << std::endl;
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// std::cout << "rendering" << std::endl;
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
//mainlogicthread.join();
|
||||
|
||||
// std::cout << "swapping buffers" << std::endl;
|
||||
}
|
||||
cancelGeneration();
|
||||
|
||||
|
||||
// std::cout << "shutting down" << std::endl;
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
// std::cout << "destroying" << std::endl;
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
// std::cout << "printing" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
//I need this: https://raais.github.io/ImStudio/
|
||||
// g++ -std=c++23 -O3 -march=native -o ./bin/g2gradc ./tests/g2chromatic2.cpp -I./imgui -L./imgui -limgui -lstb `pkg-config --cflags --libs glfw3` && ./bin/g2gradc
|
||||
@@ -1,306 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <immintrin.h>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 240;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 1;
|
||||
};
|
||||
|
||||
const float PI4 = M_PI / 4.0f;
|
||||
const float PI43 = PI4 * 3.0f;
|
||||
|
||||
struct SeedDataSoA {
|
||||
// Use alignas to ensure 16-byte alignment for SSE instructions
|
||||
std::vector<float> sx, sy; // Seed X and Y positions
|
||||
std::vector<float> sr, sg, sb, sa; // Seed R, G, B, A colors
|
||||
size_t count = 0;
|
||||
|
||||
void reserve(size_t n) {
|
||||
sx.reserve(n); sy.reserve(n);
|
||||
sr.reserve(n); sg.reserve(n); sb.reserve(n); sa.reserve(n);
|
||||
}
|
||||
};
|
||||
|
||||
SeedDataSoA convertSeedsToSoA(const std::vector<Vec2>& points, const std::vector<Vec4>& colors) {
|
||||
TIME_FUNCTION;
|
||||
SeedDataSoA soaData;
|
||||
size_t numSeeds = points.size();
|
||||
// Pad to the nearest multiple of 4 for clean SIMD loops
|
||||
size_t paddedSize = (numSeeds + 3) & ~3;
|
||||
soaData.count = paddedSize;
|
||||
soaData.reserve(paddedSize);
|
||||
|
||||
for (size_t i = 0; i < numSeeds; ++i) {
|
||||
soaData.sx.push_back(points[i].x);
|
||||
soaData.sy.push_back(points[i].y);
|
||||
soaData.sr.push_back(colors[i].r);
|
||||
soaData.sg.push_back(colors[i].g);
|
||||
soaData.sb.push_back(colors[i].b);
|
||||
soaData.sa.push_back(colors[i].a);
|
||||
}
|
||||
|
||||
// Add padding elements
|
||||
for (size_t i = numSeeds; i < paddedSize; ++i) {
|
||||
soaData.sx.push_back(0); soaData.sy.push_back(0);
|
||||
soaData.sr.push_back(0); soaData.sg.push_back(0);
|
||||
soaData.sb.push_back(0); soaData.sa.push_back(0);
|
||||
}
|
||||
std::cout << "Converted " << numSeeds << " seeds to SoA format (padded to " << paddedSize << ")" << std::endl;
|
||||
return soaData;
|
||||
}
|
||||
|
||||
// Helper for a horizontal add of a __m128 register
|
||||
inline float horizontal_add(__m128 v) {
|
||||
__m128 shuf = _mm_movehdup_ps(v); // {v.z, v.z, v.w, v.w}
|
||||
__m128 sums = _mm_add_ps(v, shuf); // {v.x+v.z, v.y+v.z, v.z+v.w, v.w+v.w}
|
||||
shuf = _mm_movehl_ps(shuf, sums); // {v.z+v.w, v.w+v.w, ...}
|
||||
sums = _mm_add_ss(sums, shuf); // adds lowest float
|
||||
return _mm_cvtss_f32(sums);
|
||||
}
|
||||
|
||||
// Non-optimized atan2 for a SIMD vector. For a real high-performance scenario,
|
||||
// you would use a library like SLEEF or a polynomial approximation.
|
||||
inline __m128 _mm_atan2_ps(__m128 y, __m128 x) {
|
||||
float y_lanes[4], x_lanes[4];
|
||||
_mm_storeu_ps(y_lanes, y);
|
||||
_mm_storeu_ps(x_lanes, x);
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
y_lanes[i] = std::atan2(y_lanes[i], x_lanes[i]);
|
||||
}
|
||||
return _mm_loadu_ps(y_lanes);
|
||||
}
|
||||
|
||||
bool initializeGrid(Grid2& grid, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::cout << "Initializing grayscale grid..." << std::endl;
|
||||
|
||||
for (int y = 1; y < config.height; ++y) {
|
||||
for (int x = 1; x < config.width; ++x) {
|
||||
float gradient = (x + y) / float(config.width + config.height - 2);
|
||||
Vec2 position(static_cast<float>(x), static_cast<float>(y));
|
||||
Vec4 color(gradient, gradient, gradient, 1.0f);
|
||||
grid.addObject(position, color, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Grayscale grid created with " << config.width * config.height << " objects" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::pair<std::vector<Vec2>, std::vector<Vec4>> generateSeedPoints(const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<Vec2> seedPoints;
|
||||
std::vector<Vec4> seedColors;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
seedPoints.emplace_back(xDist(gen), yDist(gen));
|
||||
seedColors.emplace_back(colorDist(gen), colorDist(gen), colorDist(gen), 1.0f); // Alpha fixed to 1.0
|
||||
}
|
||||
std::cout << "Generated " << config.numSeeds << " seed points for color propagation" << std::endl;
|
||||
return {seedPoints, seedColors};
|
||||
}
|
||||
|
||||
Vec4 calculateInfluencedColor(const Vec2& position, const Vec4& originalColor, float progress,
|
||||
const SeedDataSoA& seeds, const AnimationConfig& config) {
|
||||
const __m128 v_max_dist = _mm_set1_ps(std::max(config.width, config.height) * 0.6f);
|
||||
const __m128 v_progress = _mm_set1_ps(progress);
|
||||
const __m128 v_one = _mm_set1_ps(1.0f);
|
||||
const __m128 v_zero = _mm_setzero_ps();
|
||||
const __m128 v_pi4 = _mm_set1_ps(PI4);
|
||||
const __m128 v_pi43 = _mm_set1_ps(PI43);
|
||||
const __m128 v_neg_zero = _mm_set1_ps(-0.0f); // For fast absolute value
|
||||
|
||||
// Broadcast pixel position into SIMD registers
|
||||
const __m128 v_pos_x = _mm_set1_ps(position.x);
|
||||
const __m128 v_pos_y = _mm_set1_ps(position.y);
|
||||
|
||||
// Accumulators for color channel updates
|
||||
__m128 r_updates = _mm_setzero_ps();
|
||||
__m128 g_updates = _mm_setzero_ps();
|
||||
__m128 b_updates = _mm_setzero_ps();
|
||||
__m128 a_updates = _mm_setzero_ps();
|
||||
|
||||
// Process 4 seeds at a time
|
||||
for (size_t s = 0; s < seeds.count; s += 4) {
|
||||
// --- Load data for 4 seeds ---
|
||||
const __m128 seed_x = _mm_load_ps(&seeds.sx[s]);
|
||||
const __m128 seed_y = _mm_load_ps(&seeds.sy[s]);
|
||||
|
||||
// --- Calculate distance and influence ---
|
||||
const __m128 dir_x = _mm_sub_ps(v_pos_x, seed_x);
|
||||
const __m128 dir_y = _mm_sub_ps(v_pos_y, seed_y);
|
||||
const __m128 dist_sq = _mm_add_ps(_mm_mul_ps(dir_x, dir_x), _mm_mul_ps(dir_y, dir_y));
|
||||
const __m128 dist = _mm_sqrt_ps(dist_sq);
|
||||
__m128 influence = _mm_sub_ps(v_one, _mm_div_ps(dist, v_max_dist));
|
||||
influence = _mm_max_ps(v_zero, influence); // clamp to 0
|
||||
|
||||
// --- Calculate full potential color contribution ---
|
||||
const __m128 influence_progress = _mm_mul_ps(influence, v_progress);
|
||||
const __m128 contrib_r = _mm_mul_ps(_mm_load_ps(&seeds.sr[s]), influence_progress);
|
||||
const __m128 contrib_g = _mm_mul_ps(_mm_load_ps(&seeds.sg[s]), influence_progress);
|
||||
const __m128 contrib_b = _mm_mul_ps(_mm_load_ps(&seeds.sb[s]), influence_progress);
|
||||
const __m128 contrib_a = _mm_mul_ps(_mm_load_ps(&seeds.sa[s]), influence_progress);
|
||||
|
||||
// --- Branchless Masking based on Angle ---
|
||||
const __m128 angle = _mm_atan2_ps(dir_y, dir_x);
|
||||
const __m128 abs_angle = _mm_andnot_ps(v_neg_zero, angle); // fast abs
|
||||
|
||||
// Create masks for each condition
|
||||
const __m128 mask_right = _mm_cmplt_ps(abs_angle, v_pi4); // abs(angle) < PI4
|
||||
const __m128 mask_left = _mm_cmpgt_ps(abs_angle, v_pi43); // abs(angle) > PI43
|
||||
|
||||
// The "else if" logic is tricky. We need to ensure mutual exclusivity.
|
||||
// mask_below is true if (angle > 0) AND NOT (right OR left)
|
||||
const __m128 is_not_rl = _mm_andnot_ps(mask_right, _mm_andnot_ps(mask_left, v_one));
|
||||
const __m128 mask_below = _mm_and_ps(_mm_cmpgt_ps(angle, v_zero), is_not_rl);
|
||||
|
||||
// The "else" case (above) is whatever is left over.
|
||||
// mask_above is true if NOT (right OR left OR below)
|
||||
const __m128 mask_above = _mm_andnot_ps(mask_below, is_not_rl);
|
||||
|
||||
// --- Accumulate updates using masks ---
|
||||
// Add contribution only where mask is active (all 1s)
|
||||
r_updates = _mm_add_ps(r_updates, _mm_and_ps(contrib_r, mask_above));
|
||||
g_updates = _mm_add_ps(g_updates, _mm_and_ps(contrib_g, mask_below));
|
||||
b_updates = _mm_add_ps(b_updates, _mm_and_ps(contrib_b, mask_left));
|
||||
a_updates = _mm_add_ps(a_updates, _mm_and_ps(contrib_a, mask_right));
|
||||
}
|
||||
|
||||
// --- Horizontal Reduction: Sum the updates from all lanes ---
|
||||
Vec4 newColor = originalColor;
|
||||
newColor.r += horizontal_add(r_updates);
|
||||
newColor.g += horizontal_add(g_updates);
|
||||
newColor.b += horizontal_add(b_updates);
|
||||
newColor.a += horizontal_add(a_updates);
|
||||
|
||||
// --- Apply fmod(x, 1.0) which is x - floor(x) for positive numbers ---
|
||||
// Can do this with SIMD as well for the final color vector
|
||||
__m128 final_color_v = _mm_loadu_ps(&newColor.r);
|
||||
final_color_v = _mm_sub_ps(final_color_v, _mm_floor_ps(final_color_v));
|
||||
_mm_storeu_ps(&newColor.r, final_color_v);
|
||||
|
||||
return newColor.clampColor();
|
||||
}
|
||||
|
||||
void updateColorsForFrame(Grid2& grid, float progress, const SeedDataSoA& seeds, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
|
||||
grid.bulkUpdateColors([&](size_t id, const Vec2& pos, const Vec4& currentColor) {
|
||||
return calculateInfluencedColor(pos, currentColor, progress, seeds, config);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> convertFrameToBGR(Grid2& grid, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
int frameWidth, frameHeight;
|
||||
std::vector<int> bgrData;
|
||||
grid.getGridRegionAsBGR(Vec2(0,0), Vec2(config.width, config.height), frameWidth, frameHeight, bgrData);
|
||||
std::vector<uint8_t> bgrFrame(frameWidth * frameHeight * 3);
|
||||
#pragma omp parallel for
|
||||
for (int i = 0; i < frameWidth * frameHeight; ++i) {
|
||||
bgrFrame[i * 3] = static_cast<uint8_t>(std::clamp(bgrData[i * 3], 0, 255));
|
||||
bgrFrame[i * 3 + 1] = static_cast<uint8_t>(std::clamp(bgrData[i * 3 + 1], 0, 255));
|
||||
bgrFrame[i * 3 + 2] = static_cast<uint8_t>(std::clamp(bgrData[i * 3 + 2], 0, 255));
|
||||
}
|
||||
return bgrFrame;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::vector<uint8_t>> createAnimationFrames(Grid2& grid, const SeedDataSoA& seeds, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::vector<std::vector<uint8_t>> frames;
|
||||
|
||||
for (int frame = 0; frame < config.totalFrames; ++frame) {
|
||||
std::cout << "Processing frame " << frame + 1 << "/" << config.totalFrames << std::endl;
|
||||
|
||||
float progress = static_cast<float>(frame) / (config.totalFrames - 1);
|
||||
updateColorsForFrame(grid, progress, seeds, config);
|
||||
|
||||
std::vector<u_int8_t> bgrFrame = convertFrameToBGR(grid, config);
|
||||
frames.push_back(bgrFrame);
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
void printSuccessMessage(const std::string& filename, const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors, const AnimationConfig& config) {
|
||||
std::cout << "\nSuccessfully saved chromatic transformation animation to: " << filename << std::endl;
|
||||
std::cout << "Video details:" << std::endl;
|
||||
std::cout << " - Dimensions: " << config.width << " x " << config.height << std::endl;
|
||||
std::cout << " - Frames: " << config.totalFrames << " ("
|
||||
<< config.totalFrames/config.fps << " seconds at " << config.fps << "fps)" << std::endl;
|
||||
std::cout << " - Seed points: " << config.numSeeds << std::endl;
|
||||
|
||||
std::cout << "\nSeed points used:" << std::endl;
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
std::cout << " Seed " << i + 1 << ": Position " << seedPoints[i]
|
||||
<< ", Color " << seedColors[i].toColorString() << std::endl;
|
||||
}
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
}
|
||||
|
||||
void printErrorMessage(const std::vector<std::vector<uint8_t>>& frames, const AnimationConfig& config) {
|
||||
std::cerr << "Failed to save AVI file!" << std::endl;
|
||||
std::cerr << "Debug info:" << std::endl;
|
||||
std::cerr << " - Frames count: " << frames.size() << std::endl;
|
||||
if (!frames.empty()) {
|
||||
std::cerr << " - First frame size: " << frames[0].size() << std::endl;
|
||||
std::cerr << " - Expected frame size: " << config.width * config.height * 3 << std::endl;
|
||||
}
|
||||
std::cerr << " - Width: " << config.width << ", Height: " << config.height << std::endl;
|
||||
}
|
||||
|
||||
bool saveAnimation(const std::vector<std::vector<uint8_t>>& frames, const std::vector<Vec2>& seedPoints,
|
||||
const std::vector<Vec4>& seedColors, const AnimationConfig& config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
std::cout << "Attempting to save AVI file: " << filename << std::endl;
|
||||
|
||||
bool success = AVIWriter::saveAVI(filename, frames, config.width, config.height, config.fps);
|
||||
|
||||
if (success) {
|
||||
printSuccessMessage(filename, seedPoints, seedColors, config);
|
||||
return true;
|
||||
} else {
|
||||
printErrorMessage(frames, config);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::cout << "Creating chromatic transformation animation..." << std::endl;
|
||||
|
||||
AnimationConfig config;
|
||||
|
||||
Grid2 grid;
|
||||
if (!initializeGrid(grid, config)) return 1;
|
||||
|
||||
auto [seedPoints, seedColors] = generateSeedPoints(config);
|
||||
auto seeds_SoA = convertSeedsToSoA(seedPoints, seedColors);
|
||||
|
||||
// Create animation frames using the SIMD-friendly data
|
||||
auto frames = createAnimationFrames(grid, seeds_SoA, config);
|
||||
|
||||
if (!saveAnimation(frames, seedPoints, seedColors, config)) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
|
||||
int main() {
|
||||
// Create a Grid2 instance
|
||||
Grid2 grid;
|
||||
|
||||
// Grid dimensions
|
||||
const int width = 100;
|
||||
const int height = 100;
|
||||
|
||||
std::cout << "Creating grayscale gradient..." << std::endl;
|
||||
|
||||
// Add objects to create a grayscale gradient
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
// Calculate gradient value (0.0 at top-left to 1.0 at bottom-right)
|
||||
float gradient = (x + y) / float(width + height - 2);
|
||||
|
||||
// Create position
|
||||
Vec2 position(static_cast<float>(x), static_cast<float>(y));
|
||||
|
||||
// Create grayscale color (r=g=b=gradient, a=1.0)
|
||||
Vec4 color(gradient, gradient, gradient, 1.0f);
|
||||
|
||||
// Add to grid with size 1.0 (single pixel)
|
||||
grid.addObject(position, color, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Added " << width * height << " objects to grid" << std::endl;
|
||||
|
||||
// Get the entire grid as RGB data
|
||||
int outputWidth, outputHeight;
|
||||
std::vector<int> rgbData;
|
||||
grid.getGridAsRGB(outputWidth, outputHeight, rgbData);
|
||||
|
||||
std::cout << "Output dimensions: " << outputWidth << " x " << outputHeight << std::endl;
|
||||
std::cout << "RGB data size: " << rgbData.size() << " elements" << std::endl;
|
||||
|
||||
// Convert RGB data to format suitable for BMPWriter
|
||||
std::vector<Vec3> pixels;
|
||||
pixels.reserve(outputWidth * outputHeight);
|
||||
|
||||
for (size_t i = 0; i < rgbData.size(); i += 3) {
|
||||
float r = rgbData[i] / 255.0f;
|
||||
float g = rgbData[i + 1] / 255.0f;
|
||||
float b = rgbData[i + 2] / 255.0f;
|
||||
pixels.emplace_back(r, g, b);
|
||||
}
|
||||
|
||||
// Save as BMP
|
||||
std::string filename = "output/grayscale_gradient.bmp";
|
||||
bool success = BMPWriter::saveBMP(filename, pixels, outputWidth, outputHeight);
|
||||
|
||||
if (success) {
|
||||
std::cout << "Successfully saved grayscale gradient to: " << filename << std::endl;
|
||||
|
||||
// Print some gradient values for verification
|
||||
std::cout << "\nGradient values at key positions:" << std::endl;
|
||||
std::cout << "Top-left (0,0): " << grid.getColor(grid.getIndicesAt(0, 0)[0]).r << std::endl;
|
||||
std::cout << "Center (" << width/2 << "," << height/2 << "): "
|
||||
<< grid.getColor(grid.getIndicesAt(width/2, height/2)[0]).r << std::endl;
|
||||
std::cout << "Bottom-right (" << width-1 << "," << height-1 << "): "
|
||||
<< grid.getColor(grid.getIndicesAt(width-1, height-1)[0]).r << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to save BMP file!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
560
tests/g2temp.cpp
560
tests/g2temp.cpp
@@ -1,560 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
#include "../imgui/imgui.h"
|
||||
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI = 3.1415
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::atomic<int> isGenerating{0};
|
||||
std::future<void> generationFuture;
|
||||
|
||||
std::mutex previewMutex;
|
||||
std::atomic<bool> updatePreview{false};
|
||||
frame currentPreviewFrame;
|
||||
GLuint textu = 0;
|
||||
std::string previewText;
|
||||
|
||||
struct Shared {
|
||||
std::mutex mutex;
|
||||
Grid2 grid;
|
||||
bool hasNewFrame = false;
|
||||
int currentFrame = 0;
|
||||
};
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 8;
|
||||
int noisemod = 42;
|
||||
};
|
||||
|
||||
Grid2 setup(AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
Grid2 grid;
|
||||
std::vector<Vec2> pos;
|
||||
std::vector<Vec4> colors;
|
||||
std::vector<float> sizes;
|
||||
for (int y = 0; y < config.height - 1; ++y) {
|
||||
for (int x = 0; x < config.width - 1; ++x) {
|
||||
float gradient = (x + y) / float(config.width + config.height - 2);
|
||||
pos.push_back(Vec2(x,y));
|
||||
colors.push_back(Vec4(gradient, gradient, gradient, 1.0f));
|
||||
sizes.push_back(1.0f);
|
||||
}
|
||||
}
|
||||
grid.bulkAddObjects(pos,colors,sizes);
|
||||
return grid;
|
||||
}
|
||||
|
||||
void Preview(Grid2& grid) {
|
||||
TIME_FUNCTION;
|
||||
int width;
|
||||
int height;
|
||||
//std::vector<uint8_t> rgbData;
|
||||
|
||||
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
|
||||
std::cout << "Frame looks like: " << rgbData << std::endl;
|
||||
bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void livePreview(Grid2& grid, AnimationConfig config) {
|
||||
// std::lock_guard<std::mutex> lock(previewMutex);
|
||||
|
||||
// currentPreviewFrame = grid.getTempAsFrame(Vec2(0,0), Vec2(config.height,config.width), Vec2(256,256),frame::colormap::RGBA);
|
||||
// // Vec2 min;
|
||||
// // Vec2 max;
|
||||
// // grid.getBoundingBox(min, max);
|
||||
// //currentPreviewFrame = grid.getTempAsFrame(min,max, Vec2(1024,1024));
|
||||
|
||||
// glGenTextures(1, &textu);
|
||||
// glBindTexture(GL_TEXTURE_2D, textu);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
// glBindTexture(GL_TEXTURE_2D, textu);
|
||||
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
// 0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
|
||||
// updatePreview = true;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> pickSeeds(Grid2 grid, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
Vec2 point(xDist(gen), yDist(gen));
|
||||
Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255);
|
||||
size_t id = grid.getOrCreatePositionVec(point, 0.0, true);
|
||||
grid.setColor(id, color);
|
||||
seeds.push_back(std::make_tuple(id,point, color));
|
||||
}
|
||||
return seeds;
|
||||
}
|
||||
|
||||
void pickTempSeeds(Grid2& grid, AnimationConfig config) {
|
||||
std::cout << "pickTempSeeds()" << std::endl;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> temp(0.0f, 100.0f);
|
||||
|
||||
for (int i = 0; i < config.numSeeds * 100; ++i){
|
||||
grid.setTemp(Vec2(xDist(gen),yDist(gen)).floor(), temp(gen));
|
||||
}
|
||||
}
|
||||
|
||||
void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<size_t, Vec2, Vec4>>& seeds) {
|
||||
TIME_FUNCTION;
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
|
||||
|
||||
std::unordered_set<size_t> visitedThisFrame;
|
||||
for (const auto& seed : seeds) {
|
||||
visitedThisFrame.insert(std::get<0>(seed));
|
||||
}
|
||||
|
||||
//#pragma omp parallel for
|
||||
for (const std::tuple<size_t, Vec2, Vec4>& seed : seeds) {
|
||||
size_t id = std::get<0>(seed);
|
||||
Vec2 seedPOS = std::get<1>(seed);
|
||||
Vec4 seedColor = std::get<2>(seed);
|
||||
std::vector<size_t> neighbors = grid.getNeighbors(id);
|
||||
//grid.setSize(id, grid.getSize(id)+4);
|
||||
for (size_t neighbor : neighbors) {
|
||||
if (visitedThisFrame.count(neighbor)) {
|
||||
continue;
|
||||
}
|
||||
visitedThisFrame.insert(neighbor);
|
||||
|
||||
Vec2 neipos = grid.getPositionID(neighbor);
|
||||
Vec4 neighborColor = grid.getColor(neighbor);
|
||||
float distance = seedPOS.distance(neipos);
|
||||
float angle = seedPOS.directionTo(neipos);
|
||||
|
||||
float normalizedAngle = (angle + M_PI) / (2.0f * M_PI);
|
||||
float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI);
|
||||
blendFactor = std::clamp(blendFactor, 0.1f, 0.9f);
|
||||
|
||||
Vec4 newcolor = Vec4(
|
||||
seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor),
|
||||
seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor,
|
||||
seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)),
|
||||
1.0f
|
||||
);
|
||||
|
||||
newcolor = newcolor.clamp(0.0f, 1.0f);
|
||||
|
||||
grid.setColor(neighbor, newcolor);
|
||||
newseeds.emplace_back(neighbor, neipos, newcolor);
|
||||
}
|
||||
}
|
||||
seeds.clear();
|
||||
seeds.shrink_to_fit();
|
||||
seeds = std::move(newseeds);
|
||||
}
|
||||
|
||||
//bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
|
||||
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
|
||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
||||
|
||||
// Log compression statistics for all frames
|
||||
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
|
||||
size_t totalOriginalSize = 0;
|
||||
size_t totalCompressedSize = 0;
|
||||
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
totalOriginalSize += frames[i].getSourceSize();
|
||||
totalCompressedSize += frames[i].getTotalCompressedSize();
|
||||
}
|
||||
|
||||
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
|
||||
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
|
||||
|
||||
std::cout << "\n=== Overall Compression Summary ===" << std::endl;
|
||||
std::cout << "Total frames: " << frames.size() << std::endl;
|
||||
std::cout << "Compressed frames: " << frames.size() << std::endl;
|
||||
std::cout << "Total original size: " << totalOriginalSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Total compressed size: " << totalCompressedSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl;
|
||||
std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl;
|
||||
|
||||
std::filesystem::path dir = "output";
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
if (!std::filesystem::create_directories(dir)) {
|
||||
std::cout << "Failed to create output directory!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
|
||||
|
||||
if (success) {
|
||||
// Check if file actually exists
|
||||
if (std::filesystem::exists(filename)) {
|
||||
auto file_size = std::filesystem::file_size(filename);
|
||||
std::cout << "\nAVI file created successfully: " << filename
|
||||
<< " (" << file_size << " bytes, "
|
||||
<< std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Failed to save AVI file!" << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) {
|
||||
TIME_FUNCTION;
|
||||
if (isGenerating != 0) return; //apparently sometimes this function is called twice. dont know how, but this might resolve that.
|
||||
|
||||
try {
|
||||
Grid2 grid;
|
||||
isGenerating = 1;
|
||||
if (gradnoise == 0) {
|
||||
grid = setup(config);
|
||||
} else if (gradnoise == 1) {
|
||||
grid = grid.noiseGenGridTemps(0,0,config.height, config.width, 0.01, 1.0, false, config.noisemod);
|
||||
}
|
||||
grid.setDefault(Vec4(0,0,0,0));
|
||||
{
|
||||
std:: lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = 0;
|
||||
}
|
||||
//pickTempSeeds(grid,config);
|
||||
|
||||
//std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid, config);
|
||||
std::cout << "generated grid" << std::endl;
|
||||
Preview(grid);
|
||||
std::cout << "generated preview" << std::endl;
|
||||
//grid = grid.backfillGrid();
|
||||
frame tempData = grid.getTempAsFrame(Vec2(0,0), Vec2(config.height,config.width), Vec2(256,256));
|
||||
std::cout << "Temp frame looks like: " << tempData << std::endl;
|
||||
bool success = BMPWriter::saveBMP("output/temperature.bmp", tempData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
isGenerating = 2;
|
||||
std::vector<frame> frames;
|
||||
|
||||
for (int i = 0; i < config.totalFrames; ++i){
|
||||
// Check if we should stop the generation
|
||||
if (isGenerating == 0) {
|
||||
std::cout << "Generation cancelled at frame " << i << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//expandPixel(grid,config,seeds);
|
||||
grid.diffuseTemps(100);
|
||||
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = i;
|
||||
|
||||
// Print compression info for this frame
|
||||
//if (i % 10 == 0 ) {
|
||||
frame bgrframe;
|
||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||
bgrframe = grid.getTempAsFrame(Vec2(0,0), Vec2(config.height,config.width), Vec2(256,256), frame::colormap::BGR);
|
||||
frames.push_back(bgrframe);
|
||||
//bgrframe.decompress();
|
||||
//BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe);
|
||||
bgrframe.compressFrameLZ78();
|
||||
//bgrframe.printCompressionStats();
|
||||
//}
|
||||
}
|
||||
exportavi(frames,config);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "errored at: " << e.what() << std::endl;
|
||||
}
|
||||
isGenerating = 0;
|
||||
}
|
||||
|
||||
// Function to cancel ongoing generation
|
||||
void cancelGeneration() {
|
||||
if (isGenerating) {
|
||||
isGenerating = 0;
|
||||
// Wait for the thread to finish (with timeout to avoid hanging)
|
||||
if (generationFuture.valid()) {
|
||||
auto status = generationFuture.wait_for(std::chrono::milliseconds(100));
|
||||
if (status != std::future_status::ready) {
|
||||
std::cout << "Waiting for generation thread to finish..." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
int main() {
|
||||
//static bool window = true;
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(__APPLE__)
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||
#endif
|
||||
//ImGui::SetNextWindowSize(ImVec2(1110,667));
|
||||
//auto beg = ImGui::Begin("Gradient thing", &window);
|
||||
//if (beg) {
|
||||
// std::cout << "stuff breaks at 223" << std::endl;
|
||||
bool application_not_closed = true;
|
||||
//float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
|
||||
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr);
|
||||
if (window == nullptr)
|
||||
return 1;
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
//IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring.
|
||||
ImGui::CreateContext();
|
||||
// std::cout << "context created" << std::endl;
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
//style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
|
||||
//style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it.
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
|
||||
#endif
|
||||
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);
|
||||
static float f = 30.0f;
|
||||
static int i1 = 256;
|
||||
static int i2 = 256;
|
||||
static int i3 = 4800;
|
||||
static int i4 = 8;
|
||||
static int noisemod = 42;
|
||||
static float fs = 1.0;
|
||||
int gradnoise = 1;
|
||||
|
||||
std::future<void> mainlogicthread;
|
||||
Shared state;
|
||||
Grid2 grid;
|
||||
AnimationConfig config;
|
||||
previewText = "Please generate";
|
||||
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
|
||||
ImGui::Begin("settings");
|
||||
|
||||
ImGui::SliderFloat("fps", &f, 20.0f, 60.0f);
|
||||
ImGui::SliderInt("width", &i1, 256, 4096);
|
||||
ImGui::SliderInt("height", &i2, 256, 4096);
|
||||
ImGui::SliderInt("frame count", &i3, 10, 5000);
|
||||
ImGui::SliderInt("number of Seeds", &i4, 0, 10);
|
||||
ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000);
|
||||
ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0);
|
||||
ImGui::RadioButton("Gradient", &gradnoise, 0);
|
||||
ImGui::RadioButton("Perlin Noise", &gradnoise, 1);
|
||||
|
||||
if (isGenerating != 0) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Generate Animation")) {
|
||||
config = AnimationConfig(i1, i2, i3, f, i4, noisemod);
|
||||
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
|
||||
}
|
||||
|
||||
if (isGenerating == 2 && textu != 0) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid, config);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else if (isGenerating == 2) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid, config);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
} else if (textu != 0){
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else if (isGenerating != 0) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else {
|
||||
ImGui::Text("No preview available");
|
||||
ImGui::Text("Start generation to see live preview");
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
|
||||
}
|
||||
cancelGeneration();
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include "../util/grid/grid3.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
#include "../imgui/imgui.h"
|
||||
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI = 3.1415
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::atomic<bool> isGenerating{false};
|
||||
std::future<void> generationFuture;
|
||||
|
||||
std::mutex previewMutex;
|
||||
std::atomic<bool> updatePreview{false};
|
||||
frame currentPreviewFrame;
|
||||
GLuint textu = 0;
|
||||
std::string previewText;
|
||||
|
||||
struct Shared {
|
||||
std::mutex mutex;
|
||||
Grid3 grid;
|
||||
bool hasNewFrame = false;
|
||||
int currentFrame = 0;
|
||||
};
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int depth = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 8;
|
||||
int noisemod = 42;
|
||||
};
|
||||
|
||||
Grid3 setup(AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
Grid3 grid;
|
||||
std::vector<Vec3f> pos;
|
||||
std::vector<Vec4ui8> colors;
|
||||
for (int x = 0; x < config.height; ++x) {
|
||||
float r = (x / config.height) * 255;
|
||||
for (int y = 0; y < config.width; ++y) {
|
||||
float g = (y / config.height) * 255;
|
||||
for (int z = 0; z < config.depth; ++z) {
|
||||
float b = (z / config.height) * 255;
|
||||
pos.push_back(Vec3f(x,y,z));
|
||||
colors.push_back(Vec4ui8(r, g, b, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
grid.bulkAddObjects(pos,colors);
|
||||
return grid;
|
||||
}
|
||||
|
||||
void Preview(AnimationConfig config, Grid3& grid) {
|
||||
TIME_FUNCTION;
|
||||
|
||||
frame rgbData = grid.getGridAsFrame(Vec2(config.width, config.height), Ray3(Vec3f(config.width + 10,config.height + 10,config.depth + 10), Vec3f(0)), frame::colormap::RGB);
|
||||
std::cout << "Frame looks like: " << rgbData << std::endl;
|
||||
bool success = BMPWriter::saveBMP("output/grayscalesource3d.bmp", rgbData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void livePreview(const Grid3& grid, AnimationConfig config) {
|
||||
// std::lock_guard<std::mutex> lock(previewMutex);
|
||||
|
||||
// currentPreviewFrame = grid.getGridAsFrame(Vec2(config.width, config.height), Ray3(Vec3f(config.width + 10,config.height + 10,config.depth + 10), Vec3f(0)), frame::colormap::RGBA);
|
||||
|
||||
// glGenTextures(1, &textu);
|
||||
// glBindTexture(GL_TEXTURE_2D, textu);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
// glBindTexture(GL_TEXTURE_2D, textu);
|
||||
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
// 0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
|
||||
// updatePreview = true;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<size_t, Vec3f, Vec4ui8>> pickSeeds(Grid3& grid, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
// std::cout << "picking seeds" << std::endl;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_int_distribution<> zDist(0, config.depth - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<std::tuple<size_t, Vec3f, Vec4ui8>> seeds;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
Vec3f point(xDist(gen), yDist(gen), zDist(gen));
|
||||
Vec4ui8 color(colorDist(gen), colorDist(gen), colorDist(gen), 255);
|
||||
bool foundValidPos;
|
||||
int maxTries = 0;
|
||||
while (!foundValidPos && maxTries < 10) {
|
||||
maxTries++;
|
||||
//size_t id = grid.getPositionVec(point, 0.5);
|
||||
size_t id = grid.getOrCreatePositionVec(point, 0.0, true);
|
||||
if (id > 0) foundValidPos = true;
|
||||
grid.setColor(id, color);
|
||||
seeds.push_back(std::make_tuple(id,point, color));
|
||||
}
|
||||
}
|
||||
std::cout << "picked seeds" << std::endl;
|
||||
return seeds;
|
||||
}
|
||||
|
||||
void expandPixel(Grid3& grid, AnimationConfig config, std::vector<std::tuple<size_t, Vec3f, Vec4ui8>>& seeds) {
|
||||
TIME_FUNCTION;
|
||||
std::cout << "expanding pixel" << std::endl;
|
||||
std::vector<std::tuple<size_t, Vec3f, Vec4ui8>> newseeds;
|
||||
|
||||
int counter = 0;
|
||||
std::unordered_set<size_t> visitedThisFrame;
|
||||
for (const auto& seed : seeds) {
|
||||
visitedThisFrame.insert(std::get<0>(seed));
|
||||
}
|
||||
|
||||
//std::cout << "counter at: " << counter++ << std::endl;
|
||||
for (const std::tuple<size_t, Vec3f, Vec4ui8>& seed : seeds) {
|
||||
size_t id = std::get<0>(seed);
|
||||
Vec3f seedPOS = std::get<1>(seed);
|
||||
Vec4ui8 seedColor = std::get<2>(seed);
|
||||
std::vector<size_t> neighbors = grid.getNeighbors(id);
|
||||
for (size_t neighbor : neighbors) {
|
||||
std::cout << "counter at 1: " << counter++ << std::endl;
|
||||
if (visitedThisFrame.count(neighbor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3f neipos;
|
||||
try {
|
||||
neipos = grid.getPositionID(neighbor);
|
||||
} catch (const std::out_of_range& e) {
|
||||
continue;
|
||||
}
|
||||
Vec4ui8 neighborColor;
|
||||
try {
|
||||
neighborColor = grid.getColor(neighbor);
|
||||
} catch (const std::out_of_range& e) {
|
||||
// If color doesn't exist, use default or skip
|
||||
continue;
|
||||
}
|
||||
visitedThisFrame.insert(neighbor);
|
||||
|
||||
// Vec3f neipos = grid.getPositionID(neighbor);
|
||||
// Vec4 neighborColor = grid.getColor(neighbor);
|
||||
float distance = seedPOS.distance(neipos);
|
||||
float angle = seedPOS.directionTo(neipos);
|
||||
|
||||
float normalizedAngle = (angle + M_PI) / (2.0f * M_PI);
|
||||
float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI);
|
||||
blendFactor = std::clamp(blendFactor, 0.1f, 0.9f);
|
||||
//std::cout << "counter at 2: " << counter++ << std::endl;
|
||||
Vec4ui8 newcolor = Vec4ui8(
|
||||
seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor),
|
||||
seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor,
|
||||
seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)),
|
||||
1.0f
|
||||
);
|
||||
|
||||
newcolor = newcolor.clamp(0.0f, 1.0f);
|
||||
|
||||
grid.setColor(neighbor, newcolor);
|
||||
newseeds.emplace_back(neighbor, neipos, newcolor);
|
||||
std::cout << "counter at 3: " << counter++ << std::endl;
|
||||
}
|
||||
}
|
||||
seeds.clear();
|
||||
seeds.shrink_to_fit();
|
||||
seeds = std::move(newseeds);
|
||||
//std::cout << "expanded pixel" << std::endl;
|
||||
}
|
||||
|
||||
//bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
|
||||
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation3d.avi";
|
||||
|
||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
||||
|
||||
// Log compression statistics for all frames
|
||||
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
|
||||
size_t totalOriginalSize = 0;
|
||||
size_t totalCompressedSize = 0;
|
||||
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
totalOriginalSize += frames[i].getSourceSize();
|
||||
totalCompressedSize += frames[i].getTotalCompressedSize();
|
||||
}
|
||||
|
||||
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
|
||||
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
|
||||
|
||||
std::cout << "\n=== Overall Compression Summary ===" << std::endl;
|
||||
std::cout << "Total frames: " << frames.size() << std::endl;
|
||||
std::cout << "Compressed frames: " << frames.size() << std::endl;
|
||||
std::cout << "Total original size: " << totalOriginalSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Total compressed size: " << totalCompressedSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl;
|
||||
std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl;
|
||||
|
||||
std::filesystem::path dir = "output";
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
if (!std::filesystem::create_directories(dir)) {
|
||||
std::cout << "Failed to create output directory!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
|
||||
|
||||
if (success) {
|
||||
// Check if file actually exists
|
||||
if (std::filesystem::exists(filename)) {
|
||||
auto file_size = std::filesystem::file_size(filename);
|
||||
std::cout << "\nAVI file created successfully: " << filename
|
||||
<< " (" << file_size << " bytes, "
|
||||
<< std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Failed to save AVI file!" << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) {
|
||||
TIME_FUNCTION;
|
||||
isGenerating = true;
|
||||
try {
|
||||
Grid3 grid;
|
||||
if (gradnoise == 0) {
|
||||
grid = setup(config);
|
||||
} else if (gradnoise == 1) {
|
||||
grid = grid.noiseGenGrid(Vec3f(0, 0, 0), Vec3f(config.height, config.width, config.depth), 0.01, 1.0, true, config.noisemod);
|
||||
}
|
||||
grid.setDefault(Vec4ui8(0,0,0,0));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = 0;
|
||||
}
|
||||
std::cout << "generated grid" << std::endl;
|
||||
Preview(config, grid);
|
||||
std::cout << "generated preview" << std::endl;
|
||||
std::vector<std::tuple<size_t, Vec3f, Vec4ui8>> seeds = pickSeeds(grid, config);
|
||||
std::vector<frame> frames;
|
||||
|
||||
for (int i = 0; i < config.totalFrames; ++i){
|
||||
// Check if we should stop the generation
|
||||
if (!isGenerating) {
|
||||
std::cout << "Generation cancelled at frame " << i << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//expandPixel(grid,config,seeds);
|
||||
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = i;
|
||||
|
||||
//if (i % 10 == 0 ) {
|
||||
frame bgrframe;
|
||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||
bgrframe = grid.getGridAsFrame(Vec2(config.width,config.height), Ray3(Vec3f(config.width + 10,config.height + 10,config.depth + 10), Vec3f(0)), frame::colormap::BGR);
|
||||
frames.push_back(bgrframe);
|
||||
// BMPWriter::saveBMP(std::format("output/grayscalesource3d.{}.bmp", i), bgrframe);
|
||||
bgrframe.compressFrameLZ78();
|
||||
//bgrframe.printCompressionStats();
|
||||
//}
|
||||
}
|
||||
exportavi(frames,config);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "errored at: " << e.what() << std::endl;
|
||||
}
|
||||
isGenerating = false;
|
||||
}
|
||||
|
||||
// Function to cancel ongoing generation
|
||||
void cancelGeneration() {
|
||||
if (isGenerating) {
|
||||
isGenerating = false;
|
||||
// Wait for the thread to finish (with timeout to avoid hanging)
|
||||
if (generationFuture.valid()) {
|
||||
auto status = generationFuture.wait_for(std::chrono::milliseconds(100));
|
||||
if (status != std::future_status::ready) {
|
||||
std::cout << "Waiting for generation thread to finish..." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
int main() {
|
||||
//static bool window = true;
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(__APPLE__)
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||
#endif
|
||||
//ImGui::SetNextWindowSize(ImVec2(1110,667));
|
||||
//auto beg = ImGui::Begin("Gradient thing", &window);
|
||||
//if (beg) {
|
||||
// std::cout << "stuff breaks at 223" << std::endl;
|
||||
bool application_not_closed = true;
|
||||
//float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
|
||||
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr);
|
||||
if (window == nullptr)
|
||||
return 1;
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
//IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring.
|
||||
ImGui::CreateContext();
|
||||
// std::cout << "context created" << std::endl;
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
//style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
|
||||
//style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it.
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
||||
|
||||
// std::cout << "created glfw window" << std::endl;
|
||||
|
||||
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
static float f = 30.0f;
|
||||
static int iHeight = 256;
|
||||
static int iWidth = 256;
|
||||
static int iDepth = 256;
|
||||
static int i3 = 60;
|
||||
static int i4 = 8;
|
||||
static int noisemod = 42;
|
||||
static float fs = 1.0;
|
||||
|
||||
std::future<void> mainlogicthread;
|
||||
Shared state;
|
||||
Grid3 grid;
|
||||
AnimationConfig config;
|
||||
previewText = "Please generate";
|
||||
int gradnoise = true;
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
|
||||
ImGui::Begin("settings");
|
||||
|
||||
ImGui::SliderFloat("fps", &f, 20.0f, 60.0f);
|
||||
ImGui::SliderInt("width", &iHeight, 64, 4096);
|
||||
ImGui::SliderInt("height", &iWidth, 64, 4096);
|
||||
ImGui::SliderInt("depth", &iDepth, 64, 4096);
|
||||
ImGui::SliderInt("frame count", &i3, 10, 1024);
|
||||
ImGui::SliderInt("number of Seeds", &i4, 1, 10);
|
||||
ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000);
|
||||
ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0);
|
||||
ImGui::RadioButton("Gradient", &gradnoise, 0);
|
||||
ImGui::RadioButton("Perlin Noise", &gradnoise, 1);
|
||||
|
||||
if (isGenerating) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Generate Animation")) {
|
||||
config = AnimationConfig(iHeight, iWidth, iDepth, i3, f, i4, noisemod);
|
||||
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
|
||||
}
|
||||
|
||||
if (isGenerating && textu != 0) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid, config);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else if (isGenerating) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid, config);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
} else if (textu != 0){
|
||||
//ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else {
|
||||
ImGui::Text("No preview available");
|
||||
ImGui::Text("Start generation to see live preview");
|
||||
}
|
||||
//std::cout << "sleeping" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
//std::cout << "ending" << std::endl;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
// std::cout << "ending frame" << std::endl;
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// std::cout << "rendering" << std::endl;
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
//mainlogicthread.join();
|
||||
|
||||
// std::cout << "swapping buffers" << std::endl;
|
||||
}
|
||||
cancelGeneration();
|
||||
|
||||
|
||||
// std::cout << "shutting down" << std::endl;
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
// std::cout << "destroying" << std::endl;
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
// std::cout << "printing" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
285
tests/g3test.cpp
285
tests/g3test.cpp
@@ -1,285 +0,0 @@
|
||||
#include "../util/grid/grid3.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/noise/pnoise2.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
// Separate component storage
|
||||
class ComponentManager {
|
||||
private:
|
||||
std::unordered_map<size_t, Vec4ui8> colors_;
|
||||
std::unordered_map<size_t, float> sizes_;
|
||||
|
||||
public:
|
||||
void set_color(size_t id, const Vec4ui8& color) { colors_[id] = color; }
|
||||
Vec4ui8 get_color(size_t id) const {
|
||||
auto it = colors_.find(id);
|
||||
return it != colors_.end() ? it->second : Vec4ui8(1, 1, 1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Generate noise-based positions
|
||||
std::vector<Vec3f> generate_noise_positions(int width, int height, float scale = 0.01f, float threshold = 0.3f) {
|
||||
std::vector<Vec3f> positions;
|
||||
PNoise2 noise_generator(std::random_device{}());
|
||||
|
||||
// Generate positions based on 2D noise
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
// Convert to normalized coordinates
|
||||
float nx = static_cast<float>(x) / width;
|
||||
float ny = static_cast<float>(y) / height;
|
||||
|
||||
// Generate noise at multiple octaves for more interesting patterns
|
||||
float noise_value = 0.0f;
|
||||
float amplitude = 1.0f;
|
||||
float frequency = 1.0f;
|
||||
float max_value = 0.0f;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Vec2 sample_point(nx * frequency * scale, ny * frequency * scale);
|
||||
noise_value += amplitude * noise_generator.permute(sample_point);
|
||||
max_value += amplitude;
|
||||
amplitude *= 0.5f;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
noise_value = (noise_value / max_value + 1.0f) * 0.5f; // Normalize to [0, 1]
|
||||
|
||||
// Threshold to create sparse distribution
|
||||
if (noise_value > threshold) {
|
||||
// Map to world coordinates [-512, 512]
|
||||
float world_x = (nx * 2.0f - 1.0f) * 512.0f;
|
||||
float world_y = (ny * 2.0f - 1.0f) * 512.0f;
|
||||
float world_z = noise_value * 100.0f; // Use noise value for height
|
||||
|
||||
positions.emplace_back(world_x, world_y, world_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
// Generate 3D noise-based positions (more varied in 3D space)
|
||||
std::vector<Vec3f> generate_3d_noise_positions(int grid_size, float scale = 0.02f, float threshold = 0.4f) {
|
||||
std::vector<Vec3f> positions;
|
||||
PNoise2 noise_generator(std::random_device{}());
|
||||
int sizehalf = grid_size / 2;
|
||||
for (int x = 0; x < grid_size; x++) {
|
||||
for (int y = 0; y < grid_size; y++) {
|
||||
for (int z = 0; z < grid_size; z++) {
|
||||
// Convert to normalized coordinates
|
||||
float nx = static_cast<float>(x) / sizehalf;
|
||||
float ny = static_cast<float>(y) / sizehalf;
|
||||
float nz = static_cast<float>(z) / sizehalf;
|
||||
|
||||
// Generate 3D noise
|
||||
Vec3f sample_point(nx, ny, nz);
|
||||
float noise_value = noise_generator.permute(sample_point);
|
||||
|
||||
// Threshold to create sparse distribution
|
||||
if (noise_value > threshold) {
|
||||
// Map to world coordinates [-512, 512]
|
||||
float world_x = (x - sizehalf);
|
||||
float world_y = (y - sizehalf);
|
||||
float world_z = (z - sizehalf);
|
||||
|
||||
positions.emplace_back(world_x, world_y, world_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
// Generate gradient color based on position
|
||||
Vec4ui8 get_gradient_color(const Vec3f& pos, const Vec3f& min_pos, const Vec3f& max_pos) {
|
||||
// Normalize position to [0, 1]
|
||||
Vec3f normalized(
|
||||
(pos.x - min_pos.x) / (max_pos.x - min_pos.x),
|
||||
(pos.y - min_pos.y) / (max_pos.y - min_pos.y),
|
||||
(pos.z - min_pos.z) / (max_pos.z - min_pos.z)
|
||||
);
|
||||
|
||||
// Create RGB gradient based on normalized coordinates
|
||||
uint8_t r = static_cast<uint8_t>(normalized.x * 255);
|
||||
uint8_t g = static_cast<uint8_t>(normalized.y * 255);
|
||||
uint8_t b = static_cast<uint8_t>(normalized.z * 255);
|
||||
|
||||
return Vec4ui8(r, g, b, 255);
|
||||
}
|
||||
|
||||
// Generate noise-based color
|
||||
Vec4ui8 get_noise_color(const Vec3f& pos, PNoise2& noise_generator) {
|
||||
// Use noise to generate color
|
||||
Vec3f sample_point(pos.x * 0.005f, pos.y * 0.005f, pos.z * 0.005f);
|
||||
float noise_r = noise_generator.permute(sample_point);
|
||||
noise_r = (noise_r + 1.0f) * 0.5f; // Normalize to [0, 1]
|
||||
|
||||
sample_point = Vec3f(pos.x * 0.007f, pos.y * 0.007f, pos.z * 0.007f);
|
||||
float noise_g = noise_generator.permute(sample_point);
|
||||
noise_g = (noise_g + 1.0f) * 0.5f;
|
||||
|
||||
sample_point = Vec3f(pos.x * 0.009f, pos.y * 0.009f, pos.z * 0.009f);
|
||||
float noise_b = noise_generator.permute(sample_point);
|
||||
noise_b = (noise_b + 1.0f) * 0.5f;
|
||||
|
||||
uint8_t r = static_cast<uint8_t>(noise_r * 255);
|
||||
uint8_t g = static_cast<uint8_t>(noise_g * 255);
|
||||
uint8_t b = static_cast<uint8_t>(noise_b * 255);
|
||||
|
||||
return Vec4ui8(r, g, b, 255);
|
||||
}
|
||||
|
||||
// Function to save frame with given filename
|
||||
bool save_frame(const frame& f, const std::string& filename) {
|
||||
return BMPWriter::saveBMP(filename, f);
|
||||
}
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
void ensure_directory(const std::string& path) {
|
||||
std::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Create output directory
|
||||
ensure_directory("output");
|
||||
|
||||
// Create pure spatial grid
|
||||
Grid3 grid(10.0f, 15.0f); // Larger cell size for sparse distribution
|
||||
|
||||
// Separate component storage
|
||||
ComponentManager components;
|
||||
|
||||
// Generate noise-based positions
|
||||
std::cout << "Generating noise-based positions..." << std::endl;
|
||||
|
||||
// Option 1: 2D noise (faster)
|
||||
// auto positions = generate_noise_positions(200, 200, 0.02f, 0.4f);
|
||||
|
||||
// Option 2: 3D noise (more interesting, but slower)
|
||||
auto positions = generate_3d_noise_positions(1024, 0.05f, 0.5f);
|
||||
|
||||
std::cout << "Generated " << positions.size() << " positions" << std::endl;
|
||||
|
||||
// Add positions to grid
|
||||
std::cout << "Adding positions to grid..." << std::endl;
|
||||
std::vector<size_t> ids = grid.bulk_add_positions(positions);
|
||||
|
||||
// Generate bounds for color gradient
|
||||
auto bounds = grid.get_bounds();
|
||||
Vec3f min_pos = bounds.first;
|
||||
Vec3f max_pos = bounds.second;
|
||||
|
||||
// Create noise generator for colors
|
||||
PNoise2 color_noise_generator(42); // Fixed seed for consistent colors
|
||||
|
||||
// Assign colors to components
|
||||
std::cout << "Assigning colors..." << std::endl;
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
Vec3f pos = grid.get_position(ids[i]);
|
||||
|
||||
// Choose color method:
|
||||
// Vec4ui8 color = get_gradient_color(pos, min_pos, max_pos);
|
||||
Vec4ui8 color = get_noise_color(pos, color_noise_generator);
|
||||
|
||||
components.set_color(ids[i], color);
|
||||
}
|
||||
|
||||
// Query neighbors for first few points (optional)
|
||||
if (!ids.empty()) {
|
||||
auto neighbors = grid.get_neighbors(ids[0]);
|
||||
std::cout << "First point has " << neighbors.size() << " neighbors" << std::endl;
|
||||
}
|
||||
|
||||
// Region to render (full noise map area)
|
||||
Vec3f render_min(-512, -512, -512);
|
||||
Vec3f render_max(512, 512, 512);
|
||||
Vec2 resolution(1024, 1024); // Higher resolution for better detail
|
||||
|
||||
// Define multiple view angles
|
||||
struct ViewAngle {
|
||||
std::string name;
|
||||
Vec3f eye_position;
|
||||
Vec3f look_at;
|
||||
};
|
||||
|
||||
std::vector<ViewAngle> view_angles = {
|
||||
// Orthographic views
|
||||
{"front", Vec3f(0, 0, -800), Vec3f(0, 0, 0)},
|
||||
{"back", Vec3f(0, 0, 800), Vec3f(0, 0, 0)},
|
||||
{"left", Vec3f(-800, 0, 0), Vec3f(0, 0, 0)},
|
||||
{"right", Vec3f(800, 0, 0), Vec3f(0, 0, 0)},
|
||||
{"top", Vec3f(0, 800, 0), Vec3f(0, 0, 0)},
|
||||
{"bottom", Vec3f(0, -800, 0), Vec3f(0, 0, 0)},
|
||||
|
||||
// 45° angle views
|
||||
{"front_right_top", Vec3f(800, 800, -800), Vec3f(0, 0, 0)},
|
||||
{"front_left_top", Vec3f(-800, 800, -800), Vec3f(0, 0, 0)},
|
||||
{"front_right_bottom", Vec3f(800, -800, -800), Vec3f(0, 0, 0)},
|
||||
{"front_left_bottom", Vec3f(-800, -800, -800), Vec3f(0, 0, 0)},
|
||||
|
||||
{"back_right_top", Vec3f(800, 800, 800), Vec3f(0, 0, 0)},
|
||||
{"back_left_top", Vec3f(-800, 800, 800), Vec3f(0, 0, 0)},
|
||||
{"back_right_bottom", Vec3f(800, -800, 800), Vec3f(0, 0, 0)},
|
||||
{"back_left_bottom", Vec3f(-800, -800, 800), Vec3f(0, 0, 0)},
|
||||
|
||||
// Additional interesting angles
|
||||
{"diagonal_xy", Vec3f(800, 800, 0), Vec3f(0, 0, 0)},
|
||||
{"diagonal_xz", Vec3f(800, 0, 800), Vec3f(0, 0, 0)},
|
||||
{"diagonal_yz", Vec3f(0, 800, 800), Vec3f(0, 0, 0)},
|
||||
|
||||
// Isometric-like views
|
||||
{"isometric_1", Vec3f(600, 600, 600), Vec3f(0, 0, 0)},
|
||||
{"isometric_2", Vec3f(-600, 600, 600), Vec3f(0, 0, 0)},
|
||||
{"isometric_3", Vec3f(600, -600, 600), Vec3f(0, 0, 0)},
|
||||
{"isometric_4", Vec3f(-600, -600, 600), Vec3f(0, 0, 0)}
|
||||
};
|
||||
|
||||
// Render and save from each angle
|
||||
std::cout << "Rendering from multiple angles..." << std::endl;
|
||||
int saved_count = 0;
|
||||
|
||||
for (const auto& view : view_angles) {
|
||||
// Create view ray
|
||||
Vec3f direction = (view.look_at - view.eye_position).normalized();
|
||||
Ray3<float> view_ray(view.eye_position, direction);
|
||||
|
||||
// Render the frame
|
||||
auto frame = Grid3View::render_region(
|
||||
grid,
|
||||
render_min,
|
||||
render_max,
|
||||
resolution,
|
||||
view_ray,
|
||||
[&](size_t id) { return components.get_color(id); },
|
||||
frame::colormap::RGBA,
|
||||
Vec4ui8(0, 0, 0, 255) // Black background
|
||||
);
|
||||
|
||||
// Save the rendered frame
|
||||
std::string filename = "output/view_" + view.name + ".bmp";
|
||||
bool saved = save_frame(frame, filename);
|
||||
|
||||
if (saved) {
|
||||
saved_count++;
|
||||
std::cout << "Saved: " << filename << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to save: " << filename << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nSuccessfully saved " << saved_count << " out of " << view_angles.size()
|
||||
<< " views to output/" << std::endl;
|
||||
|
||||
// Optional: Save a summary image with grid statistics
|
||||
std::cout << "\nGrid statistics:" << std::endl;
|
||||
std::cout << "Total positions: " << grid.size() << std::endl;
|
||||
std::cout << "Bounds: [" << min_pos << "] to [" << max_pos << "]" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,850 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <cmath>
|
||||
|
||||
#include "../util/grid/grid3.hpp"
|
||||
#include "../util/grid/g3_serialization.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/output/frame.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
#include "../util/noise/pnoise2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
|
||||
#include "../imgui/imgui.h"
|
||||
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
// theoretical max size: 16384. but it segfaults above 512.
|
||||
struct defaults {
|
||||
int outWidth = 512;
|
||||
int outHeight = 512;
|
||||
int gridWidth = 512;
|
||||
int gridHeight = 512;
|
||||
int gridDepth = 512;
|
||||
float fps = 30.0f;
|
||||
PNoise2 noise = PNoise2(42);
|
||||
};
|
||||
|
||||
std::mutex PreviewMutex;
|
||||
GLuint textu = 0;
|
||||
bool textureInitialized = false;
|
||||
bool updatePreview = false;
|
||||
bool previewRequested = false;
|
||||
|
||||
// Add AVI recording variables
|
||||
std::atomic<bool> isRecordingAVI{false};
|
||||
std::atomic<bool> recordingRequested{false};
|
||||
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;
|
||||
};
|
||||
|
||||
void setup(defaults config, VoxelGrid& grid) {
|
||||
TIME_FUNCTION;
|
||||
uint8_t threshold = 0.1 * 255;
|
||||
grid.resize(config.gridWidth, config.gridHeight, config.gridDepth);
|
||||
std::cout << "Generating grid of size " << config.gridWidth << "x" << config.gridHeight << "x" << config.gridDepth << std::endl;
|
||||
|
||||
size_t rValw = config.gridWidth / 64;
|
||||
size_t rValh = config.gridHeight / 64;
|
||||
size_t rVald = config.gridDepth / 64;
|
||||
size_t gValw = config.gridWidth / 32;
|
||||
size_t gValh = config.gridHeight / 32;
|
||||
size_t gVald = config.gridDepth / 32;
|
||||
size_t bValw = config.gridWidth / 16;
|
||||
size_t bValh = config.gridHeight / 16;
|
||||
size_t bVald = config.gridDepth / 16;
|
||||
size_t aValw = config.gridWidth / 8;
|
||||
size_t aValh = config.gridHeight / 8;
|
||||
size_t aVald = config.gridDepth / 8;
|
||||
|
||||
for (int z = 0; z < config.gridDepth; ++z) {
|
||||
if (z % 64 == 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) {
|
||||
uint8_t r = config.noise.permute(Vec3f(static_cast<float>(x) * rValw, static_cast<float>(y) * rValh, static_cast<float>(z) * rVald)) * 255;
|
||||
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;
|
||||
if (a > threshold) {
|
||||
grid.set(Vec3i(x, y, z), true, Vec3ui8(r,g,b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Noise grid generation complete!" << std::endl;
|
||||
grid.printStats();
|
||||
}
|
||||
|
||||
void createSphere(defaults config, VoxelGrid& grid) {
|
||||
TIME_FUNCTION;
|
||||
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);
|
||||
|
||||
// Collect all positions to set
|
||||
std::vector<Vec3i> positions;
|
||||
positions.reserve(static_cast<size_t>(4.0/3.0 * M_PI * sphereConfig.radius * sphereConfig.radius * sphereConfig.radius));
|
||||
|
||||
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) {
|
||||
positions.emplace_back(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // Process in batches to manage memory
|
||||
// if (z % 16 == 0 && !positions.empty()) {
|
||||
// grid.setBatch(positions, true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25f);
|
||||
// positions.clear();
|
||||
// }
|
||||
}
|
||||
grid.setBatch(positions, true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25f);
|
||||
positions.clear();
|
||||
|
||||
// Process any remaining positions
|
||||
if (!positions.empty()) {
|
||||
grid.setBatch(positions, true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25f);
|
||||
}
|
||||
|
||||
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.printStats();
|
||||
}
|
||||
|
||||
void livePreview(VoxelGrid& grid, defaults& config, const Camera& cam) {
|
||||
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||
updatePreview = true;
|
||||
frame currentPreviewFrame = grid.renderFrame(cam, Vec2i(config.outWidth, config.outHeight), frame::colormap::RGB);
|
||||
|
||||
glGenTextures(1, &textu);
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
|
||||
updatePreview = false;
|
||||
textureInitialized = true;
|
||||
if (isRecordingAVI) {
|
||||
std::lock_guard<std::mutex> recLock(recordingMutex);
|
||||
currentPreviewFrame.compressFrameLZ78();
|
||||
recordedFrames.push_back(currentPreviewFrame);
|
||||
}
|
||||
}
|
||||
|
||||
bool savePreview(VoxelGrid& grid, defaults& config, const Camera& cam) {
|
||||
TIME_FUNCTION;
|
||||
|
||||
// Render the view
|
||||
frame output = grid.renderFrame(cam, Vec2i(config.outWidth, config.outHeight), frame::colormap::RGB);
|
||||
//grid.renderOut(renderBuffer, width, height, cam);
|
||||
|
||||
// Save to BMP
|
||||
bool success = BMPWriter::saveBMP("output/save.bmp", output);
|
||||
//bool success = BMPWriter::saveBMP(filename, renderBuffer, width, height);
|
||||
|
||||
// if (success) {
|
||||
// std::cout << "Saved: " << filename << std::endl;
|
||||
// } else {
|
||||
// std::cout << "Failed to save: " << filename << std::endl;
|
||||
// }
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void startAVIRecording(int frameCount) {
|
||||
std::lock_guard<std::mutex> lock(recordingMutex);
|
||||
recordedFrames.clear();
|
||||
recordedFrames.reserve(frameCount);
|
||||
recordingFramesRemaining = frameCount;
|
||||
recordingRequested = true;
|
||||
}
|
||||
|
||||
void stopAndSaveAVI(defaults& config, const std::string& filename) {
|
||||
TIME_FUNCTION;
|
||||
std::lock_guard<std::mutex> lock(recordingMutex);
|
||||
|
||||
if (!recordedFrames.empty()) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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;
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(finalFilename, recordedFrames, config.outWidth, config.outHeight, config.fps);
|
||||
|
||||
if (success) {
|
||||
std::cout << "AVI saved to: " << finalFilename << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to save AVI: " << finalFilename << std::endl;
|
||||
}
|
||||
|
||||
recordedFrames.clear();
|
||||
}
|
||||
|
||||
isRecordingAVI = false;
|
||||
recordingFramesRemaining = 0;
|
||||
}
|
||||
|
||||
void saveSlices(const defaults& config, VoxelGrid& grid) {
|
||||
TIME_FUNCTION;
|
||||
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";
|
||||
BMPWriter::saveBMP(filename, frames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
int main() {
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(__APPLE__)
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||
#endif
|
||||
|
||||
bool application_not_closed = true;
|
||||
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "voxelgrid live renderer", nullptr, nullptr);
|
||||
if (window == nullptr) {
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
(void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
|
||||
#endif
|
||||
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);
|
||||
|
||||
defaults config;
|
||||
VoxelGrid grid;
|
||||
bool gridInitialized = false;
|
||||
//auto supposedGrid = VoxelGrid::deserializeFromFile("output/gridsave.ygg3");
|
||||
// if (supposedGrid) {
|
||||
// grid = std::move(*supposedGrid);
|
||||
// gridInitialized = true;
|
||||
// config.gridDepth = grid.getDepth();
|
||||
// config.gridHeight = grid.getHeight();
|
||||
// config.gridWidth = grid.getWidth();
|
||||
// }
|
||||
|
||||
|
||||
|
||||
Camera cam(Vec3f(config.gridWidth/2.0f, config.gridHeight/2.0f, config.gridDepth/2.0f), Vec3f(0,0,1), Vec3f(0,1,0), 80);
|
||||
|
||||
// Variables for camera sliders
|
||||
float camX = 0.0f;
|
||||
float camY = 0.0f;
|
||||
float camZ = 0.0f;
|
||||
float camvX = 0.f;
|
||||
float camvY = 0.f;
|
||||
float camvZ = 0.f;
|
||||
//float camYaw = 0.0f;
|
||||
//float camPitch = 0.0f;
|
||||
bool autoRotate = false; // Toggle for auto-rotation
|
||||
bool autoRotateView = false; // Toggle for auto-rotation of the view only
|
||||
float rotationSpeedX = 0.1f; // Speed for X rotation
|
||||
float rotationSpeedY = 0.07f; // Speed for Y rotation
|
||||
float rotationSpeedZ = 0.05f; // Speed for Z rotation
|
||||
float autoRotationTime = 0.0f; // Timer for auto-rotation
|
||||
Vec3f initialViewDir = Vec3f(0, 0, 1); // Initial view direction
|
||||
float rotationRadius = 255.0f; // Distance from center for rotation
|
||||
float yawSpeed = 0.5f; // Horizontal rotation speed (degrees per second)
|
||||
float pitchSpeed = 0.3f; // Vertical rotation speed
|
||||
float rollSpeed = 0.2f; // Roll rotation speed (optional)
|
||||
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();
|
||||
double accumulator = 0.0;
|
||||
|
||||
// For camera movement
|
||||
bool cameraMoved = false;
|
||||
double lastUpdateTime = glfwGetTime();
|
||||
|
||||
// AVI recording variables
|
||||
int recordingDurationFrames = 300; // 10 seconds at 30fps
|
||||
std::string aviFilename = "output/recording.avi";
|
||||
|
||||
// For frame-based timing (not real time)
|
||||
int frameCounter = 0;
|
||||
float animationTime = 0.0f;
|
||||
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
double currentTime = glfwGetTime();
|
||||
double deltaTime = currentTime - lastFrameTime;
|
||||
lastFrameTime = currentTime;
|
||||
|
||||
// Accumulate time
|
||||
accumulator += deltaTime;
|
||||
|
||||
// Limit framerate
|
||||
if (accumulator < targetFrameTime) {
|
||||
std::this_thread::sleep_for(std::chrono::duration<double>(targetFrameTime - accumulator));
|
||||
currentTime = glfwGetTime();
|
||||
accumulator = targetFrameTime;
|
||||
}
|
||||
|
||||
// Frame-based timing for animations (independent of real time)
|
||||
frameCounter++;
|
||||
animationTime = frameCounter / config.fps; // Time in seconds based on frame count
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
{
|
||||
ImGui::Begin("settings");
|
||||
|
||||
if(ImGui::CollapsingHeader("output", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::SliderInt("Width", &config.outWidth, 256, 4096);
|
||||
ImGui::SliderInt("Height", &config.outHeight, 256, 4096);
|
||||
ImGui::SliderFloat("FPS", &config.fps, 1.0f, 120.0f);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Grid Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::SliderInt("#Width", &config.gridWidth, 64, 1024);
|
||||
ImGui::SliderInt("#Height", &config.gridHeight, 64, 1024);
|
||||
ImGui::SliderInt("#Depth", &config.gridDepth, 64, 1024);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Generate Grid")) {
|
||||
setup(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;
|
||||
}
|
||||
|
||||
// Add the new green sphere button
|
||||
if (ImGui::Button("Create Green Sphere")) {
|
||||
createSphere(config, grid);
|
||||
gridInitialized = true;
|
||||
|
||||
// Reset camera to edge of grid
|
||||
camX = config.gridWidth - 1;
|
||||
camY = config.gridHeight - 1;
|
||||
camZ = config.gridDepth -1;
|
||||
|
||||
// Update camera position
|
||||
cam.posfor.origin = Vec3f(camX, camY, camZ);
|
||||
cam.posfor.direction = Vec3f(camvX, camvY, camvZ);
|
||||
|
||||
savePreview(grid, config, cam);
|
||||
cameraMoved = true;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Save Slices")) {
|
||||
saveSlices(config, grid);
|
||||
}
|
||||
|
||||
// AVI Recording Controls
|
||||
ImGui::Separator();
|
||||
ImGui::Text("AVI Recording:");
|
||||
|
||||
if (!isRecordingAVI) {
|
||||
ImGui::InputInt("Frames to Record", &recordingDurationFrames, 30, 300);
|
||||
recordingDurationFrames = std::max(30, recordingDurationFrames);
|
||||
|
||||
if (ImGui::Button("Start AVI Recording")) {
|
||||
startAVIRecording(recordingDurationFrames);
|
||||
ImGui::OpenPopup("Recording Started");
|
||||
}
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "RECORDING");
|
||||
ImGui::Text("Frames captured: %d / %d",
|
||||
recordedFrames.size(),
|
||||
recordingDurationFrames);
|
||||
|
||||
if (ImGui::Button("Stop Recording Early")) {
|
||||
isRecordingAVI = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Display camera controls
|
||||
if (gridInitialized) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Camera Controls:");
|
||||
|
||||
// Calculate max slider values based on grid size squared
|
||||
float maxSliderValueX = config.gridWidth;
|
||||
float maxSliderValueY = config.gridHeight;
|
||||
float maxSliderValueZ = config.gridDepth;
|
||||
float maxSliderValueRotation = 360.0f; // Degrees
|
||||
|
||||
ImGui::Text("Position (0 to grid size²):");
|
||||
if (ImGui::SliderFloat("Camera X", &camX, 0.0f, maxSliderValueX)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.origin.x = camX;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera Y", &camY, 0.0f, maxSliderValueY)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.origin.y = camY;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera Z", &camZ, 0.0f, maxSliderValueZ)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.origin.z = camZ;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
// ImGui::Text("Rotation (degrees):");
|
||||
// if (ImGui::SliderFloat("Yaw", &camYaw, 0.0f, maxSliderValueRotation)) {
|
||||
// cameraMoved = true;
|
||||
// // Reset and reapply rotation
|
||||
// // You might need to adjust this based on your Camera class implementation
|
||||
// cam = Camera(config.gridWidth, Vec3f(camX, camY, camZ), Vec3f(0,1,0), 80);
|
||||
// cam.rotateYaw(camYaw);
|
||||
// cam.rotatePitch(camPitch);
|
||||
// }
|
||||
// if (ImGui::SliderFloat("Pitch", &camPitch, 0.0f, maxSliderValueRotation)) {
|
||||
// cameraMoved = true;
|
||||
// // Reset and reapply rotation
|
||||
// cam = Camera(config.gridWidth, Vec3f(camX, camY, camZ), Vec3f(0,1,0), 80);
|
||||
// cam.rotateYaw(camYaw);
|
||||
// cam.rotatePitch(camPitch);
|
||||
// }
|
||||
ImGui::Text("View Direction:");
|
||||
if (ImGui::SliderFloat("Camera View X", &camvX, -1.0f, 1.0f)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.direction.x = camvX;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera View Y", &camvY, -1.0f, 1.0f)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.direction.y = camvY;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera View Z", &camvZ, -1.0f, 1.0f)) {
|
||||
cameraMoved = true;
|
||||
cam.posfor.direction.z = camvZ;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Current Camera Position:");
|
||||
ImGui::Text("X: %.2f, Y: %.2f, Z: %.2f",
|
||||
cam.posfor.origin.x,
|
||||
cam.posfor.origin.y,
|
||||
cam.posfor.origin.z);
|
||||
// ImGui::Text("Yaw: %.2f°, Pitch: %.2f°", camYaw, camPitch);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
{
|
||||
ImGui::Begin("Preview");
|
||||
|
||||
if (gridInitialized && textureInitialized) {
|
||||
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
||||
} else if (gridInitialized) {
|
||||
ImGui::Text("Preview not generated yet");
|
||||
} else {
|
||||
ImGui::Text("No grid generated");
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Auto-rotation controls
|
||||
{
|
||||
ImGui::Begin("Animation Controls");
|
||||
|
||||
ImGui::Text("Auto-Rotation:");
|
||||
|
||||
// Toggle button for auto-rotation
|
||||
if (ImGui::Button(autoRotate ? "Stop Auto-Rotation" : "Start Auto-Rotation")) {
|
||||
autoRotate = !autoRotate;
|
||||
if (autoRotate) {
|
||||
autoRotationTime = 0.0f;
|
||||
initialViewDir = Vec3f(camvX, camvY, camvZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button(autoRotateView ? "Stop Looking Around" : "Start Looking Around")) {
|
||||
autoRotateView = !autoRotateView;
|
||||
if (autoRotateView) {
|
||||
autoRotationAngle = 0.0f;
|
||||
initialViewDir = Vec3f(camvX, camvY, camvZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (autoRotate) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("(Running)");
|
||||
|
||||
// Use frame-based timing for animation
|
||||
float frameTime = 1.0f / config.fps;
|
||||
autoRotationTime += frameTime; // Use constant frame time, not real time
|
||||
|
||||
// Calculate new view direction using frame-based timing
|
||||
float angleX = autoRotationTime * rotationSpeedX;
|
||||
float angleY = autoRotationTime * rotationSpeedY;
|
||||
float angleZ = autoRotationTime * rotationSpeedZ;
|
||||
|
||||
camvX = sinf(angleX) * cosf(angleY);
|
||||
camvY = sinf(angleY) * sinf(angleZ);
|
||||
camvZ = cosf(angleX) * cosf(angleZ);
|
||||
|
||||
// Normalize
|
||||
float length = sqrtf(camvX * camvX + camvY * camvY + camvZ * camvZ);
|
||||
if (length > 0.001f) {
|
||||
camvX /= length;
|
||||
camvY /= length;
|
||||
camvZ /= length;
|
||||
}
|
||||
|
||||
// Update camera position
|
||||
camX = config.gridWidth / 2.0f + rotationRadius * camvX;
|
||||
camY = config.gridHeight / 2.0f + rotationRadius * camvY;
|
||||
camZ = config.gridDepth / 2.0f + rotationRadius * camvZ;
|
||||
|
||||
// Update camera
|
||||
cam.posfor.origin = Vec3f(camX, camY, camZ);
|
||||
cam.posfor.direction = Vec3f(camvX, camvY, camvZ);
|
||||
|
||||
cameraMoved = true;
|
||||
|
||||
// Sliders to control rotation speeds
|
||||
ImGui::SliderFloat("X Speed", &rotationSpeedX, 0.01f, 1.0f);
|
||||
ImGui::SliderFloat("Y Speed", &rotationSpeedY, 0.01f, 1.0f);
|
||||
ImGui::SliderFloat("Z Speed", &rotationSpeedZ, 0.01f, 1.0f);
|
||||
|
||||
// Slider for orbit radius
|
||||
ImGui::SliderFloat("Orbit Radius", &rotationRadius, 10.0f, 200.0f);
|
||||
}
|
||||
|
||||
if (autoRotateView) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), " ACTIVE");
|
||||
|
||||
// Use frame-based timing
|
||||
float frameTime = 1.0f / config.fps;
|
||||
autoRotationAngle += frameTime;
|
||||
|
||||
// Calculate rotation angles using frame-based timing
|
||||
float yaw = autoRotationAngle * yawSpeed * (3.14159f / 180.0f);
|
||||
float pitch = sinf(autoRotationAngle * 0.7f) * pitchSpeed * (3.14159f / 180.0f);
|
||||
|
||||
// Apply rotations
|
||||
Vec3f forward = initialViewDir;
|
||||
|
||||
// Yaw rotation (around Y axis)
|
||||
float cosYaw = cosf(yaw);
|
||||
float sinYaw = sinf(yaw);
|
||||
Vec3f tempForward;
|
||||
tempForward.x = forward.x * cosYaw + forward.z * sinYaw;
|
||||
tempForward.y = forward.y;
|
||||
tempForward.z = -forward.x * sinYaw + forward.z * cosYaw;
|
||||
forward = tempForward;
|
||||
|
||||
// Pitch rotation (around X axis)
|
||||
float cosPitch = cosf(pitch);
|
||||
float sinPitch = sinf(pitch);
|
||||
tempForward.x = forward.x;
|
||||
tempForward.y = forward.y * cosPitch - forward.z * sinPitch;
|
||||
tempForward.z = forward.y * sinPitch + forward.z * cosPitch;
|
||||
forward = tempForward;
|
||||
|
||||
// Normalize
|
||||
float length = sqrtf(forward.x * forward.x + forward.y * forward.y + forward.z * forward.z);
|
||||
if (length > 0.001f) {
|
||||
forward.x /= length;
|
||||
forward.y /= length;
|
||||
forward.z /= length;
|
||||
}
|
||||
|
||||
// Update view direction
|
||||
camvX = forward.x;
|
||||
camvY = forward.y;
|
||||
camvZ = forward.z;
|
||||
|
||||
// Update camera
|
||||
cam.posfor.direction = Vec3f(camvX, camvY, camvZ);
|
||||
|
||||
cameraMoved = true;
|
||||
|
||||
// Show current view direction
|
||||
ImGui::Text("Current View: (%.3f, %.3f, %.3f)", camvX, camvY, camvZ);
|
||||
|
||||
// Sliders to control rotation speeds
|
||||
ImGui::SliderFloat("Yaw Speed", &yawSpeed, 0.1f, 5.0f, "%.2f deg/sec");
|
||||
ImGui::SliderFloat("Pitch Speed", &pitchSpeed, 0.0f, 2.0f, "%.2f deg/sec");
|
||||
}
|
||||
|
||||
// Record button during animations
|
||||
if ((autoRotate || autoRotateView) && !isRecordingAVI) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Record Animation to AVI")) {
|
||||
startAVIRecording(recordingDurationFrames);
|
||||
}
|
||||
}
|
||||
|
||||
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 / 255, sphereConfig.g / 255, sphereConfig.b / 255};
|
||||
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;
|
||||
recordingRequested = false;
|
||||
}
|
||||
|
||||
// Check if recording should stop
|
||||
if (isRecordingAVI && recordedFrames.size() >= recordingDurationFrames) {
|
||||
stopAndSaveAVI(config, aviFilename);
|
||||
ImGui::OpenPopup("Recording Complete");
|
||||
}
|
||||
|
||||
// Update preview if camera moved or enough time has passed
|
||||
if (gridInitialized && !updatePreview) {
|
||||
double timeSinceLastUpdate = currentTime - lastUpdateTime;
|
||||
|
||||
// Update preview if needed
|
||||
if (cameraMoved || timeSinceLastUpdate > 0.1) {
|
||||
livePreview(grid, config, cam);
|
||||
lastUpdateTime = currentTime;
|
||||
cameraMoved = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset accumulator for next frame
|
||||
accumulator -= targetFrameTime;
|
||||
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "../util/grid/grid33.hpp"
|
||||
//#include "../util/grid/treexy/treexy_serialization.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/noise/pnoise2.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
struct configuration {
|
||||
float threshold = 0.1;
|
||||
int gridWidth = 128;
|
||||
int gridHeight = 128;
|
||||
int gridDepth = 128;
|
||||
PNoise2 noise = PNoise2(42);
|
||||
};
|
||||
|
||||
void setup(configuration& config, VoxelGrid<Vec3ui8, 2, 3>& grid) {
|
||||
TIME_FUNCTION;
|
||||
uint8_t thresh = config.threshold * 255;
|
||||
for (int z = 0; z < config.gridDepth; ++z) {
|
||||
if (z % 64 == 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) {
|
||||
uint8_t r = std::clamp(config.noise.permute(Vec3f(static_cast<float>(x) / config.gridWidth / 64, static_cast<float>(y) / config.gridHeight / 64, static_cast<float>(z) / config.gridDepth / 64)), 0.f, 1.f) * 255;
|
||||
uint8_t g = std::clamp(config.noise.permute(Vec3f(static_cast<float>(x) / config.gridWidth / 32, static_cast<float>(y) / config.gridHeight / 32, static_cast<float>(z) / config.gridDepth / 32)), 0.f, 1.f) * 255;
|
||||
uint8_t b = std::clamp(config.noise.permute(Vec3f(static_cast<float>(x) / config.gridWidth / 16, static_cast<float>(y) / config.gridHeight / 16, static_cast<float>(z) / config.gridDepth / 16)), 0.f, 1.f) * 255;
|
||||
uint8_t a = std::clamp(config.noise.permute(Vec3f(static_cast<float>(x) / config.gridWidth / 8 , static_cast<float>(y) / config.gridHeight / 8 , static_cast<float>(z) / config.gridDepth / 8 )), 0.f, 1.f) * 255;
|
||||
if (a > thresh) {
|
||||
bool wasOn = grid.setVoxelColor(Vec3d(x,y,z), Vec3ui8(r,g,b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize random number generator
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> pos_dist(-1.0, 1.0);
|
||||
std::uniform_int_distribution<> color_dist(0, 255);
|
||||
|
||||
// Create a voxel grid with 0.1 unit resolution
|
||||
VoxelGrid<Vec3ui8, 2, 3> voxelGrid(0.1);
|
||||
|
||||
configuration config;
|
||||
setup(config, voxelGrid);
|
||||
|
||||
std::cout << "\nMemory usage: " << voxelGrid.getMemoryUsage() << " bytes" << std::endl;
|
||||
|
||||
// Render to BMP
|
||||
std::cout << "\nRendering orthographic projection to BMP..." << std::endl;
|
||||
|
||||
int width = 512;
|
||||
int height = 512;
|
||||
|
||||
// Create a buffer for the rendered image
|
||||
std::vector<uint8_t> imageBuffer;
|
||||
|
||||
// Render with orthographic projection (view along Z axis)
|
||||
Vec3d camPos = Vec3d(config.gridDepth, config.gridHeight, config.gridWidth * 2);
|
||||
Vec3d lookAt = Vec3d(config.gridDepth / 2, config.gridHeight / 2, config.gridWidth / 2);
|
||||
Vec3d viewDir = (lookAt-camPos).normalized();
|
||||
voxelGrid.renderToRGB(imageBuffer, width, height, camPos, viewDir, Vec3d(0, 1, 0), 80.f);
|
||||
|
||||
std::cout << "Image buffer size: " << imageBuffer.size() << " bytes" << std::endl;
|
||||
std::cout << "Expected size: " << (width * height * 3) << " bytes" << std::endl;
|
||||
|
||||
// Save to BMP using BMPWriter
|
||||
std::string filename = "output/voxel_render.bmp";
|
||||
|
||||
// Create a frame object from the buffer
|
||||
frame renderFrame(width, height, frame::colormap::RGB);
|
||||
renderFrame.setData(imageBuffer);
|
||||
|
||||
// Save as BMP
|
||||
if (BMPWriter::saveBMP(filename, renderFrame)) {
|
||||
std::cout << "Successfully saved to: " << filename << std::endl;
|
||||
|
||||
} else {
|
||||
std::cout << "Failed to save BMP!" << std::endl;
|
||||
}
|
||||
|
||||
// Test accessor functionality
|
||||
std::cout << "\nTesting accessor functionality..." << std::endl;
|
||||
auto accessor = voxelGrid.createAccessor();
|
||||
|
||||
// Try to retrieve one of the voxels
|
||||
Vec3d testPos(0.0, 0.0, 0.0); // Center point
|
||||
Vec3i testCoord = voxelGrid.posToCoord(testPos);
|
||||
|
||||
Vec3ui8* retrievedColor = voxelGrid.getVoxelColor(testPos);
|
||||
if (retrievedColor) {
|
||||
std::cout << "Found voxel at center: Color(RGB:"
|
||||
<< static_cast<int>(retrievedColor->x) << ","
|
||||
<< static_cast<int>(retrievedColor->y) << ","
|
||||
<< static_cast<int>(retrievedColor->z) << ")" << std::endl;
|
||||
} else {
|
||||
std::cout << "No voxel found at center position" << std::endl;
|
||||
}
|
||||
|
||||
// Iterate through all voxels using forEachCell
|
||||
std::cout << "\nIterating through all voxels:" << std::endl;
|
||||
int voxelCount = 0;
|
||||
voxelGrid.forEachCell([&](const Vec3ui8& color, const Vec3i& coord) {
|
||||
Vec3d pos = voxelGrid.Vec3iToPos(coord);
|
||||
std::cout << "Voxel " << ++voxelCount << ": "
|
||||
<< "Coord(" << coord.x << ", " << coord.y << ", " << coord.z << ") "
|
||||
<< "WorldPos(" << pos.x << ", " << pos.y << ", " << pos.z << ") "
|
||||
<< "Color(RGB:" << static_cast<int>(color.x) << ","
|
||||
<< static_cast<int>(color.y) << "," << static_cast<int>(color.z) << ")"
|
||||
<< std::endl;
|
||||
});
|
||||
|
||||
std::cout << "\nTotal voxels in grid: " << voxelCount << std::endl;
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
#include "../imgui/imgui.h"
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int numSeeds = 8;
|
||||
};
|
||||
|
||||
Grid2 setup(AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
Grid2 grid;
|
||||
std::vector<Vec2> pos;
|
||||
std::vector<Vec4> colors;
|
||||
std::vector<float> sizes;
|
||||
for (int y = 0; y < config.height; ++y) {
|
||||
for (int x = 0; x < config.width; ++x) {
|
||||
float gradient = (x + y) / float(config.width + config.height - 2);
|
||||
pos.push_back(Vec2(x,y));
|
||||
colors.push_back(Vec4(gradient, gradient, gradient, 1.0f));
|
||||
sizes.push_back(1.0f);
|
||||
}
|
||||
}
|
||||
grid.bulkAddObjects(pos,colors,sizes);
|
||||
return grid;
|
||||
}
|
||||
|
||||
void Preview(Grid2 grid) {
|
||||
TIME_FUNCTION;
|
||||
int width;
|
||||
int height;
|
||||
//std::vector<uint8_t> rgbData;
|
||||
|
||||
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
|
||||
bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> pickSeeds(Grid2 grid, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> xDist(0, config.width - 1);
|
||||
std::uniform_int_distribution<> yDist(0, config.height - 1);
|
||||
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
|
||||
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds;
|
||||
|
||||
for (int i = 0; i < config.numSeeds; ++i) {
|
||||
Vec2 point(xDist(gen), yDist(gen));
|
||||
Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255);
|
||||
size_t id = grid.getPositionVec(point);
|
||||
grid.setColor(id, color);
|
||||
seeds.push_back(std::make_tuple(id,point, color));
|
||||
}
|
||||
return seeds;
|
||||
}
|
||||
|
||||
void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<size_t, Vec2, Vec4>>& seeds) {
|
||||
TIME_FUNCTION;
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
|
||||
|
||||
std::unordered_set<size_t> visitedThisFrame;
|
||||
for (const auto& seed : seeds) {
|
||||
visitedThisFrame.insert(std::get<0>(seed));
|
||||
}
|
||||
|
||||
//#pragma omp parallel for
|
||||
for (const std::tuple<size_t, Vec2, Vec4>& seed : seeds) {
|
||||
size_t id = std::get<0>(seed);
|
||||
Vec2 seedPOS = std::get<1>(seed);
|
||||
Vec4 seedColor = std::get<2>(seed);
|
||||
std::vector<size_t> neighbors = grid.getNeighbors(id);
|
||||
//grid.setSize(id, grid.getSize(id)+4);
|
||||
for (size_t neighbor : neighbors) {
|
||||
if (visitedThisFrame.count(neighbor)) {
|
||||
continue;
|
||||
}
|
||||
visitedThisFrame.insert(neighbor);
|
||||
|
||||
Vec2 neipos = grid.getPositionID(neighbor);
|
||||
Vec4 neighborColor = grid.getColor(neighbor);
|
||||
float distance = seedPOS.distance(neipos);
|
||||
float angle = seedPOS.directionTo(neipos);
|
||||
|
||||
float normalizedAngle = (angle + M_PI) / (2.0f * M_PI);
|
||||
float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI);
|
||||
blendFactor = std::clamp(blendFactor, 0.1f, 0.9f);
|
||||
|
||||
Vec4 newcolor = Vec4(
|
||||
seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor),
|
||||
seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor,
|
||||
seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)),
|
||||
1.0f
|
||||
);
|
||||
|
||||
newcolor = newcolor.clamp(0.0f, 1.0f);
|
||||
|
||||
grid.setColor(neighbor, newcolor);
|
||||
newseeds.emplace_back(neighbor, neipos, newcolor);
|
||||
}
|
||||
}
|
||||
seeds.clear();
|
||||
seeds.shrink_to_fit();
|
||||
seeds = std::move(newseeds);
|
||||
}
|
||||
|
||||
//bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
|
||||
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
|
||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
||||
|
||||
// Log compression statistics for all frames
|
||||
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
|
||||
size_t totalOriginalSize = 0;
|
||||
size_t totalCompressedSize = 0;
|
||||
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
totalOriginalSize += frames[i].getSourceSize();
|
||||
totalCompressedSize += frames[i].getTotalCompressedSize();
|
||||
}
|
||||
|
||||
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
|
||||
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
|
||||
|
||||
std::cout << "\n=== Overall Compression Summary ===" << std::endl;
|
||||
std::cout << "Total frames: " << frames.size() << std::endl;
|
||||
std::cout << "Compressed frames: " << frames.size() << std::endl;
|
||||
std::cout << "Total original size: " << totalOriginalSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Total compressed size: " << totalCompressedSize << " bytes ("
|
||||
<< std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl;
|
||||
std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl;
|
||||
|
||||
std::filesystem::path dir = "output";
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
if (!std::filesystem::create_directories(dir)) {
|
||||
std::cout << "Failed to create output directory!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
|
||||
|
||||
if (success) {
|
||||
// Check if file actually exists
|
||||
if (std::filesystem::exists(filename)) {
|
||||
auto file_size = std::filesystem::file_size(filename);
|
||||
std::cout << "\nAVI file created successfully: " << filename
|
||||
<< " (" << file_size << " bytes, "
|
||||
<< std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Failed to save AVI file!" << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mainLogic(){
|
||||
AnimationConfig config;
|
||||
// std::cout << "g2c2175" << std::endl;
|
||||
|
||||
Grid2 grid = setup(config);
|
||||
// std::cout << "g2c2178" << std::endl;
|
||||
Preview(grid);
|
||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid,config);
|
||||
std::vector<frame> frames;
|
||||
|
||||
for (int i = 0; i < config.totalFrames; ++i){
|
||||
expandPixel(grid,config,seeds);
|
||||
|
||||
// Print compression info for this frame
|
||||
if (i % 10 == 0 ) {
|
||||
frame bgrframe;
|
||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||
bgrframe = grid.getGridAsFrame(frame::colormap::BGR);
|
||||
bgrframe.printCompressionStats();
|
||||
//(bgrframe, i + 1);
|
||||
frames.push_back(bgrframe);
|
||||
//bgrframe.decompress();
|
||||
//BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe);
|
||||
bgrframe.compressFrameLZ78();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exportavi(frames,config);
|
||||
}
|
||||
|
||||
int main() {
|
||||
static bool window = true;
|
||||
ImGui::SetNextWindowSize(ImVec2(1110,667));
|
||||
auto beg = ImGui::Begin("Gradient thing", &window);
|
||||
if (beg) {
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(435.5,200));
|
||||
ImGui::PushItemWidth(200);
|
||||
static int i1 = 123;
|
||||
ImGui::InputInt("Width", &i1);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(432,166));
|
||||
ImGui::PushItemWidth(200);
|
||||
static int i2 = 123;
|
||||
ImGui::InputInt("Height", &i2);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(533.5,271));
|
||||
ImGui::Button("Start", ImVec2(43,19)); //remove size argument (ImVec2) to auto-resize
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(400.5,366));
|
||||
ImGui::PushItemWidth(200);
|
||||
static int i6 = 123;
|
||||
ImGui::InputInt("number of Seeds", &i6);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -1,423 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include "../util/grid/grid2.hpp"
|
||||
#include "../util/output/aviwriter.hpp"
|
||||
#include "../util/output/bmpwriter.hpp"
|
||||
#include "../util/timing_decorator.cpp"
|
||||
|
||||
#include "../imgui/imgui.h"
|
||||
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI = 3.1415
|
||||
#endif
|
||||
|
||||
std::mutex m;
|
||||
std::atomic<bool> isGenerating{false};
|
||||
std::future<void> generationFuture;
|
||||
|
||||
std::mutex previewMutex;
|
||||
std::atomic<bool> updatePreview{false};
|
||||
frame currentPreviewFrame;
|
||||
GLuint textu = 0;
|
||||
std::string previewText;
|
||||
|
||||
struct Shared {
|
||||
std::mutex mutex;
|
||||
Grid2 grid;
|
||||
bool hasNewFrame = false;
|
||||
int currentFrame = 0;
|
||||
};
|
||||
|
||||
struct AnimationConfig {
|
||||
int width = 1024;
|
||||
int height = 1024;
|
||||
int totalFrames = 480;
|
||||
float fps = 30.0f;
|
||||
int noisemod = 42;
|
||||
};
|
||||
|
||||
void Preview(Grid2& grid) {
|
||||
TIME_FUNCTION;
|
||||
int width;
|
||||
int height;
|
||||
//std::vector<uint8_t> rgbData;
|
||||
|
||||
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
|
||||
std::cout << "Frame looks like: " << rgbData << std::endl;
|
||||
bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData);
|
||||
if (!success) {
|
||||
std::cout << "yo! this failed in Preview" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void livePreview(const Grid2& grid) {
|
||||
std::lock_guard<std::mutex> lock(previewMutex);
|
||||
|
||||
currentPreviewFrame = grid.getGridAsFrame(frame::colormap::RGBA);
|
||||
|
||||
glGenTextures(1, &textu);
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
|
||||
updatePreview = true;
|
||||
}
|
||||
|
||||
void flowWater(Grid2& grid, AnimationConfig config) {
|
||||
|
||||
}
|
||||
|
||||
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
|
||||
TIME_FUNCTION;
|
||||
std::string filename = "output/chromatic_transformation.avi";
|
||||
|
||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
||||
|
||||
// Log compression statistics for all frames
|
||||
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
|
||||
size_t totalOriginalSize = 0;
|
||||
size_t totalCompressedSize = 0;
|
||||
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
totalOriginalSize += frames[i].getSourceSize();
|
||||
totalCompressedSize += frames[i].getTotalCompressedSize();
|
||||
}
|
||||
|
||||
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
|
||||
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
|
||||
|
||||
std::filesystem::path dir = "output";
|
||||
if (!std::filesystem::exists(dir)) {
|
||||
if (!std::filesystem::create_directories(dir)) {
|
||||
std::cout << "Failed to create output directory!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
|
||||
|
||||
if (!success) {
|
||||
std::cout << "Failed to save AVI file!" << std::endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) {
|
||||
TIME_FUNCTION;
|
||||
isGenerating = true;
|
||||
try {
|
||||
Grid2 grid;
|
||||
if (gradnoise == 1) {
|
||||
grid = grid.noiseGenGrid(0,0,config.height, config.width, 0.0, 1.0, false, config.noisemod);
|
||||
}
|
||||
grid.setDefault(Vec4(0,0,0,0));
|
||||
{
|
||||
std:: lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = 0;
|
||||
}
|
||||
std::cout << "generated grid" << std::endl;
|
||||
Preview(grid);
|
||||
std::cout << "generated preview" << std::endl;
|
||||
std::vector<frame> frames;
|
||||
|
||||
for (int i = 0; i < config.totalFrames; ++i){
|
||||
// Check if we should stop the generation
|
||||
if (!isGenerating) {
|
||||
std::cout << "Generation cancelled at frame " << i << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
flowWater(grid,config);
|
||||
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
state.grid = grid;
|
||||
state.hasNewFrame = true;
|
||||
state.currentFrame = i;
|
||||
|
||||
// Print compression info for this frame
|
||||
if (i % 10 == 0 ) {
|
||||
frame bgrframe;
|
||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||
bgrframe = grid.getGridAsFrame(frame::colormap::BGR);
|
||||
frames.push_back(bgrframe);
|
||||
//bgrframe.decompress();
|
||||
//BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe);
|
||||
bgrframe.compressFrameLZ78();
|
||||
//bgrframe.printCompressionStats();
|
||||
}
|
||||
}
|
||||
exportavi(frames,config);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "errored at: " << e.what() << std::endl;
|
||||
}
|
||||
isGenerating = false;
|
||||
}
|
||||
|
||||
// Function to cancel ongoing generation
|
||||
void cancelGeneration() {
|
||||
if (isGenerating) {
|
||||
isGenerating = false;
|
||||
// Wait for the thread to finish (with timeout to avoid hanging)
|
||||
if (generationFuture.valid()) {
|
||||
auto status = generationFuture.wait_for(std::chrono::milliseconds(100));
|
||||
if (status != std::future_status::ready) {
|
||||
std::cout << "Waiting for generation thread to finish..." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
int main() {
|
||||
//static bool window = true;
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
#elif defined(__APPLE__)
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||
#endif
|
||||
//ImGui::SetNextWindowSize(ImVec2(1110,667));
|
||||
//auto beg = ImGui::Begin("Gradient thing", &window);
|
||||
//if (beg) {
|
||||
// std::cout << "stuff breaks at 223" << std::endl;
|
||||
bool application_not_closed = true;
|
||||
//float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
|
||||
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr);
|
||||
if (window == nullptr)
|
||||
return 1;
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
//IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring.
|
||||
ImGui::CreateContext();
|
||||
// std::cout << "context created" << std::endl;
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
//style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
|
||||
//style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it.
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
||||
|
||||
// std::cout << "created glfw window" << std::endl;
|
||||
|
||||
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
static float f = 30.0f;
|
||||
static int i1 = 1024;
|
||||
static int i2 = 1024;
|
||||
static int i3 = 480;
|
||||
static int noisemod = 42;
|
||||
static float fs = 1.0;
|
||||
|
||||
std::future<void> mainlogicthread;
|
||||
Shared state;
|
||||
Grid2 grid;
|
||||
AnimationConfig config;
|
||||
previewText = "Please generate";
|
||||
int gradnoise = true;
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
{
|
||||
|
||||
ImGui::Begin("settings");
|
||||
|
||||
ImGui::SliderFloat("fps", &f, 20.0f, 60.0f);
|
||||
ImGui::SliderInt("width", &i1, 256, 4096);
|
||||
ImGui::SliderInt("height", &i2, 256, 4096);
|
||||
ImGui::SliderInt("frame count", &i3, 10, 5000);
|
||||
ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000);
|
||||
ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0);
|
||||
ImGui::RadioButton("Gradient", &gradnoise, 0);
|
||||
ImGui::RadioButton("Perlin Noise", &gradnoise, 1);
|
||||
|
||||
if (isGenerating) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Generate Animation")) {
|
||||
config = AnimationConfig(i1, i2, i3, f, noisemod);
|
||||
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
|
||||
}
|
||||
|
||||
if (isGenerating && textu != 0) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else if (isGenerating) {
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
cancelGeneration();
|
||||
}
|
||||
// Check for new frames from the generation thread
|
||||
bool hasNewFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state.mutex);
|
||||
if (state.hasNewFrame) {
|
||||
livePreview(state.grid);
|
||||
state.hasNewFrame = false;
|
||||
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
} else if (textu != 0){
|
||||
//ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text(previewText.c_str());
|
||||
|
||||
if (textu != 0) {
|
||||
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
|
||||
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
|
||||
} else {
|
||||
ImGui::Text("Generating preview...");
|
||||
}
|
||||
|
||||
} else {
|
||||
ImGui::Text("No preview available");
|
||||
ImGui::Text("Start generation to see live preview");
|
||||
}
|
||||
//std::cout << "sleeping" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
//std::cout << "ending" << std::endl;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
// std::cout << "ending frame" << std::endl;
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// std::cout << "rendering" << std::endl;
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
//mainlogicthread.join();
|
||||
|
||||
// std::cout << "swapping buffers" << std::endl;
|
||||
}
|
||||
cancelGeneration();
|
||||
|
||||
|
||||
// std::cout << "shutting down" << std::endl;
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
// std::cout << "destroying" << std::endl;
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
// std::cout << "printing" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
//I need this: https://raais.github.io/ImStudio/
|
||||
// g++ -std=c++23 -O3 -march=native -o ./bin/g2gradc ./tests/g2chromatic2.cpp -I./imgui -L./imgui -limgui -lstb `pkg-config --cflags --libs glfw3` && ./bin/g2gradc
|
||||
Reference in New Issue
Block a user