Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d96e569c8 | ||
|
|
31fb9ffedb | ||
|
|
4478bc1845 | ||
|
|
83989e955e | ||
|
|
494ee931df | ||
|
|
d36d00bc13 | ||
|
|
a0afac9c18 | ||
|
|
565646e13e | ||
|
|
14158fae83 | ||
|
|
4f0dba5eb4 | ||
|
|
926ffe18cd | ||
|
|
b4a7f536bc | ||
|
|
06d5b383e5 | ||
|
|
18aa8f06b7 | ||
|
|
fdd5553d20 | ||
|
|
e99bbc08af | ||
|
|
c7eb5739f4 | ||
|
|
2c993995e8 | ||
|
|
cb0b8b7643 | ||
|
|
dc36b93e4f | ||
|
|
2768b6849e | ||
|
|
4f227df1d7 | ||
|
|
14b4d06495 | ||
|
|
d6d638cb01 | ||
|
|
a1e4a852be | ||
|
|
af4e6ebe3f | ||
|
|
6b9362219e | ||
|
|
e402e712b4 | ||
|
|
1ba5611672 | ||
|
|
d07325932c | ||
|
|
2db9575bd1 | ||
|
|
798506c300 | ||
|
|
8a564f51ad | ||
|
|
5198650926 | ||
|
|
ca226d8c02 | ||
|
|
02361effc5 | ||
|
|
d6d4a30798 | ||
|
|
73eaa63dfb | ||
|
|
17e4cdf1a5 | ||
|
|
47b264d016 | ||
|
|
39c38e4061 | ||
|
|
341066a0cf | ||
|
|
b84c5f141e | ||
|
|
62adc8575b | ||
|
|
d51f6ba3da | ||
|
|
e54c524f26 | ||
|
|
88ee3732b6 | ||
|
|
9b902cebd7 | ||
|
|
d719d0a922 | ||
|
|
3f88fdd14e | ||
|
|
58d90e9cd7 | ||
|
|
2006fd5f57 | ||
|
|
9a87eeb355 | ||
|
|
662da70665 | ||
|
|
ce526bebc9 | ||
|
|
33c27fd5b7 | ||
|
|
9b81d0f5f8 | ||
|
|
c8aa34dcda | ||
|
|
c6038722e5 | ||
|
|
820cc1f873 | ||
|
|
c282acd725 | ||
|
|
670ff42b82 | ||
|
|
44068ba32c | ||
|
|
aaa7b1e24e | ||
|
|
f24fcaa691 | ||
|
|
4febc51784 | ||
|
|
19462868ea | ||
|
|
e5e0c5b838 | ||
|
|
d45df35101 | ||
|
|
c0d15a0b9f | ||
|
|
e0764318b4 | ||
|
|
c3916d146a | ||
|
|
ed6b625826 | ||
|
|
e7ce32b266 | ||
|
|
c77d3b25ba | ||
|
|
4f409cedc5 | ||
|
|
9c0be89a8b | ||
|
|
65d36cc34c | ||
|
|
a453149f57 | ||
|
|
842da7a507 | ||
|
|
5d18ff0199 | ||
|
|
5ef07c6af5 | ||
|
|
0aeed604a7 | ||
|
|
dc514cfe31 | ||
|
|
ff3e4711db | ||
|
|
63a21b103f | ||
|
|
76e0d4e06a | ||
|
|
a4e378bbcd | ||
|
|
c52c6b14b9 | ||
|
|
a0b9fab5f9 | ||
|
|
58555f96c8 | ||
|
|
840a3f1517 | ||
|
|
f91998e839 | ||
|
|
db423030dd | ||
|
|
0782189838 | ||
|
|
310b4233be | ||
|
|
835ac45c9a | ||
|
|
a914e7a1f7 | ||
|
|
1ae8514e01 | ||
|
|
a4e8581e99 | ||
|
|
b820c89bd0 | ||
|
|
81af30d4c6 | ||
|
|
0cb71a4799 | ||
|
|
acba629774 | ||
|
|
4dfe6c9a5e | ||
|
|
71777dc135 | ||
|
|
c39a3beeff | ||
|
|
175fb19da1 | ||
|
|
40c761edb7 | ||
|
|
082682a339 | ||
|
|
2870be6cf3 | ||
|
|
3a183e0f92 | ||
|
|
5d614e6737 | ||
|
|
b1cffb9a54 | ||
|
|
1a73679f4e | ||
|
|
4a98b10189 | ||
|
|
1c9958711d | ||
|
|
cdd1ef5e8e | ||
|
|
044c059c1d | ||
|
|
2e5d2b150a | ||
|
|
a8a528beda | ||
|
|
1aa81ce65f | ||
|
|
65f2464c70 | ||
|
|
7c569afd46 | ||
|
|
d9aff085a8 | ||
|
|
b74ba3cfdc | ||
|
|
f347637b77 | ||
|
|
264f5d9496 | ||
|
|
466fa26dc7 | ||
|
|
57e9834772 | ||
|
|
7bc711a3ad | ||
|
|
a9caaeb40f | ||
|
|
c92b5dc203 | ||
|
|
aed793b6fe | ||
|
|
17975b58a9 | ||
|
|
02115dcfc0 | ||
|
|
05f709c00b | ||
|
|
4cb1bd2530 | ||
|
|
bc55db2c74 | ||
|
|
1a4ad39642 | ||
|
|
82a10cb2c5 | ||
|
|
cd60918540 | ||
|
|
ce27939e4e | ||
|
|
7252c655a2 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,3 +2,6 @@
|
|||||||
/bin/
|
/bin/
|
||||||
/web/output/
|
/web/output/
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
#prototyping in python below
|
||||||
|
*.py
|
||||||
|
/venv/
|
||||||
|
|||||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -4,3 +4,7 @@
|
|||||||
[submodule "stb"]
|
[submodule "stb"]
|
||||||
path = stb
|
path = stb
|
||||||
url = https://github.com/nothings/stb
|
url = https://github.com/nothings/stb
|
||||||
|
|
||||||
|
[submodule "eigen"]
|
||||||
|
path = eigen
|
||||||
|
url = https://gitlab.com/libeigen/eigen.git
|
||||||
|
|||||||
1
eigen
Submodule
1
eigen
Submodule
Submodule eigen added at fdfdd4c96b
114
h.cpp
Normal file
114
h.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "util/grid/gridtest.hpp"
|
||||||
|
#include <random>
|
||||||
|
#include <memory>
|
||||||
|
#include "util/output/bmpwriter.hpp"
|
||||||
|
|
||||||
|
// Function to create a randomized voxel grid
|
||||||
|
std::unique_ptr<VoxelGrid> createRandomizedGrid(int width, int height, int depth, float density = 0.2f) {
|
||||||
|
auto grid = std::make_unique<VoxelGrid>(width, height, depth);
|
||||||
|
|
||||||
|
// Set up random number generation
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 gen(rd());
|
||||||
|
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
|
||||||
|
std::uniform_real_distribution<float> colorDist(0.2f, 1.0f);
|
||||||
|
|
||||||
|
// Fill grid with random active voxels
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int z = 0; z < depth; z++) {
|
||||||
|
if (dist(gen) < density) {
|
||||||
|
// Random color
|
||||||
|
Vec3f color(colorDist(gen), colorDist(gen), colorDist(gen));
|
||||||
|
grid->set(x, y, z, true, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a simple structure to hold camera position and orientation
|
||||||
|
struct CameraPose {
|
||||||
|
Vec3f position;
|
||||||
|
Vec3f target;
|
||||||
|
Vec3f up;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Create a randomized voxel grid (15x15x15 with 20% density)
|
||||||
|
const int gridSize = 128;
|
||||||
|
auto grid = createRandomizedGrid(gridSize, gridSize, gridSize, 0.2f);
|
||||||
|
|
||||||
|
// Create a renderer with our custom grid
|
||||||
|
VoxelRenderer renderer;
|
||||||
|
renderer.getGrid() = *grid; // Replace the default test pattern
|
||||||
|
|
||||||
|
// Define 6 camera positions (looking at the center from each axis direction)
|
||||||
|
std::vector<CameraPose> cameraPoses = {
|
||||||
|
// Front (looking along negative Z)
|
||||||
|
{Vec3f(0, 0, -gridSize * 2), Vec3f(0, 0, 0), Vec3f(0, 1, 0)},
|
||||||
|
|
||||||
|
// Back (looking along positive Z)
|
||||||
|
{Vec3f(0, 0, gridSize * 2), Vec3f(0, 0, 0), Vec3f(0, 1, 0)},
|
||||||
|
|
||||||
|
// Right (looking along positive X)
|
||||||
|
{Vec3f(gridSize * 2, 0, 0), Vec3f(0, 0, 0), Vec3f(0, 1, 0)},
|
||||||
|
|
||||||
|
// Left (looking along negative X)
|
||||||
|
{Vec3f(-gridSize * 2, 0, 0), Vec3f(0, 0, 0), Vec3f(0, 1, 0)},
|
||||||
|
|
||||||
|
// Top (looking along negative Y)
|
||||||
|
{Vec3f(0, gridSize * 2, 0), Vec3f(0, 0, 0), Vec3f(0, 0, -1)},
|
||||||
|
|
||||||
|
// Bottom (looking along positive Y)
|
||||||
|
{Vec3f(0, -gridSize * 2, 0), Vec3f(0, 0, 0), Vec3f(0, 0, 1)}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render settings
|
||||||
|
const int screenWidth = 400;
|
||||||
|
const int screenHeight = 400;
|
||||||
|
|
||||||
|
// Vector to store all frames
|
||||||
|
std::vector<frame> frames;
|
||||||
|
|
||||||
|
std::cout << "Rendering voxel grid from 6 directions..." << std::endl;
|
||||||
|
|
||||||
|
// Render from each camera position
|
||||||
|
for (size_t i = 0; i < cameraPoses.size(); i++) {
|
||||||
|
auto& pose = cameraPoses[i];
|
||||||
|
|
||||||
|
// Update camera position
|
||||||
|
renderer.getCamera().position = pose.position;
|
||||||
|
|
||||||
|
// Calculate forward direction (from position to target)
|
||||||
|
Vec3f forward = (pose.target - pose.position).normalized();
|
||||||
|
|
||||||
|
// Set camera orientation
|
||||||
|
renderer.getCamera().forward = forward;
|
||||||
|
renderer.getCamera().up = pose.up;
|
||||||
|
|
||||||
|
std::cout << "Rendering view " << (i+1) << "/6 from position ("
|
||||||
|
<< pose.position.x << ", " << pose.position.y << ", " << pose.position.z << ")" << std::endl;
|
||||||
|
|
||||||
|
// Render and add to frames vector
|
||||||
|
frames.push_back(renderer.renderToFrame(screenWidth, screenHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print frame information
|
||||||
|
std::cout << "\nRendering complete!" << std::endl;
|
||||||
|
std::cout << "Number of frames created: " << frames.size() << std::endl;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < frames.size(); i++) {
|
||||||
|
std::cout << "Frame " << (i+1) << ": " << frames[i] << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Save frames to files if your frame class supports it
|
||||||
|
for (size_t i = 0; i < frames.size(); i++) {
|
||||||
|
std::string filename = "output/voxel_view_" + std::to_string(i+1) + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, frames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
2
imgui
2
imgui
Submodule imgui updated: 23bd697f05...814c6a194b
32
makefile
32
makefile
@@ -7,17 +7,35 @@ STB_DIR := ./stb
|
|||||||
|
|
||||||
# Compiler and flags
|
# Compiler and flags
|
||||||
CXX := g++
|
CXX := g++
|
||||||
CXXFLAGS = -std=c++23 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(STB_DIR)
|
BASE_CXXFLAGS = -std=c++23 -O3 -fopenmp -march=native -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(STB_DIR) -g
|
||||||
CXXFLAGS += `pkg-config --cflags glfw3`
|
BASE_CXXFLAGS += `pkg-config --cflags glfw3`
|
||||||
CFLAGS = $(CXXFLAGS)
|
CFLAGS = $(BASE_CXXFLAGS)
|
||||||
LDFLAGS := -L./imgui -limgui -lGL
|
LDFLAGS := -L./imgui -limgui -lGL
|
||||||
LINUX_GL_LIBS = -lGL -ltbb
|
LINUX_GL_LIBS = -lGL -ltbb
|
||||||
PKG_FLAGS := $(LINUX_GL_LIBS) `pkg-config --static --cflags --libs glfw3`
|
PKG_FLAGS := $(LINUX_GL_LIBS) `pkg-config --static --cflags --libs glfw3`
|
||||||
CXXFLAGS += $(PKG_FLAGS)
|
BASE_CXXFLAGS += $(PKG_FLAGS)
|
||||||
|
|
||||||
|
# Test if AVX is supported (run once, store result)
|
||||||
|
AVX_SUPPORTED := $(shell echo "int main(){}" | $(CXX) -mavx -x c++ -o /dev/null - 2>/dev/null && echo "yes" || echo "no")
|
||||||
|
SSE2_SUPPORTED := $(shell echo "int main(){}" | $(CXX) -msse2 -x c++ -o /dev/null - 2>/dev/null && echo "yes" || echo "no")
|
||||||
|
|
||||||
|
# Set SIMD flags based on detection
|
||||||
|
ifeq ($(AVX_SUPPORTED),yes)
|
||||||
|
SIMD_CXXFLAGS = -mavx2 -mfma -DAVX
|
||||||
|
$(info Building with AVX support)
|
||||||
|
else ifeq ($(SSE2_SUPPORTED),yes)
|
||||||
|
SIMD_CXXFLAGS = -msse2 -DSSE
|
||||||
|
$(info Building with SSE2 support (no AVX))
|
||||||
|
else
|
||||||
|
SIMD_CXXFLAGS = -DNO_SIMD
|
||||||
|
$(warning No SIMD support detected, building scalar version)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS)
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
#SRC := $(SRC_DIR)/g3chromatic.cpp
|
SRC := $(SRC_DIR)/ptest.cpp
|
||||||
SRC := $(SRC_DIR)/g2atomic.cpp
|
#SRC := $(SRC_DIR)/g2chromatic2.cpp
|
||||||
SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||||
SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
||||||
SRC += $(SRC_DIR)/stb_image.cpp
|
SRC += $(SRC_DIR)/stb_image.cpp
|
||||||
@@ -43,7 +61,7 @@ $(OBJ_DIR)/%.o: $(STB_DIR)/%.cpp
|
|||||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
all: $(EXE)
|
all: $(EXE)
|
||||||
@echo Build complete for $(ECHO_MESSAGE)
|
@echo "Build complete for $(UNAME_S)"
|
||||||
|
|
||||||
$(EXE): $(OBJS)
|
$(EXE): $(OBJS)
|
||||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
||||||
|
|||||||
1
readme.md
Normal file
1
readme.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Thanks to Treexy, Amanatides, Woo, Occam, Incf, and so on.
|
||||||
170
tests/fluidsim.cpp
Normal file
170
tests/fluidsim.cpp
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#ifndef FLUIDSIM_CPP
|
||||||
|
#define FLUIDSIM_CPP
|
||||||
|
|
||||||
|
#include "../util/sim/fluidsim.hpp"
|
||||||
|
#include "../util/timing_decorator.cpp"
|
||||||
|
#include "../util/output/bmpwriter.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"
|
||||||
|
|
||||||
|
|
||||||
|
struct fluidSimPreviewConfig {
|
||||||
|
float lodDist = 4096.0f;
|
||||||
|
float lodDropoff = 0.001f;
|
||||||
|
bool slowRender = false;
|
||||||
|
int outWidth = 1024;
|
||||||
|
int outHeight = 1024;
|
||||||
|
int rayCount = 1;
|
||||||
|
int reflectCount = 1;
|
||||||
|
bool globalIllumination = false;
|
||||||
|
bool useLod = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FluidSimUI {
|
||||||
|
private:
|
||||||
|
fluidSim sim;
|
||||||
|
fluidParticle baseParticle;
|
||||||
|
fluidSimPreviewConfig viewConfig;
|
||||||
|
Camera cam;
|
||||||
|
|
||||||
|
// UI State
|
||||||
|
bool isRunning = false;
|
||||||
|
int particlesToSpawnPerFrame = 2;
|
||||||
|
int totalParticleCap = 5000;
|
||||||
|
|
||||||
|
// Texture Management
|
||||||
|
GLuint textu = 0;
|
||||||
|
std::mutex PreviewMutex;
|
||||||
|
bool updatePreview = false;
|
||||||
|
bool textureInitialized = false;
|
||||||
|
frame currentPreviewFrame;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FluidSimUI() {
|
||||||
|
cam.origin = Eigen::Vector3f(0.0f, 4000.0f, -5800.0f);
|
||||||
|
cam.direction = (Eigen::Vector3f(0.0f, 0.0f, 0.0f) - cam.origin).normalized();
|
||||||
|
cam.up = Eigen::Vector3f(0.0f, 1.0f, 0.0f);
|
||||||
|
cam.fov = 60.0f;
|
||||||
|
|
||||||
|
baseParticle.velocity = Eigen::Vector3f::Zero();
|
||||||
|
baseParticle.acceleration = Eigen::Vector3f::Zero();
|
||||||
|
baseParticle.forceAccumulator = Eigen::Vector3f::Zero();
|
||||||
|
baseParticle.pressure = 0.0f;
|
||||||
|
baseParticle.viscosity = 8.0f;
|
||||||
|
baseParticle.restitution = 200.0f;
|
||||||
|
|
||||||
|
sim.grid.setLODFalloff(viewConfig.lodDropoff);
|
||||||
|
sim.grid.setLODMinDistance(viewConfig.lodDist);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FluidSimUI() {
|
||||||
|
if (textu != 0) {
|
||||||
|
glDeleteTextures(1, &textu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (isRunning) {
|
||||||
|
if (sim.getParticleCount() < (size_t)totalParticleCap) {
|
||||||
|
sim.spawnParticles(baseParticle, particlesToSpawnPerFrame);
|
||||||
|
}
|
||||||
|
sim.applyPhysics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderUI() {
|
||||||
|
ImGui::Begin("Fluid Simulation Control");
|
||||||
|
|
||||||
|
if (ImGui::Button(isRunning ? "Pause" : "Start")) {
|
||||||
|
isRunning = !isRunning;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Step")) {
|
||||||
|
sim.applyPhysics();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Reset")) {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
sim.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Particle Management");
|
||||||
|
ImGui::DragInt("Spawn Rate (per frame)", &particlesToSpawnPerFrame, 1, 0, 100);
|
||||||
|
ImGui::DragInt("Max Particles", &totalParticleCap, 10, 0, 50000);
|
||||||
|
ImGui::Text("Current Particles: %zu", sim.getParticleCount());
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Physics Parameters")) {
|
||||||
|
ImGui::DragFloat("Smoothing Radius", &sim.config.SMOOTHING_RADIUS, 1.0f, 100.0f, 5000.0f);
|
||||||
|
ImGui::DragFloat("Rest Density", &sim.config.REST_DENSITY, 0.00001f, 0.0f, 0.01f, "%.6f");
|
||||||
|
ImGui::DragFloat("Timestep", &sim.config.TIMESTEP, 0.001f, 0.001f, 0.1f);
|
||||||
|
ImGui::DragFloat("Gravity (Attraction)", &sim.config.G_ATTRACTION, 0.1f, 0.0f, 500.0f);
|
||||||
|
|
||||||
|
ImGui::Text("Base Particle Properties");
|
||||||
|
ImGui::DragFloat("Viscosity", &baseParticle.viscosity, 0.1f, 0.0f, 50.0f);
|
||||||
|
ImGui::DragFloat("Restitution", &baseParticle.restitution, 1.0f, 0.0f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("View Settings")) {
|
||||||
|
ImGui::DragFloat("Camera FOV", &cam.fov, 1.0f, 10.0f, 170.0f);
|
||||||
|
|
||||||
|
float camPos[3] = {cam.origin.x(), cam.origin.y(), cam.origin.z()};
|
||||||
|
if(ImGui::DragFloat3("Camera Pos", camPos, 10.0f)) {
|
||||||
|
cam.origin = Eigen::Vector3f(camPos[0], camPos[1], camPos[2]);
|
||||||
|
cam.direction = (Eigen::Vector3f(0.0f, 0.0f, 0.0f) - cam.origin).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("High Quality Render", &viewConfig.slowRender);
|
||||||
|
if (viewConfig.slowRender) {
|
||||||
|
ImGui::DragInt("Ray Count", &viewConfig.rayCount, 1, 1, 16);
|
||||||
|
ImGui::Checkbox("Global Illumination", &viewConfig.globalIllumination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePreviewTexture();
|
||||||
|
|
||||||
|
if (textureInitialized) {
|
||||||
|
float aspect = (float)currentPreviewFrame.getWidth() / (float)currentPreviewFrame.getHeight();
|
||||||
|
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
ImGui::Image((void*)(intptr_t)textu, ImVec2(availWidth, availWidth / aspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updatePreviewTexture() {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
updatePreview = true;
|
||||||
|
|
||||||
|
sim.grid.setLODMinDistance(viewConfig.lodDist);
|
||||||
|
sim.grid.setLODFalloff(viewConfig.lodDropoff);
|
||||||
|
|
||||||
|
if (viewConfig.slowRender) {
|
||||||
|
currentPreviewFrame = sim.grid.renderFrame(cam, viewConfig.outWidth, viewConfig.outHeight, frame::colormap::RGB, viewConfig.rayCount, viewConfig.reflectCount, viewConfig.globalIllumination, viewConfig.useLod);
|
||||||
|
} else {
|
||||||
|
currentPreviewFrame = sim.grid.fastRenderFrame(cam, viewConfig.outWidth, viewConfig.outHeight, frame::colormap::RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textu == 0) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||||
|
0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||||
|
|
||||||
|
updatePreview = false;
|
||||||
|
textureInitialized = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,508 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <random>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <filesystem>
|
|
||||||
#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.14159265358979323846
|
|
||||||
#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;
|
|
||||||
float currentTime = 0.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SimulationConfig {
|
|
||||||
int width = 1024;
|
|
||||||
int height = 1024;
|
|
||||||
int totalFrames = 480;
|
|
||||||
float fps = 30.0f;
|
|
||||||
float atomDensity = 0.3f;
|
|
||||||
int simulationStepsPerFrame = 1;
|
|
||||||
|
|
||||||
// Physics parameters
|
|
||||||
float timeStep = 0.016f;
|
|
||||||
float coulombStrength = 1.0f;
|
|
||||||
float gravityStrength = 1.0e-6f;
|
|
||||||
float dampingFactor = 0.99f;
|
|
||||||
float temperature = 300.0f;
|
|
||||||
|
|
||||||
// Element distribution
|
|
||||||
float hydrogenProb = 0.4f;
|
|
||||||
float heliumProb = 0.2f;
|
|
||||||
float carbonProb = 0.15f;
|
|
||||||
float oxygenProb = 0.15f;
|
|
||||||
float ironProb = 0.1f;
|
|
||||||
|
|
||||||
// Interaction settings
|
|
||||||
bool enableCoulomb = true;
|
|
||||||
bool enableGravity = true;
|
|
||||||
bool enableFusion = true;
|
|
||||||
bool enableElectronTransfer = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Grid2 setupAtomicGrid(SimulationConfig config) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
Grid2 grid;
|
|
||||||
|
|
||||||
// Set physics parameters
|
|
||||||
grid.setPhysicsParameters(config.timeStep, config.coulombStrength,
|
|
||||||
config.gravityStrength, config.dampingFactor);
|
|
||||||
|
|
||||||
// Set simulation bounds
|
|
||||||
Vec2 minBound(0, 0);
|
|
||||||
Vec2 maxBound(config.width, config.height);
|
|
||||||
grid.setSimulationBounds(minBound, maxBound);
|
|
||||||
|
|
||||||
// Set default background (dark space)
|
|
||||||
grid.setDefault(0.0f, 0.0f, 0.1f, 1.0f);
|
|
||||||
|
|
||||||
// Generate random atoms
|
|
||||||
grid.generateRandomAtoms(0, 0, config.width, config.height, config.atomDensity);
|
|
||||||
|
|
||||||
std::cout << "Generated atomic grid with size: " << config.width << "x" << config.height << std::endl;
|
|
||||||
std::cout << "Atom density: " << config.atomDensity << std::endl;
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Preview(Grid2& grid) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
|
|
||||||
std::cout << "Frame size: " << rgbData.getWidth() << "x" << rgbData.getHeight() << std::endl;
|
|
||||||
bool success = BMPWriter::saveBMP("output/atomic_preview.bmp", rgbData);
|
|
||||||
if (!success) {
|
|
||||||
std::cout << "Failed to save preview image!" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void livePreview(const Grid2& grid) {
|
|
||||||
std::lock_guard<std::mutex> lock(previewMutex);
|
|
||||||
|
|
||||||
currentPreviewFrame = grid.getGridAsFrame(frame::colormap::RGBA);
|
|
||||||
|
|
||||||
if (textu == 0) {
|
|
||||||
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);
|
|
||||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exportavi(std::vector<frame> frames, SimulationConfig config) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
std::string filename = "output/atomic_simulation.avi";
|
|
||||||
|
|
||||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
|
||||||
|
|
||||||
// Create output directory
|
|
||||||
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) {
|
|
||||||
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 atomicSimulationLoop(const SimulationConfig& config, Shared& state) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
isGenerating = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Setup atomic grid
|
|
||||||
Grid2 grid = setupAtomicGrid(config);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(state.mutex);
|
|
||||||
state.grid = grid;
|
|
||||||
state.hasNewFrame = true;
|
|
||||||
state.currentFrame = 0;
|
|
||||||
state.currentTime = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Generated atomic grid" << std::endl;
|
|
||||||
|
|
||||||
// Get initial statistics
|
|
||||||
size_t totalAtoms, totalProtons, totalNeutrons, totalElectrons;
|
|
||||||
float totalCharge, totalMass;
|
|
||||||
grid.getStatistics(totalAtoms, totalProtons, totalNeutrons,
|
|
||||||
totalElectrons, totalCharge, totalMass);
|
|
||||||
|
|
||||||
std::cout << "Initial Statistics:" << std::endl;
|
|
||||||
std::cout << " Total atoms: " << totalAtoms << std::endl;
|
|
||||||
std::cout << " Total protons: " << totalProtons << std::endl;
|
|
||||||
std::cout << " Total neutrons: " << totalNeutrons << std::endl;
|
|
||||||
std::cout << " Total electrons: " << totalElectrons << std::endl;
|
|
||||||
std::cout << " Total charge: " << totalCharge << " e" << std::endl;
|
|
||||||
std::cout << " Total mass: " << totalMass << " kg" << std::endl;
|
|
||||||
|
|
||||||
// Save initial preview
|
|
||||||
Preview(grid);
|
|
||||||
std::cout << "Saved initial preview" << std::endl;
|
|
||||||
|
|
||||||
std::vector<frame> frames;
|
|
||||||
|
|
||||||
for (int frameNum = 0; frameNum < config.totalFrames; frameNum++) {
|
|
||||||
// Check if we should stop the generation
|
|
||||||
if (!isGenerating) {
|
|
||||||
std::cout << "Simulation cancelled at frame " << frameNum << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple simulation steps per frame for smoother physics
|
|
||||||
for (int step = 0; step < config.simulationStepsPerFrame; step++) {
|
|
||||||
// Update physics
|
|
||||||
grid.updatePhysics(config.timeStep);
|
|
||||||
|
|
||||||
// Simulate atomic interactions
|
|
||||||
grid.simulateAtomicInteractions();
|
|
||||||
|
|
||||||
// Update time
|
|
||||||
state.currentTime += config.timeStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update shared state
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(state.mutex);
|
|
||||||
state.grid = grid;
|
|
||||||
state.hasNewFrame = true;
|
|
||||||
state.currentFrame = frameNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save frame every 5 frames or at the end
|
|
||||||
if (frameNum % 5 == 0 || frameNum == config.totalFrames - 1) {
|
|
||||||
frame currentFrame = grid.getGridAsFrame(frame::colormap::BGR);
|
|
||||||
frames.push_back(currentFrame);
|
|
||||||
|
|
||||||
// Optional: Save individual frames for debugging
|
|
||||||
if (frameNum % 50 == 0) {
|
|
||||||
std::string frameFilename = "output/atomic_frame_" +
|
|
||||||
std::to_string(frameNum) + ".bmp";
|
|
||||||
BMPWriter::saveBMP(frameFilename, currentFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Processed frame " << frameNum + 1 << "/"
|
|
||||||
<< config.totalFrames << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update statistics periodically
|
|
||||||
if (frameNum % 100 == 0) {
|
|
||||||
grid.getStatistics(totalAtoms, totalProtons, totalNeutrons,
|
|
||||||
totalElectrons, totalCharge, totalMass);
|
|
||||||
|
|
||||||
std::cout << "\nFrame " << frameNum << " Statistics:" << std::endl;
|
|
||||||
std::cout << " Total atoms: " << totalAtoms << std::endl;
|
|
||||||
std::cout << " Net charge: " << totalCharge << " e" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export final video
|
|
||||||
exportavi(frames, config);
|
|
||||||
|
|
||||||
std::cout << "\nSimulation completed!" << std::endl;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error in atomic simulation: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
isGenerating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancelGeneration() {
|
|
||||||
if (isGenerating) {
|
|
||||||
isGenerating = false;
|
|
||||||
if (generationFuture.valid()) {
|
|
||||||
auto status = generationFuture.wait_for(std::chrono::milliseconds(500));
|
|
||||||
if (status != std::future_status::ready) {
|
|
||||||
std::cout << "Waiting for simulation 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() {
|
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
|
||||||
if (!glfwInit()) {
|
|
||||||
std::cerr << "Failed to initialize GLFW" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GL version setup
|
|
||||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
|
||||||
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)
|
|
||||||
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__)
|
|
||||||
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);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
||||||
#else
|
|
||||||
const char* glsl_version = "#version 130";
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Create window
|
|
||||||
GLFWwindow* window = glfwCreateWindow(1280, 800, "Atomic Simulation", nullptr, nullptr);
|
|
||||||
if (window == nullptr) {
|
|
||||||
std::cerr << "Failed to create GLFW window" << std::endl;
|
|
||||||
glfwTerminate();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(window);
|
|
||||||
glfwSwapInterval(1);
|
|
||||||
|
|
||||||
// Setup ImGui
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
||||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
|
||||||
|
|
||||||
// Simulation parameters
|
|
||||||
bool show_demo_window = true;
|
|
||||||
bool show_another_window = false;
|
|
||||||
ImVec4 clear_color = ImVec4(0.1f, 0.1f, 0.15f, 1.00f);
|
|
||||||
|
|
||||||
// Configuration variables
|
|
||||||
static SimulationConfig config;
|
|
||||||
static int simulationMode = 0; // 0: Random atoms, 1: Custom setup
|
|
||||||
static float previewScale = 0.5f;
|
|
||||||
static bool showStatistics = true;
|
|
||||||
static bool showPhysicsControls = true;
|
|
||||||
|
|
||||||
Shared state;
|
|
||||||
std::future<void> simulationThread;
|
|
||||||
previewText = "Ready to simulate";
|
|
||||||
|
|
||||||
// Main loop
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
|
||||||
glfwPollEvents();
|
|
||||||
|
|
||||||
// Start ImGui frame
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
|
||||||
ImGui_ImplGlfw_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
{
|
|
||||||
ImGui::Begin("Atomic Simulation Controls");
|
|
||||||
|
|
||||||
ImGui::Text("Simulation Settings");
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::SliderInt("Width", &config.width, 256, 4096);
|
|
||||||
ImGui::SliderInt("Height", &config.height, 256, 4096);
|
|
||||||
ImGui::SliderInt("Frame Count", &config.totalFrames, 10, 5000);
|
|
||||||
ImGui::SliderFloat("FPS", &config.fps, 10.0f, 60.0f);
|
|
||||||
ImGui::SliderFloat("Atom Density", &config.atomDensity, 0.01f, 0.8f);
|
|
||||||
ImGui::SliderInt("Steps/Frame", &config.simulationStepsPerFrame, 1, 10);
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Physics Parameters");
|
|
||||||
|
|
||||||
ImGui::SliderFloat("Time Step", &config.timeStep, 0.001f, 0.1f, "%.3f");
|
|
||||||
ImGui::SliderFloat("Coulomb Strength", &config.coulombStrength, 0.0f, 10.0f);
|
|
||||||
ImGui::SliderFloat("Gravity Strength", &config.gravityStrength, 0.0f, 1.0e-3f, "%.6f");
|
|
||||||
ImGui::SliderFloat("Damping", &config.dampingFactor, 0.9f, 1.0f);
|
|
||||||
ImGui::SliderFloat("Temperature", &config.temperature, 100.0f, 10000.0f);
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Element Distribution");
|
|
||||||
|
|
||||||
ImGui::SliderFloat("Hydrogen %", &config.hydrogenProb, 0.0f, 1.0f);
|
|
||||||
ImGui::SliderFloat("Helium %", &config.heliumProb, 0.0f, 1.0f);
|
|
||||||
ImGui::SliderFloat("Carbon %", &config.carbonProb, 0.0f, 1.0f);
|
|
||||||
ImGui::SliderFloat("Oxygen %", &config.oxygenProb, 0.0f, 1.0f);
|
|
||||||
ImGui::SliderFloat("Iron %", &config.ironProb, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
// Normalize probabilities
|
|
||||||
float total = config.hydrogenProb + config.heliumProb +
|
|
||||||
config.carbonProb + config.oxygenProb + config.ironProb;
|
|
||||||
if (total > 0) {
|
|
||||||
config.hydrogenProb /= total;
|
|
||||||
config.heliumProb /= total;
|
|
||||||
config.carbonProb /= total;
|
|
||||||
config.oxygenProb /= total;
|
|
||||||
config.ironProb /= total;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Interaction Settings");
|
|
||||||
|
|
||||||
ImGui::Checkbox("Coulomb Forces", &config.enableCoulomb);
|
|
||||||
ImGui::Checkbox("Gravity", &config.enableGravity);
|
|
||||||
ImGui::Checkbox("Fusion Reactions", &config.enableFusion);
|
|
||||||
ImGui::Checkbox("Electron Transfer", &config.enableElectronTransfer);
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::SliderFloat("Preview Scale", &previewScale, 0.1f, 2.0f);
|
|
||||||
|
|
||||||
// Simulation controls
|
|
||||||
if (isGenerating) {
|
|
||||||
ImGui::BeginDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Start Atomic Simulation")) {
|
|
||||||
simulationThread = std::async(std::launch::async, atomicSimulationLoop,
|
|
||||||
config, std::ref(state));
|
|
||||||
previewText = "Starting atomic simulation...";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGenerating) {
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Cancel Simulation")) {
|
|
||||||
cancelGeneration();
|
|
||||||
previewText = "Cancelling simulation...";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update preview from simulation thread
|
|
||||||
bool hasNewFrame = false;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(state.mutex);
|
|
||||||
if (state.hasNewFrame) {
|
|
||||||
livePreview(state.grid);
|
|
||||||
state.hasNewFrame = false;
|
|
||||||
previewText = "Simulating... Frame: " +
|
|
||||||
std::to_string(state.currentFrame) +
|
|
||||||
", Time: " + std::to_string(state.currentTime) + "s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Status: %s", previewText.c_str());
|
|
||||||
|
|
||||||
// Display preview image
|
|
||||||
if (textu != 0) {
|
|
||||||
ImVec2 imageSize = ImVec2(config.width * previewScale,
|
|
||||||
config.height * previewScale);
|
|
||||||
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("No preview available");
|
|
||||||
ImGui::Text("Start simulation to see atomic interactions");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display statistics if available
|
|
||||||
if (showStatistics) {
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Simulation Statistics");
|
|
||||||
// Statistics would be updated from the simulation thread
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render
|
|
||||||
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
|
|
||||||
cancelGeneration();
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
|
||||||
ImGui_ImplGlfw_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
|
|
||||||
if (textu != 0) {
|
|
||||||
glDeleteTextures(1, &textu);
|
|
||||||
}
|
|
||||||
|
|
||||||
glfwDestroyWindow(window);
|
|
||||||
glfwTerminate();
|
|
||||||
|
|
||||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -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<Vec4f> 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(Vec4f(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, Vec4f>> 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, Vec4f>> seeds;
|
|
||||||
|
|
||||||
for (int i = 0; i < config.numSeeds; ++i) {
|
|
||||||
Vec2 point(xDist(gen), yDist(gen));
|
|
||||||
Vec4f 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, Vec4f>>& seeds) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
std::vector<std::tuple<size_t, Vec2, Vec4f>> 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, Vec4f>& seed : seeds) {
|
|
||||||
size_t id = std::get<0>(seed);
|
|
||||||
Vec2 seedPOS = std::get<1>(seed);
|
|
||||||
Vec4f 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);
|
|
||||||
Vec4f 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);
|
|
||||||
|
|
||||||
Vec4f newcolor = Vec4f(
|
|
||||||
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(Vec4f(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, Vec4f>> 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;
|
|
||||||
}
|
|
||||||
874
tests/g3etest.cpp
Normal file
874
tests/g3etest.cpp
Normal file
@@ -0,0 +1,874 @@
|
|||||||
|
#include <map>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
|
#include "../util/output/bmpwriter.hpp"
|
||||||
|
#include "../util/output/frame.hpp"
|
||||||
|
#include "../util/timing_decorator.cpp"
|
||||||
|
#include "../util/noise/pnoise2.hpp"
|
||||||
|
#include "../util/noise/pnoise.cpp"
|
||||||
|
#include "../util/output/aviwriter.hpp"
|
||||||
|
#include "fluidsim.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"
|
||||||
|
|
||||||
|
struct defaults {
|
||||||
|
int outWidth = 512;
|
||||||
|
int outHeight = 512;
|
||||||
|
int gridSizecube = 10000;
|
||||||
|
bool slowRender = false;
|
||||||
|
bool globalIllumination = true;
|
||||||
|
bool useLod = true;
|
||||||
|
int rayCount = 5;
|
||||||
|
int reflectCount = 3;
|
||||||
|
int lodDist = 50000;
|
||||||
|
float lodDropoff = 0.1;
|
||||||
|
PNoise2 noise = PNoise2(42);
|
||||||
|
|
||||||
|
int meshResolution = 16;
|
||||||
|
float meshIsoLevel = 0.4f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct spheredefaults {
|
||||||
|
float centerX = 0.0f;
|
||||||
|
float centerY = 0.0f;
|
||||||
|
float centerZ = 0.0f;
|
||||||
|
float radius = 1024.0f;
|
||||||
|
float color[3] = {0.0f, 1.0f, 0.0f};
|
||||||
|
bool light = false;
|
||||||
|
float emittance = 0.0f;
|
||||||
|
float reflection = 0.0f;
|
||||||
|
float refraction = 0.0f;
|
||||||
|
bool fillInside = false;
|
||||||
|
float voxelSize = 10.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stardefaults {
|
||||||
|
float x = 3000.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
|
||||||
|
float color[3] = {1.0f, 0.95f, 0.8f};
|
||||||
|
float emittance = 1000.0f;
|
||||||
|
float size = 1000.0f;
|
||||||
|
bool enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::mutex PreviewMutex;
|
||||||
|
GLuint textu = 0;
|
||||||
|
bool textureInitialized = false;
|
||||||
|
bool updatePreview = false;
|
||||||
|
bool previewRequested = false;
|
||||||
|
using PointType = Eigen::Matrix<float, 3, 1>;
|
||||||
|
|
||||||
|
// Render FPS tracking variables
|
||||||
|
double renderFrameTime = 0.0;
|
||||||
|
double avgRenderFrameTime = 0.0;
|
||||||
|
double renderFPS = 0.0;
|
||||||
|
const int FRAME_HISTORY_SIZE = 60;
|
||||||
|
std::vector<double> renderFrameTimes;
|
||||||
|
int frameHistoryIndex = 0;
|
||||||
|
bool firstFrameMeasured = false;
|
||||||
|
|
||||||
|
// Stats update timer
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
const std::chrono::seconds STATS_UPDATE_INTERVAL(10);
|
||||||
|
std::string cachedStats;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
|
||||||
|
Scene scene;
|
||||||
|
bool meshNeedsUpdate = false;
|
||||||
|
|
||||||
|
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
||||||
|
if (!grid.empty()) grid.clear();
|
||||||
|
|
||||||
|
Eigen::Vector3f colorVec(sconfig.color[0], sconfig.color[1], sconfig.color[2]);
|
||||||
|
Eigen::Vector3f center(sconfig.centerX, sconfig.centerY, sconfig.centerZ);
|
||||||
|
|
||||||
|
float voxelSize = sconfig.voxelSize;
|
||||||
|
float radius = sconfig.radius;
|
||||||
|
|
||||||
|
// Calculate how many voxels fit in the diameter
|
||||||
|
int voxelsPerDiameter = static_cast<int>(2.0f * radius / voxelSize);
|
||||||
|
if (voxelsPerDiameter < 1) voxelsPerDiameter = 1;
|
||||||
|
|
||||||
|
// Create a 3D grid that covers the sphere's bounding box
|
||||||
|
for (int i = 0; i <= voxelsPerDiameter; i++) {
|
||||||
|
for (int j = 0; j <= voxelsPerDiameter; j++) {
|
||||||
|
for (int k = 0; k <= voxelsPerDiameter; k++) {
|
||||||
|
// Calculate position in the grid
|
||||||
|
float x = center.x() - radius + i * voxelSize;
|
||||||
|
float y = center.y() - radius + j * voxelSize;
|
||||||
|
float z = center.z() - radius + k * voxelSize;
|
||||||
|
|
||||||
|
Eigen::Vector3f pos(x, y, z);
|
||||||
|
|
||||||
|
// Calculate distance from center
|
||||||
|
float dist = (pos - center).norm();
|
||||||
|
|
||||||
|
// For solid sphere: include all points within radius
|
||||||
|
if (dist <= radius + voxelSize * 0.5f) {
|
||||||
|
// Optional: For better surface quality, adjust surface points
|
||||||
|
if (dist > radius - voxelSize * 0.5f) {
|
||||||
|
// This is a surface voxel, adjust to exactly on surface
|
||||||
|
if (dist > 0.001f) {
|
||||||
|
pos = center + (pos - center).normalized() * radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x() >= 0 && pos.x() < config.gridSizecube &&
|
||||||
|
pos.y() >= 0 && pos.y() < config.gridSizecube &&
|
||||||
|
pos.z() >= 0 && pos.z() < config.gridSizecube) {
|
||||||
|
|
||||||
|
grid.set(1, pos, true, colorVec, voxelSize, true, 1,
|
||||||
|
sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we want a truly solid sphere without gaps, we need a second pass
|
||||||
|
if (sconfig.fillInside) {
|
||||||
|
// Scan for potential gaps in the interior
|
||||||
|
int interiorSteps = static_cast<int>(radius / voxelSize);
|
||||||
|
float interiorStep = voxelSize * 0.5f; // Half-step for gap checking
|
||||||
|
|
||||||
|
for (int i = 0; i <= interiorSteps * 2; i++) {
|
||||||
|
for (int j = 0; j <= interiorSteps * 2; j++) {
|
||||||
|
for (int k = 0; k <= interiorSteps * 2; k++) {
|
||||||
|
Eigen::Vector3f pos(
|
||||||
|
center.x() - radius + i * interiorStep,
|
||||||
|
center.y() - radius + j * interiorStep,
|
||||||
|
center.z() - radius + k * interiorStep
|
||||||
|
);
|
||||||
|
|
||||||
|
float dist = (pos - center).norm();
|
||||||
|
|
||||||
|
// If deep inside the sphere
|
||||||
|
if (dist < radius * 0.8f) {
|
||||||
|
// Check if position is valid
|
||||||
|
if (pos.x() >= 0 && pos.x() < config.gridSizecube &&
|
||||||
|
pos.y() >= 0 && pos.y() < config.gridSizecube &&
|
||||||
|
pos.z() >= 0 && pos.z() < config.gridSizecube) {
|
||||||
|
|
||||||
|
// Try to add the point
|
||||||
|
grid.set(1, pos, true, colorVec, voxelSize, true, 1,
|
||||||
|
sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "voxel grid ready" << std::endl;
|
||||||
|
meshNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addStar(const defaults& config, const stardefaults& starconf, Octree<int>& grid) {
|
||||||
|
if (!starconf.enabled) return;
|
||||||
|
|
||||||
|
Eigen::Vector3f colorVec(starconf.color[0], starconf.color[1], starconf.color[2]);
|
||||||
|
PointType pos(starconf.x, starconf.y, starconf.z);
|
||||||
|
|
||||||
|
grid.set(2, pos, true, colorVec, starconf.size, true, 2, true, starconf.emittance, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
meshNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatsCache(Octree<int>& grid) {
|
||||||
|
std::stringstream gridstats;
|
||||||
|
grid.printStats(gridstats);
|
||||||
|
cachedStats = gridstats.str();
|
||||||
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
updatePreview = true;
|
||||||
|
|
||||||
|
|
||||||
|
// if (meshNeedsUpdate) {
|
||||||
|
// scene.clear();
|
||||||
|
// std::shared_ptr<Mesh> planetMesh = grid.generateMesh(1, config.meshIsoLevel, pow(config.meshResolution, 2));
|
||||||
|
// std::shared_ptr<Mesh> starMesh = grid.generateMesh(2, config.meshIsoLevel, config.meshResolution);
|
||||||
|
|
||||||
|
// scene.addMesh(planetMesh);
|
||||||
|
// scene.addMesh(starMesh);
|
||||||
|
|
||||||
|
// // planetMesh.setResolution(config.meshResolution);
|
||||||
|
// // planetMesh.setIsoLevel(config.meshIsoLevel);
|
||||||
|
// // planetMesh.update(grid);
|
||||||
|
// meshNeedsUpdate = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto renderStart = std::chrono::high_resolution_clock::now();
|
||||||
|
frame currentPreviewFrame;
|
||||||
|
|
||||||
|
// currentPreviewFrame = scene.render(cam, config.outWidth, config.outHeight, 0.1f, 10000.0f, frame::colormap::RGB);
|
||||||
|
|
||||||
|
grid.setLODMinDistance(config.lodDist);
|
||||||
|
grid.setLODFalloff(config.lodDropoff);
|
||||||
|
if (config.slowRender) {
|
||||||
|
currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, config.rayCount, config.reflectCount, config.globalIllumination, config.useLod);
|
||||||
|
} else {
|
||||||
|
currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto renderEnd = std::chrono::high_resolution_clock::now();
|
||||||
|
renderFrameTime = std::chrono::duration<double>(renderEnd - renderStart).count();
|
||||||
|
|
||||||
|
if (!firstFrameMeasured) {
|
||||||
|
renderFrameTimes.resize(FRAME_HISTORY_SIZE, renderFrameTime);
|
||||||
|
firstFrameMeasured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFrameTimes[frameHistoryIndex] = renderFrameTime;
|
||||||
|
frameHistoryIndex = (frameHistoryIndex + 1) % FRAME_HISTORY_SIZE;
|
||||||
|
|
||||||
|
avgRenderFrameTime = 0.0;
|
||||||
|
int validFrames = 0;
|
||||||
|
for (int i = 0; i < FRAME_HISTORY_SIZE; i++) {
|
||||||
|
if (renderFrameTimes[i] > 0) {
|
||||||
|
avgRenderFrameTime += renderFrameTimes[i];
|
||||||
|
validFrames++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validFrames > 0) {
|
||||||
|
avgRenderFrameTime /= validFrames;
|
||||||
|
renderFPS = 1.0 / avgRenderFrameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
if (textu == 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetView(Camera& cam, float gridSize) {
|
||||||
|
cam.origin = Vector3f(gridSize, gridSize, gridSize);
|
||||||
|
Vector3f center(gridSize / 2.0f, gridSize / 2.0f, gridSize / 2.0f);
|
||||||
|
cam.lookAt(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
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), "StupidSim", 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);
|
||||||
|
|
||||||
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
|
defaults config;
|
||||||
|
PointType minBound(-config.gridSizecube, -config.gridSizecube, -config.gridSizecube);
|
||||||
|
PointType maxBound(config.gridSizecube, config.gridSizecube, config.gridSizecube);
|
||||||
|
Octree<int> grid(minBound, maxBound, 8, 32);
|
||||||
|
bool gridInitialized = false;
|
||||||
|
float ghalf = config.gridSizecube / 2.f;
|
||||||
|
|
||||||
|
spheredefaults sphereConf;
|
||||||
|
stardefaults starConf;
|
||||||
|
|
||||||
|
// Initialize Noise Preview State
|
||||||
|
NoisePreviewState noiseState;
|
||||||
|
NoiseLayer l1;
|
||||||
|
l1.type = NoiseType::Fractal;
|
||||||
|
l1.blend = BlendMode::Add; // Start with base
|
||||||
|
l1.scale = 0.005f;
|
||||||
|
strcpy(l1.name, "Continents");
|
||||||
|
noiseState.layers.push_back(l1);
|
||||||
|
|
||||||
|
updateNoiseTexture(noiseState);
|
||||||
|
|
||||||
|
FluidSimUI fluidUI;
|
||||||
|
bool showFluidSim = false;
|
||||||
|
|
||||||
|
sphereConf.centerX = ghalf;
|
||||||
|
sphereConf.centerY = ghalf;
|
||||||
|
sphereConf.centerZ = ghalf;
|
||||||
|
|
||||||
|
bool autoRotate = false;
|
||||||
|
bool autoRotateView = false;
|
||||||
|
bool orbitEquator = false;
|
||||||
|
bool orbitPoles = false;
|
||||||
|
|
||||||
|
float rotationSpeedX = 0.1f;
|
||||||
|
float rotationSpeedY = 0.07f;
|
||||||
|
float rotationSpeedZ = 0.05f;
|
||||||
|
float autoRotationTime = 0.0f;
|
||||||
|
PointType initialViewDir(0, 0, 1);
|
||||||
|
float rotationRadius = 2000.0f;
|
||||||
|
float yawSpeed = 0.5f;
|
||||||
|
float pitchSpeed = 0.3f;
|
||||||
|
float rollSpeed = 0.2f;
|
||||||
|
float autoRotationAngle = 0.0f;
|
||||||
|
PointType initialUpDir(0, 1, 0);
|
||||||
|
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 camspeed = 50;
|
||||||
|
Camera cam(PointType(400, 400, 400), PointType(0,0,1), PointType(0,1,0), 80, camspeed);
|
||||||
|
|
||||||
|
std::map<int, bool> keyStates;
|
||||||
|
bool mouseCaptured = false;
|
||||||
|
double lastMouseX = 0, lastMouseY = 0;
|
||||||
|
float deltaTime = 0.016f;
|
||||||
|
renderFrameTimes.resize(FRAME_HISTORY_SIZE, 0.0);
|
||||||
|
|
||||||
|
bool worldPreview = false;
|
||||||
|
|
||||||
|
if (grid.load("output/Treegrid.yggs")) {
|
||||||
|
gridInitialized = true;
|
||||||
|
resetView(cam, config.gridSizecube);
|
||||||
|
meshNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
double currentTime = glfwGetTime();
|
||||||
|
static double lastFrameTime = currentTime;
|
||||||
|
deltaTime = currentTime - lastFrameTime;
|
||||||
|
lastFrameTime = currentTime;
|
||||||
|
|
||||||
|
if (showFluidSim) {
|
||||||
|
fluidUI.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orbitEquator || orbitPoles || autoRotate) autoRotationTime += deltaTime;
|
||||||
|
if (autoRotateView) autoRotationAngle += deltaTime;
|
||||||
|
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
||||||
|
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera movement - WASD + QE + ZX
|
||||||
|
float actualMoveSpeed = deltaTime;
|
||||||
|
float actualRotateSpeed = deltaTime;
|
||||||
|
|
||||||
|
if (keyStates[GLFW_KEY_W]) {
|
||||||
|
cam.moveForward(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_S]) {
|
||||||
|
cam.moveBackward(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_A]) {
|
||||||
|
cam.moveLeft(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_D]) {
|
||||||
|
cam.moveRight(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_Z]) {
|
||||||
|
cam.moveUp(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_X]) {
|
||||||
|
cam.moveDown(actualMoveSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_Q]) {
|
||||||
|
cam.rotateYaw(actualRotateSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_R]) {
|
||||||
|
cam.rotateYaw(-actualRotateSpeed);
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cx = sphereConf.centerX;
|
||||||
|
float cy = sphereConf.centerY;
|
||||||
|
float cz = sphereConf.centerZ;
|
||||||
|
|
||||||
|
if (orbitEquator || orbitPoles) {
|
||||||
|
float speed = 0.5f;
|
||||||
|
float angle = autoRotationTime * speed;
|
||||||
|
|
||||||
|
if (orbitEquator) {
|
||||||
|
cam.origin[0] = cx + rotationRadius * cosf(angle);
|
||||||
|
cam.origin[1] = cy;
|
||||||
|
cam.origin[2] = cz + rotationRadius * sinf(angle);
|
||||||
|
} else {
|
||||||
|
cam.origin[0] = cx;
|
||||||
|
cam.origin[1] = cy + rotationRadius * cosf(angle);
|
||||||
|
cam.origin[2] = cz + rotationRadius * sinf(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
PointType target(cx, cy, cz);
|
||||||
|
cam.direction = (target - cam.origin).normalized();
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update camera position and view direction variables for UI
|
||||||
|
camX = cam.origin[0];
|
||||||
|
camY = cam.origin[1];
|
||||||
|
camZ = cam.origin[2];
|
||||||
|
|
||||||
|
camvX = cam.direction[0];
|
||||||
|
camvY = cam.direction[1];
|
||||||
|
camvZ = cam.direction[2];
|
||||||
|
camspeed = cam.movementSpeed;
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
ImGui_ImplGlfw_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
{
|
||||||
|
ImGui::Begin("Sim Controls");
|
||||||
|
|
||||||
|
ImGui::Text("Planet");
|
||||||
|
float pos[3] = { sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ };
|
||||||
|
if (ImGui::DragFloat3("Center (X,Y,Z)", pos, 1.0f, 0.0f, (float)config.gridSizecube)) {
|
||||||
|
sphereConf.centerX = pos[0];
|
||||||
|
sphereConf.centerY = pos[1];
|
||||||
|
sphereConf.centerZ = pos[2];
|
||||||
|
}
|
||||||
|
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
||||||
|
ImGui::DragFloat("Density (Overlap)", &sphereConf.voxelSize, 0.1f, 1.0f, 100.0f);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Multiplies calculated point size. >1.0 ensures solid surface.");
|
||||||
|
}
|
||||||
|
ImGui::ColorEdit3("Color", sphereConf.color);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
// ImGui::Text("Marching Cubes Config");
|
||||||
|
// if (ImGui::SliderInt("Mesh Resolution", &config.meshResolution, 1, 64)) {
|
||||||
|
// meshNeedsUpdate = true;
|
||||||
|
// }
|
||||||
|
// if (ImGui::SliderFloat("Iso Level", &config.meshIsoLevel, 0.01f, 1.0f)) {
|
||||||
|
// meshNeedsUpdate = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ImGui::Separator();
|
||||||
|
// ImGui::Checkbox("Is Light", &sphereConf.light);
|
||||||
|
if(sphereConf.light) {
|
||||||
|
ImGui::DragFloat("Emittance", &sphereConf.emittance, 0.1f, 0.0f, 100.0f);
|
||||||
|
}
|
||||||
|
ImGui::SliderFloat("Reflection", &sphereConf.reflection, 0.0f, 1.0f);
|
||||||
|
ImGui::SliderFloat("Refraction", &sphereConf.refraction, 0.0f, 1.0f);
|
||||||
|
ImGui::Checkbox("Fill Inside", &sphereConf.fillInside);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Star/Sun Parameters", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::Checkbox("Enable Star", &starConf.enabled);
|
||||||
|
|
||||||
|
// Allow large range for position to place it "far away"
|
||||||
|
float starPos[3] = { starConf.x, starConf.y, starConf.z };
|
||||||
|
if (ImGui::DragFloat3("Position", starPos, 5.0f, -2000.0f, 2000.0f)) {
|
||||||
|
starConf.x = starPos[0];
|
||||||
|
starConf.y = starPos[1];
|
||||||
|
starConf.z = starPos[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::DragFloat("Size (Radius)", &starConf.size, 1.0f, 1.0f, 1000.0f);
|
||||||
|
ImGui::DragFloat("Brightness", &starConf.emittance, 1.0f, 0.0f, 1000.0f);
|
||||||
|
ImGui::ColorEdit3("Light Color", starConf.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Create Sphere & Render")) {
|
||||||
|
createSphere(config, sphereConf, grid);
|
||||||
|
addStar(config, starConf, grid);
|
||||||
|
gridInitialized = true;
|
||||||
|
scene.updateStats();
|
||||||
|
|
||||||
|
resetView(cam, config.gridSizecube);
|
||||||
|
grid.generateLODs();
|
||||||
|
livePreview(grid, config, cam);
|
||||||
|
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Modules");
|
||||||
|
ImGui::Checkbox("Fluid Simulation", &showFluidSim);
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showFluidSim) {
|
||||||
|
fluidUI.renderUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
// scene.drawSceneWindow("Planet Preview", cam, 0.01, 1000);
|
||||||
|
// scene.drawGridStats();
|
||||||
|
|
||||||
|
{
|
||||||
|
ImGui::Begin("Planet Preview");
|
||||||
|
if (worldPreview) {
|
||||||
|
if (gridInitialized) {
|
||||||
|
livePreview(grid, config, cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Text("Render Performance:");
|
||||||
|
if (renderFPS > 0) {
|
||||||
|
// Color code based on FPS
|
||||||
|
ImVec4 fpsColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
// if (renderFPS >= 30.0) {
|
||||||
|
// fpsColor = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); // Green for good FPS
|
||||||
|
// } else if (renderFPS >= 15.0) {
|
||||||
|
// fpsColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); // Yellow for okay FPS
|
||||||
|
// } else {
|
||||||
|
// fpsColor = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); // Red for poor FPS
|
||||||
|
// }
|
||||||
|
|
||||||
|
ImGui::TextColored(fpsColor, "FPS: %.1f", renderFPS);
|
||||||
|
ImGui::Text("Frame time: %.1f ms", avgRenderFrameTime * 1000.0);
|
||||||
|
|
||||||
|
// Simple progress bar for frame time
|
||||||
|
ImGui::Text("%.1f/100 ms", avgRenderFrameTime * 1000.0);
|
||||||
|
|
||||||
|
// Show latest frame time
|
||||||
|
ImGui::Text("Latest: %.1f ms", renderFrameTime * 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (gridInitialized) {
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
if ((now - lastStatsUpdate) > STATS_UPDATE_INTERVAL) updateStatsCache(grid);
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ImGui::Begin("controls");
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Camera Controls:");
|
||||||
|
|
||||||
|
float maxSliderValueX = config.gridSizecube;
|
||||||
|
float maxSliderValueY = config.gridSizecube;
|
||||||
|
float maxSliderValueZ = config.gridSizecube;
|
||||||
|
|
||||||
|
ImGui::Text("Position (0 to grid size):");
|
||||||
|
if (ImGui::SliderFloat("Camera X", &camX, -maxSliderValueX, maxSliderValueX)) {
|
||||||
|
cam.origin[0] = camX;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("Camera Y", &camY, -maxSliderValueY, maxSliderValueY)) {
|
||||||
|
cam.origin[1] = camY;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("Camera Z", &camZ, -maxSliderValueZ, maxSliderValueZ)) {
|
||||||
|
cam.origin[2] = camZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("View Direction:");
|
||||||
|
if (ImGui::SliderFloat("Camera View X", &camvX, -1.0f, 1.0f)) {
|
||||||
|
cam.direction[0] = camvX;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("Camera View Y", &camvY, -1.0f, 1.0f)) {
|
||||||
|
cam.direction[1] = camvY;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("Camera View Z", &camvZ, -1.0f, 1.0f)) {
|
||||||
|
cam.direction[2] = camvZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::SliderFloat("Camera Speed", &camspeed, 1, 500)) {
|
||||||
|
cam.movementSpeed = camspeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Focus on Planet")) {
|
||||||
|
PointType target(sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ);
|
||||||
|
PointType newDir = (target - cam.origin).normalized();
|
||||||
|
cam.direction = newDir;
|
||||||
|
camvX = newDir[0];
|
||||||
|
camvY = newDir[1];
|
||||||
|
camvZ = newDir[2];
|
||||||
|
previewRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
// Equator Orbit
|
||||||
|
if (ImGui::Button(orbitEquator ? "Stop Equator" : "Orbit Equator")) {
|
||||||
|
orbitEquator = !orbitEquator;
|
||||||
|
if (orbitEquator) {
|
||||||
|
orbitPoles = false;
|
||||||
|
autoRotate = false;
|
||||||
|
autoRotationTime = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
// Polar Orbit
|
||||||
|
if (ImGui::Button(orbitPoles ? "Stop Poles" : "Orbit Poles")) {
|
||||||
|
orbitPoles = !orbitPoles;
|
||||||
|
if (orbitPoles) {
|
||||||
|
orbitEquator = false;
|
||||||
|
autoRotate = false;
|
||||||
|
autoRotationTime = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Current Camera Position:");
|
||||||
|
ImGui::Text("X: %.2f, Y: %.2f, Z: %.2f", cam.origin[0], cam.origin[1], cam.origin[2]);
|
||||||
|
|
||||||
|
ImGui::Text("Auto-Rotation:");
|
||||||
|
|
||||||
|
// Toggle button for auto-rotation
|
||||||
|
if (ImGui::Button(autoRotate ? "Stop random Rotate" : "Start random Rotate")) {
|
||||||
|
autoRotate = !autoRotate;
|
||||||
|
if (autoRotate) {
|
||||||
|
orbitEquator = false;
|
||||||
|
orbitPoles = false;
|
||||||
|
autoRotationTime = 0.0f;
|
||||||
|
initialViewDir = PointType(camvX, camvY, camvZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(autoRotateView ? "Stop Looking Around" : "Start Looking Around")) {
|
||||||
|
autoRotateView = !autoRotateView;
|
||||||
|
if (autoRotateView) {
|
||||||
|
autoRotationAngle = 0.0f;
|
||||||
|
initialViewDir = PointType(camvX, camvY, camvZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoRotate) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("(Running)");
|
||||||
|
|
||||||
|
// 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.gridSizecube / 2.0f + rotationRadius * camvX;
|
||||||
|
camY = config.gridSizecube / 2.0f + rotationRadius * camvY;
|
||||||
|
camZ = config.gridSizecube / 2.0f + rotationRadius * camvZ;
|
||||||
|
|
||||||
|
// Update camera
|
||||||
|
cam.origin = PointType(camX, camY, camZ);
|
||||||
|
cam.direction = PointType(camvX, camvY, camvZ);
|
||||||
|
cam.lookAt(PointType(sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ));
|
||||||
|
|
||||||
|
// 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, 5000.0f);
|
||||||
|
|
||||||
|
if (autoRotateView) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), " ACTIVE");
|
||||||
|
|
||||||
|
// 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
|
||||||
|
PointType forward = initialViewDir;
|
||||||
|
|
||||||
|
// Yaw rotation (around Y axis)
|
||||||
|
float cosYaw = cosf(yaw);
|
||||||
|
float sinYaw = sinf(yaw);
|
||||||
|
PointType tempForward;
|
||||||
|
tempForward[0] = forward[0] * cosYaw + forward[2] * sinYaw;
|
||||||
|
tempForward[1] = forward[1];
|
||||||
|
tempForward[2] = -forward[0] * sinYaw + forward[2] * cosYaw;
|
||||||
|
forward = tempForward;
|
||||||
|
|
||||||
|
// Pitch rotation (around X axis)
|
||||||
|
float cosPitch = cosf(pitch);
|
||||||
|
float sinPitch = sinf(pitch);
|
||||||
|
tempForward[0] = forward[0];
|
||||||
|
tempForward[1] = forward[1] * cosPitch - forward[2] * sinPitch;
|
||||||
|
tempForward[2] = forward[1] * sinPitch + forward[2] * cosPitch;
|
||||||
|
forward = tempForward;
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
float length = sqrtf(forward[0] * forward[0] + forward[1] * forward[1] + forward[2] * forward[2]);
|
||||||
|
if (length > 0.001f) {
|
||||||
|
forward[0] /= length;
|
||||||
|
forward[1] /= length;
|
||||||
|
forward[2] /= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update view direction
|
||||||
|
camvX = forward[0];
|
||||||
|
camvY = forward[1];
|
||||||
|
camvZ = forward[2];
|
||||||
|
|
||||||
|
// Update camera
|
||||||
|
cam.direction = PointType(camvX, camvY, camvZ);
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// ImGui::Checkbox("Continuous Preview", &worldPreview);
|
||||||
|
|
||||||
|
ImGui::Checkbox("update Preview", &worldPreview);
|
||||||
|
ImGui::Checkbox("Use Slower renderer", &config.slowRender);
|
||||||
|
if (config.slowRender) {
|
||||||
|
ImGui::InputInt("Rays per pixel", &config.rayCount);
|
||||||
|
ImGui::InputInt("Max reflections", &config.reflectCount);
|
||||||
|
}
|
||||||
|
ImGui::InputFloat("Lod dropoff", &config.lodDropoff);
|
||||||
|
ImGui::InputInt("lod minimum Distance", &config.lodDist);
|
||||||
|
ImGui::Checkbox("use Global illumination", &config.globalIllumination);
|
||||||
|
ImGui::Checkbox("use Lod", &config.useLod);
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawNoiseLab(noiseState);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
grid.save("output/Treegrid.yggs");
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
if (textu != 0) {
|
||||||
|
glDeleteTextures(1, &textu);
|
||||||
|
textu = 0;
|
||||||
|
}
|
||||||
|
if (noiseState.textureId != 0) {
|
||||||
|
glDeleteTextures(1, &noiseState.textureId);
|
||||||
|
noiseState.textureId = 0;
|
||||||
|
}
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
195
tests/materialtest.cpp
Normal file
195
tests/materialtest.cpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Include Eigen and project headers
|
||||||
|
#include "../eigen/Eigen/Dense"
|
||||||
|
#include "../util/grid/camera.hpp"
|
||||||
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
|
#include "../util/output/frame.hpp"
|
||||||
|
#include "../util/output/bmpwriter.hpp"
|
||||||
|
|
||||||
|
// Helper function to create a solid volume of voxels with material properties
|
||||||
|
void createBox(Octree<int>& octree, const Eigen::Vector3f& center, const Eigen::Vector3f& size,
|
||||||
|
const Eigen::Vector3f& albedo, float emission = 0.0f,
|
||||||
|
float roughness = 0.8f, float metallic = 0.0f, float transmission = 0.0f, float ior = 1.45f) {
|
||||||
|
float step = 0.1f; // Voxel spacing
|
||||||
|
Eigen::Vector3f halfSize = size / 2.0f;
|
||||||
|
Eigen::Vector3f minB = center - halfSize;
|
||||||
|
Eigen::Vector3f maxB = center + halfSize;
|
||||||
|
|
||||||
|
for (float x = minB.x(); x <= maxB.x(); x += step) {
|
||||||
|
for (float y = minB.y(); y <= maxB.y(); y += step) {
|
||||||
|
for (float z = minB.z(); z <= maxB.z(); z += step) {
|
||||||
|
Eigen::Vector3f pos(x, y, z);
|
||||||
|
|
||||||
|
// .set(data, pos, visible, albedo, size, active, objectId, subId, emission, roughness, metallic, transmission, ior)
|
||||||
|
octree.set(1, pos, true, albedo, step, true, -1, 0, emission, roughness, metallic, transmission, ior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create a checkerboard pattern volume
|
||||||
|
void createCheckerBox(Octree<int>& octree, const Eigen::Vector3f& center, const Eigen::Vector3f& size,
|
||||||
|
const Eigen::Vector3f& color1, const Eigen::Vector3f& color2, float checkerSize) {
|
||||||
|
float step = 0.1f;
|
||||||
|
Eigen::Vector3f halfSize = size / 2.0f;
|
||||||
|
Eigen::Vector3f minB = center - halfSize;
|
||||||
|
Eigen::Vector3f maxB = center + halfSize;
|
||||||
|
|
||||||
|
for (float x = minB.x(); x <= maxB.x(); x += step) {
|
||||||
|
for (float y = minB.y(); y <= maxB.y(); y += step) {
|
||||||
|
for (float z = minB.z(); z <= maxB.z(); z += step) {
|
||||||
|
Eigen::Vector3f pos(x, y, z);
|
||||||
|
|
||||||
|
// Use floor to correctly handle negative coordinates for the repeating pattern
|
||||||
|
int cx = static_cast<int>(std::floor(x / checkerSize));
|
||||||
|
int cy = static_cast<int>(std::floor(y / checkerSize));
|
||||||
|
int cz = static_cast<int>(std::floor(z / checkerSize));
|
||||||
|
|
||||||
|
// 3D Checkerboard logic
|
||||||
|
bool isEven = ((cx + cy + cz) % 2 == 0);
|
||||||
|
Eigen::Vector3f albedo = isEven ? color1 : color2;
|
||||||
|
|
||||||
|
octree.set(1, pos, true, albedo, step, true, -1, 0, 0.0f, 0.8f, 0.1f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Initializing Octree..." << std::endl;
|
||||||
|
|
||||||
|
// 1. Initialize Octree bounds
|
||||||
|
Eigen::Vector3f minBound(-10.0f, -10.0f, -10.0f);
|
||||||
|
Eigen::Vector3f maxBound(10.0f, 10.0f, 10.0f);
|
||||||
|
Octree<int> octree(minBound, maxBound, 8, 16);
|
||||||
|
|
||||||
|
// Set a dark background to emphasize the PBR light emission
|
||||||
|
octree.setBackgroundColor(Eigen::Vector3f(0.02f, 0.02f, 0.02f));
|
||||||
|
octree.setSkylight(Eigen::Vector3f(0.01f, 0.01f, 0.01f));
|
||||||
|
|
||||||
|
std::cout << "Building scene..." << std::endl;
|
||||||
|
|
||||||
|
// 2a. Build Room (Floor and 4 Walls)
|
||||||
|
Eigen::Vector3f cLightGray(0.8f, 0.8f, 0.8f);
|
||||||
|
Eigen::Vector3f cDarkGray(0.2f, 0.2f, 0.2f);
|
||||||
|
float chkSize = 1.0f;
|
||||||
|
|
||||||
|
// Floor (Bounds: Z from -0.7 to -0.5)
|
||||||
|
// The boxes sit exactly on Z = -0.5
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f(0.0f, 0.0f, -0.6f), Eigen::Vector3f(14.4f, 14.4f, 0.2f), cLightGray, cDarkGray, chkSize);
|
||||||
|
|
||||||
|
// Walls (Bounds: X/Y inner boundaries at +/- 7.0, rising from Z=-0.5 up to Z=7.5)
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 7.1f, 0.0f, 3.5f), Eigen::Vector3f(0.2f, 14.4f, 8.0f), cLightGray, cDarkGray, chkSize); // +X
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f(-7.1f, 0.0f, 3.5f), Eigen::Vector3f(0.2f, 14.4f, 8.0f), cLightGray, cDarkGray, chkSize); // -X
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 0.0f, 7.1f, 3.5f), Eigen::Vector3f(14.0f, 0.2f, 8.0f), cLightGray, cDarkGray, chkSize); // +Y
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 0.0f, -7.1f, 3.5f), Eigen::Vector3f(14.0f, 0.2f, 8.0f), cLightGray, cDarkGray, chkSize); // -Y
|
||||||
|
|
||||||
|
// 2b. Create the 3x3 material sampler grid inside the room
|
||||||
|
Eigen::Vector3f cRed(1.0f, 0.1f, 0.1f);
|
||||||
|
Eigen::Vector3f cBlue(0.1f, 0.1f, 1.0f);
|
||||||
|
Eigen::Vector3f cPurple(0.6f, 0.1f, 0.8f);
|
||||||
|
Eigen::Vector3f size(1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
float sp = 2.0f; // spacing between cubes
|
||||||
|
|
||||||
|
// --- LAYER 1: Metals ---
|
||||||
|
// (metallic = 1.0, slight roughness for blurry reflections, transmission = 0.0)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, -sp, 0.0f), size, cRed, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, -sp, 0.0f), size, cBlue, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, -sp, 0.0f), size, cPurple, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
|
||||||
|
// --- LAYER 2: Opaque & Highly Refractive ---
|
||||||
|
// (metallic = 0.0, very low roughness. transmission = 0.0 for opacity, ior = 2.4 for extreme diamond-like reflection)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, 0, 0.0f), size, cRed, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, 0, 0.0f), size, cBlue, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, 0, 0.0f), size, cPurple, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
|
||||||
|
// --- LAYER 3: Clear Glass ---
|
||||||
|
// (metallic = 0.0, near-zero roughness, transmission = 1.0 for full transparency, ior = 1.5 for glass)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, sp, 0.0f), size, cRed, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, sp, 0.0f), size, cBlue, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, sp, 0.0f), size, cPurple, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
|
||||||
|
// White Light Box (Above)
|
||||||
|
// Placed near the ceiling (Z=7.4), made large (8x8) to cast soft shadows evenly over the whole 3x3 grid
|
||||||
|
createBox(octree, Eigen::Vector3f(0.0f, 0.0f, 7.4f), Eigen::Vector3f(8.0f, 8.0f, 0.2f), Eigen::Vector3f(1.0f, 1.0f, 1.0f), 15.0f);
|
||||||
|
|
||||||
|
std::cout << "Optimizing and Generating LODs..." << std::endl;
|
||||||
|
octree.generateLODs();
|
||||||
|
octree.printStats();
|
||||||
|
|
||||||
|
// 3. Setup rendering loop
|
||||||
|
int width = 512;
|
||||||
|
int height = 512;
|
||||||
|
int samples = 400;
|
||||||
|
int bounces = 5;
|
||||||
|
|
||||||
|
struct View {
|
||||||
|
std::string name;
|
||||||
|
Eigen::Vector3f origin;
|
||||||
|
Eigen::Vector3f up;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The walls are set perfectly at +/- 7.0 inner edges.
|
||||||
|
// Placing camera at +/- 6.8 will put it "just barely inside".
|
||||||
|
// Floor is at Z = -0.5, Wall top is at Z = 7.5
|
||||||
|
std::vector<View> views = {
|
||||||
|
{"+X", Eigen::Vector3f( 6.8f, 0.0f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"-X", Eigen::Vector3f(-6.8f, 0.0f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"+Y", Eigen::Vector3f( 0.0f, 6.8f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"-Y", Eigen::Vector3f( 0.0f, -6.8f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"+Z", Eigen::Vector3f( 0.0f, 0.0f, 7.3f), Eigen::Vector3f(0.0f, 1.0f, 0.0f)} // Looking down from just beneath wall top
|
||||||
|
};
|
||||||
|
|
||||||
|
Eigen::Vector3f target(0.0f, 0.0f, 0.5f);
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Fast Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.fastRenderFrame(cam, height, width, frame::colormap::RGB);
|
||||||
|
|
||||||
|
std::string filename = "output/fast/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Medium 60s Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.renderFrameTimed(cam, height, width, frame::colormap::RGB, 60, bounces, false, true);
|
||||||
|
|
||||||
|
std::string filename = "output/medium/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Slow 400 Samples Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.renderFrame(cam, height, width, frame::colormap::RGB, samples, bounces, false, true);
|
||||||
|
|
||||||
|
std::string filename = "output/slow/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\nAll renders complete!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
582
tests/planet.cpp
Normal file
582
tests/planet.cpp
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
#ifndef PLANET_CPP
|
||||||
|
#define PLANET_CPP
|
||||||
|
|
||||||
|
#include "../util/sim/planet.hpp"
|
||||||
|
#include "../util/grid/camera.hpp"
|
||||||
|
#include "../util/noise/pnoise2.hpp"
|
||||||
|
#include "../util/noise/pnoise.cpp"
|
||||||
|
|
||||||
|
class planetSimUI {
|
||||||
|
private:
|
||||||
|
planetsim sim;
|
||||||
|
Camera cam;
|
||||||
|
bool isRunning = false;
|
||||||
|
|
||||||
|
// Texture Management
|
||||||
|
GLuint textu = 0;
|
||||||
|
std::mutex PreviewMutex;
|
||||||
|
bool updatePreview = false;
|
||||||
|
bool textureInitialized = false;
|
||||||
|
frame currentPreviewFrame;
|
||||||
|
int outWidth = 1024;
|
||||||
|
int outHeight = 1024;
|
||||||
|
float fps = 60;
|
||||||
|
int rayCount = 3;
|
||||||
|
int reflectCount = 4;
|
||||||
|
bool slowRender = false;
|
||||||
|
float lodDist = 1024.0f;
|
||||||
|
float lodDropoff = 0.001f;
|
||||||
|
float maxViewDistance = 4096;
|
||||||
|
bool globalIllumination = false;
|
||||||
|
bool useLod = true;
|
||||||
|
std::map<int, bool> keyStates;
|
||||||
|
float deltaTime = 0.16f;
|
||||||
|
bool orbitEquator = false;
|
||||||
|
float rotationRadius = 2500;
|
||||||
|
float angle = 0.0f;
|
||||||
|
const float ω = (std::pow(M_PI, 2) / 30) / 10;
|
||||||
|
bool tectonicGenned = false;
|
||||||
|
bool doFixPlates = true;
|
||||||
|
bool platesUseCellular = false;
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
std::string cachedStats;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
float framerate = 60.0;
|
||||||
|
|
||||||
|
enum class DebugColorMode {
|
||||||
|
BASE,
|
||||||
|
PLATES,
|
||||||
|
NOISE,
|
||||||
|
RESERVED
|
||||||
|
};
|
||||||
|
DebugColorMode currentColorMode = DebugColorMode::BASE;
|
||||||
|
|
||||||
|
enum class DebugMapMode {
|
||||||
|
NONE,
|
||||||
|
BASE,
|
||||||
|
NOISE,
|
||||||
|
TECTONIC,
|
||||||
|
TECTONICCOLOR,
|
||||||
|
CURRENT
|
||||||
|
};
|
||||||
|
DebugMapMode currentMapMode = DebugMapMode::NONE;
|
||||||
|
GLuint mapTexture = 0;
|
||||||
|
frame mapFrame;
|
||||||
|
|
||||||
|
public:
|
||||||
|
planetSimUI() {
|
||||||
|
cam.origin = v3(4000, 4000, 4000);
|
||||||
|
cam.direction = (v3(0,0,0) - cam.origin).normalized();
|
||||||
|
cam.up = v3(0,1,0);
|
||||||
|
cam.fov = 60;
|
||||||
|
cam.rotationSpeed = M_1_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
~planetSimUI() {
|
||||||
|
if (textu != 0) {
|
||||||
|
glDeleteTextures(1, &textu);
|
||||||
|
}
|
||||||
|
if (mapTexture != 0) {
|
||||||
|
glDeleteTextures(1, &mapTexture);
|
||||||
|
}
|
||||||
|
sim.grid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderUI(GLFWwindow* window) {
|
||||||
|
handleCameraControls(window);
|
||||||
|
ImGui::Begin("Planet Simulation");
|
||||||
|
if (ImGui::BeginTable("MainLayout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter)) {
|
||||||
|
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||||
|
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
renderControlsPanel();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
renderPreviewPanel();
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCameraControls(GLFWwindow* window) {
|
||||||
|
if (orbitEquator) {
|
||||||
|
angle += cam.rotationSpeed * deltaTime * ω;
|
||||||
|
|
||||||
|
cam.origin[0] = sim.config.center[0] + rotationRadius * cosf(angle);
|
||||||
|
cam.origin[1] = sim.config.center[1];
|
||||||
|
cam.origin[2] = sim.config.center[2] + rotationRadius * sinf(angle);
|
||||||
|
|
||||||
|
v3 target(sim.config.center);
|
||||||
|
cam.direction = (target - cam.origin).normalized();
|
||||||
|
}
|
||||||
|
glfwPollEvents();
|
||||||
|
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
||||||
|
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
|
||||||
|
}
|
||||||
|
if (keyStates[GLFW_KEY_W]) cam.moveForward(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_S]) cam.moveBackward(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_A]) cam.moveLeft(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_D]) cam.moveRight(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_Z]) cam.moveUp(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_X]) cam.moveDown(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_Q]) cam.rotateYaw(deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_R]) cam.rotateYaw(-deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderControlsPanel() {
|
||||||
|
ImGui::BeginChild("ControlsScroll", ImVec2(0, 0), true);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Base Configuration", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::DragFloat("Radius", &sim.config.radius, 1.0f, 10.0f, 10000.0f);
|
||||||
|
ImGui::InputInt("Surface Points", &sim.config.surfacePoints);
|
||||||
|
ImGui::DragFloat("Voxel Size", &sim.config.voxelSize, 0.1f, 0.1f, 100.0f);
|
||||||
|
ImGui::ColorEdit3("Base Color", sim.config.color.data());
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("1. Generate Fib Sphere", ImVec2(-1, 40))) {
|
||||||
|
sim.generateFibSphere();
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
ImGui::Text("Current Step: %d", sim.config.currentStep);
|
||||||
|
ImGui::Text("Nodes: %zu", sim.config.surfaceNodes.size());
|
||||||
|
ImGui::InputFloat("Noise strength", &sim.config.noiseStrength, 0.01, 1, "%.4f");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Physics Parameters")) {
|
||||||
|
ImGui::DragFloat("Gravity (G)", &sim.config.G_ATTRACTION, 0.1f);
|
||||||
|
ImGui::DragFloat("Time Step", &sim.config.TIMESTEP, 0.001f, 0.0001f, 0.1f);
|
||||||
|
ImGui::DragFloat("Viscosity", &sim.config.dampingFactor, 0.001f, 0.0f, 1.0f);
|
||||||
|
ImGui::DragFloat("Pressure Stiffness", &sim.config.pressureStiffness, 10.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Tectonic Simulation")) {
|
||||||
|
ImGui::DragInt("Num Plates", &sim.config.numPlates, 1, 1, 100);
|
||||||
|
ImGui::DragInt("Smoothing Passes", &sim.config.smoothingPasses, 1, 0, 10);
|
||||||
|
ImGui::DragFloat("Mountain Height", &sim.config.mountHeight, 1.0f, 0.0f, 1000.0f);
|
||||||
|
ImGui::DragFloat("Valley Depth", &sim.config.valleyDepth, 1.0f, -1000.0f, 0.0f);
|
||||||
|
ImGui::DragFloat("Transform Roughness", &sim.config.transformRough, 1.0f, 0.0f, 500.0f);
|
||||||
|
ImGui::DragInt("Stress Passes", &sim.config.stressPasses, 1, 0, 20);
|
||||||
|
ImGui::DragFloat("Max Elevation Ratio", &sim.config.maxElevationRatio, 1.0f, 0.0f, 1.0f);
|
||||||
|
ImGui::Checkbox("Fix Boundaries", &doFixPlates);
|
||||||
|
ImGui::Checkbox("use Cellular", &platesUseCellular);
|
||||||
|
|
||||||
|
if (ImGui::Button("2. Simulate Tectonics", ImVec2(-1, 40))) {
|
||||||
|
simulateTectonics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Celestial Bodies")) {
|
||||||
|
///TODO: add controls for moon, star.
|
||||||
|
if (ImGui::Button("Add Star", ImVec2(-1, 40))) {
|
||||||
|
sim.addStar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Fillings")) {
|
||||||
|
if (ImGui::Button("Interpolate surface", ImVec2(-1, 40))) {
|
||||||
|
interpolateSurface();
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Fill Planet", ImVec2(-1, 40))) {
|
||||||
|
fillPlanet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Debug Views")) {
|
||||||
|
ImGui::Text("3D Planet Color Mode:");
|
||||||
|
bool colorChanged = false;
|
||||||
|
if (ImGui::RadioButton("Base Color", currentColorMode == DebugColorMode::BASE)) {
|
||||||
|
currentColorMode = DebugColorMode::BASE;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Plates", currentColorMode == DebugColorMode::PLATES)) {
|
||||||
|
currentColorMode = DebugColorMode::PLATES;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::RadioButton("Noise", currentColorMode == DebugColorMode::NOISE)) {
|
||||||
|
currentColorMode = DebugColorMode::NOISE;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Reserved", currentColorMode == DebugColorMode::RESERVED)) {
|
||||||
|
currentColorMode = DebugColorMode::RESERVED;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorChanged) {
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("2D Height Map Mode:");
|
||||||
|
bool mapChanged = false;
|
||||||
|
|
||||||
|
if (ImGui::RadioButton("None", currentMapMode == DebugMapMode::NONE)) {
|
||||||
|
currentMapMode = DebugMapMode::NONE;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Base Pos", currentMapMode == DebugMapMode::BASE)) {
|
||||||
|
currentMapMode = DebugMapMode::BASE;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Noise Pos", currentMapMode == DebugMapMode::NOISE)) {
|
||||||
|
currentMapMode = DebugMapMode::NOISE;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tectonicGenned) ImGui::BeginDisabled();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Tectonic Pos", currentMapMode == DebugMapMode::TECTONIC)) {
|
||||||
|
currentMapMode = DebugMapMode::TECTONIC;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::RadioButton("Tectonic Color", currentMapMode == DebugMapMode::TECTONICCOLOR)) {
|
||||||
|
currentMapMode = DebugMapMode::TECTONICCOLOR;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (!tectonicGenned) ImGui::EndDisabled();
|
||||||
|
|
||||||
|
if (ImGui::RadioButton("Current Pos", currentMapMode == DebugMapMode::CURRENT)) {
|
||||||
|
currentMapMode = DebugMapMode::CURRENT;
|
||||||
|
mapChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Refresh Map", ImVec2(-1, 24))) {
|
||||||
|
mapChanged = true;
|
||||||
|
generateDebugMap(currentMapMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapChanged && currentMapMode != DebugMapMode::NONE) {
|
||||||
|
generateDebugMap(currentMapMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMapMode != DebugMapMode::NONE && mapTexture != 0) {
|
||||||
|
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
ImGui::Image((void*)(intptr_t)mapTexture, ImVec2(availWidth, availWidth * 0.5f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Camera Controls", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::DragFloat3("Origin", cam.origin.data());
|
||||||
|
ImGui::DragFloat3("Direction", cam.direction.data(), 0.0001f, -1.0f, 1.0f);
|
||||||
|
ImGui::DragFloat("Movement Speed", &cam.movementSpeed, 0.1f, 1.0f, 500.0f);
|
||||||
|
ImGui::DragFloat("Rotation Speed", &cam.rotationSpeed, M_1_PI, M_1_PI, M_PI);
|
||||||
|
ImGui::InputFloat("Rotation Distance", &rotationRadius, 10, 100);
|
||||||
|
ImGui::InputFloat("Max Framerate", &framerate, 1, 10);
|
||||||
|
|
||||||
|
ImGui::Checkbox("Use Slower Render", &slowRender);
|
||||||
|
|
||||||
|
if (ImGui::Button("Focus on Planet")) {
|
||||||
|
v3 target(sim.config.center);
|
||||||
|
v3 newDir = (target - cam.origin).normalized();
|
||||||
|
cam.direction = newDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(orbitEquator ? "Stop Equator" : "Orbit Equator")) orbitEquator = !orbitEquator;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatsCache();
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderPreviewPanel() {
|
||||||
|
ImGui::BeginChild("PreviewChild", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
|
||||||
|
livePreview();
|
||||||
|
if (textureInitialized) {
|
||||||
|
float aspect = (float)currentPreviewFrame.getWidth() / (float)currentPreviewFrame.getHeight();
|
||||||
|
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
ImGui::Image((void*)(intptr_t)textu, ImVec2(availWidth, availWidth / aspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyDebugColorMode() {
|
||||||
|
if (sim.config.surfaceNodes.empty()) return;
|
||||||
|
|
||||||
|
float minNoise = std::numeric_limits<float>::max();
|
||||||
|
float maxNoise = std::numeric_limits<float>::lowest();
|
||||||
|
int minSub = std::numeric_limits<int>::max();
|
||||||
|
int maxSub = std::numeric_limits<int>::lowest();
|
||||||
|
|
||||||
|
for (const auto& p : sim.config.surfaceNodes) {
|
||||||
|
if (p.noiseDisplacement < minNoise) minNoise = p.noiseDisplacement;
|
||||||
|
if (p.noiseDisplacement > maxNoise) maxNoise = p.noiseDisplacement;
|
||||||
|
}
|
||||||
|
int snf = 0;
|
||||||
|
int inf = 0;
|
||||||
|
|
||||||
|
for (auto& p : sim.config.surfaceNodes) {
|
||||||
|
v3 color = p.originColor.cast<float>();
|
||||||
|
|
||||||
|
switch (currentColorMode) {
|
||||||
|
case DebugColorMode::PLATES:
|
||||||
|
if (p.plateID != -1 && p.plateID < sim.plates.size()) {
|
||||||
|
color = sim.plates[p.plateID].debugColor;
|
||||||
|
} else {
|
||||||
|
color = v3(0.5f, 0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DebugColorMode::NOISE: {
|
||||||
|
float t = 0.5f;
|
||||||
|
if (maxNoise > minNoise) t = (p.noiseDisplacement - minNoise) / (maxNoise - minNoise);
|
||||||
|
color = v3(t, t, t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DebugColorMode::BASE:
|
||||||
|
default:
|
||||||
|
color = p.originColor.cast<float>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sim.grid.setColor(p.currentPos, color)) {
|
||||||
|
snf++;
|
||||||
|
}
|
||||||
|
// sim.grid.update(p.currentPos, p.currentPos, p, true, color, sim.config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
for (auto& p : sim.config.interpolatedNodes) {
|
||||||
|
v3 color = p.originColor.cast<float>();
|
||||||
|
|
||||||
|
switch (currentColorMode) {
|
||||||
|
case DebugColorMode::PLATES:
|
||||||
|
if (p.plateID != -1 && p.plateID < sim.plates.size()) {
|
||||||
|
color = sim.plates[p.plateID].debugColor;
|
||||||
|
} else {
|
||||||
|
color = v3(0.5f, 0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DebugColorMode::NOISE: {
|
||||||
|
float t = 0.5f;
|
||||||
|
if (maxNoise > minNoise) t = (p.noiseDisplacement - minNoise) / (maxNoise - minNoise);
|
||||||
|
color = v3(t, t, t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DebugColorMode::BASE:
|
||||||
|
default:
|
||||||
|
color = p.originColor.cast<float>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sim.grid.setColor(p.currentPos, color)) {
|
||||||
|
inf++;
|
||||||
|
}
|
||||||
|
// sim.grid.update(p.currentPos, p.currentPos, p, true, color, sim.config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
if (snf > 0 || inf > 0) {
|
||||||
|
std::cout << snf << " original nodes failed to set" << std::endl;
|
||||||
|
std::cout << inf << " interpolated nodes failed to set" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateDebugMap(DebugMapMode mode) {
|
||||||
|
if (mode == DebugMapMode::NONE || sim.config.surfaceNodes.empty()) return;
|
||||||
|
|
||||||
|
int w = 512;
|
||||||
|
int h = 348;
|
||||||
|
std::vector<float> depths(w * h, -1.0f);
|
||||||
|
float minD = std::numeric_limits<float>::max();
|
||||||
|
float maxD = std::numeric_limits<float>::lowest();
|
||||||
|
|
||||||
|
for (const auto& p : sim.config.surfaceNodes) {
|
||||||
|
v3 pos;
|
||||||
|
switch(mode) {
|
||||||
|
case DebugMapMode::BASE:
|
||||||
|
pos = p.altPos->originalPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::NOISE:
|
||||||
|
pos = p.altPos->noisePos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::TECTONIC:
|
||||||
|
pos = p.altPos->tectonicPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::CURRENT:
|
||||||
|
default:
|
||||||
|
pos = p.currentPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
float d = pos.norm();
|
||||||
|
if (d < minD) minD = d;
|
||||||
|
if (d > maxD) maxD = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& p : sim.config.surfaceNodes) {
|
||||||
|
v3 pos;
|
||||||
|
switch(mode) {
|
||||||
|
case DebugMapMode::BASE:
|
||||||
|
pos = p.altPos->originalPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::NOISE:
|
||||||
|
pos = p.altPos->noisePos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::TECTONIC:
|
||||||
|
pos = p.altPos->tectonicPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::TECTONICCOLOR:
|
||||||
|
pos = sim.plates[p.plateID].debugColor;
|
||||||
|
break;
|
||||||
|
case DebugMapMode::CURRENT:
|
||||||
|
default:
|
||||||
|
pos = p.currentPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float d = pos.norm();
|
||||||
|
v3 n = p.altPos->originalPos.cast<float>().normalized();
|
||||||
|
|
||||||
|
float u = 0.5f + std::atan2(n.z(), n.x()) / (2.0f * static_cast<float>(M_PI));
|
||||||
|
float v = 0.5f - std::asin(n.y()) / static_cast<float>(M_PI);
|
||||||
|
|
||||||
|
int px = std::clamp(static_cast<int>(u * w), 0, w - 1);
|
||||||
|
int py = std::clamp(static_cast<int>(v * h), 0, h - 1);
|
||||||
|
|
||||||
|
float normalizedD = (maxD > minD) ? (d - minD) / (maxD - minD) : 0.5f;
|
||||||
|
|
||||||
|
for (int dy = -1; dy <= 1; dy++) {
|
||||||
|
for (int dx = -1; dx <= 1; dx++) {
|
||||||
|
int nx = px + dx;
|
||||||
|
int ny = py + dy;
|
||||||
|
|
||||||
|
if (nx < 0) nx += w;
|
||||||
|
if (nx >= w) nx -= w;
|
||||||
|
|
||||||
|
if (ny >= 0 && ny < h) {
|
||||||
|
int idx = ny * w + nx;
|
||||||
|
if (depths[idx] < 0.0f || normalizedD > depths[idx]) {
|
||||||
|
depths[idx] = normalizedD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < w * h; i++) {
|
||||||
|
if (depths[i] < 0.0f) depths[i] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels(w * h * 3);
|
||||||
|
for (int i = 0; i < w * h; i++) {
|
||||||
|
uint8_t val = static_cast<uint8_t>(depths[i] * 255.0f);
|
||||||
|
pixels[i * 3 + 0] = val;
|
||||||
|
pixels[i * 3 + 1] = val;
|
||||||
|
pixels[i * 3 + 2] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapFrame = frame(w, h, frame::colormap::RGB);
|
||||||
|
mapFrame.setData(pixels);
|
||||||
|
|
||||||
|
if (mapTexture == 0) {
|
||||||
|
glGenTextures(1, &mapTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mapTexture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, mapFrame.getData().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyNoise(const NoisePreviewState& noiseState) {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
auto triplanarNoise = [&](const Eigen::Vector3f& pos) -> float {
|
||||||
|
PNoise2 gen(noiseState.masterSeed);
|
||||||
|
|
||||||
|
Eigen::Vector3f n = pos.normalized();
|
||||||
|
Eigen::Vector3f blend = n.cwiseAbs();
|
||||||
|
float sum = blend.x() + blend.y() + blend.z();
|
||||||
|
blend /= sum;
|
||||||
|
|
||||||
|
Eigen::Vector3f offsetPos = pos + Eigen::Vector3f(noiseState.offset[0], noiseState.offset[1], 0.0f);
|
||||||
|
float vXY = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.x(), offsetPos.y()), noiseState, gen);
|
||||||
|
float vXZ = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.x(), offsetPos.z()), noiseState, gen);
|
||||||
|
float vYZ = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.y(), offsetPos.z()), noiseState, gen);
|
||||||
|
|
||||||
|
// Blend results
|
||||||
|
return vYZ * blend.x() + vXZ * blend.y() + vXY * blend.z();
|
||||||
|
};
|
||||||
|
sim._applyNoise(triplanarNoise);
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void livePreview() {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
updatePreview = true;
|
||||||
|
|
||||||
|
sim.grid.setLODMinDistance(lodDist);
|
||||||
|
sim.grid.setLODFalloff(lodDropoff);
|
||||||
|
sim.grid.setMaxDistance(maxViewDistance);
|
||||||
|
float invFrameRate = 1 / framerate;
|
||||||
|
|
||||||
|
if (slowRender) {
|
||||||
|
currentPreviewFrame = sim.grid.renderFrameTimed(cam, outHeight, outWidth, frame::colormap::RGB, invFrameRate, reflectCount, globalIllumination, useLod);
|
||||||
|
} else {
|
||||||
|
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outHeight, outWidth, frame::colormap::RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textu == 0) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||||
|
0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||||
|
|
||||||
|
updatePreview = false;
|
||||||
|
textureInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetView() {
|
||||||
|
cam.origin = Vector3f(sim.config.gridSizeCube, sim.config.gridSizeCube, sim.config.gridSizeCube);
|
||||||
|
Vector3f center(sim.config.gridSizeCube / 2.0f, sim.config.gridSizeCube / 2.0f, sim.config.gridSizeCube / 2.0f);
|
||||||
|
cam.lookAt(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
void simulateTectonics() {
|
||||||
|
|
||||||
|
currentColorMode = DebugColorMode::PLATES;
|
||||||
|
|
||||||
|
sim.assignSeeds();
|
||||||
|
sim.buildAdjacencyList();
|
||||||
|
if (platesUseCellular) {
|
||||||
|
sim.growPlatesCellular();
|
||||||
|
} else sim.growPlatesRandom();
|
||||||
|
if (doFixPlates) sim.fixBoundaries();
|
||||||
|
sim.extraplateste();
|
||||||
|
sim.boundaryStress();
|
||||||
|
sim.finalizeApplyResults();
|
||||||
|
|
||||||
|
applyDebugColorMode();
|
||||||
|
tectonicGenned = true;
|
||||||
|
if(currentMapMode != DebugMapMode::NONE) generateDebugMap(currentMapMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void interpolateSurface() {
|
||||||
|
sim.interpolateSurface();
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillPlanet() {
|
||||||
|
sim.fillPlanet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatsCache() {
|
||||||
|
std::stringstream gridstats;
|
||||||
|
sim.grid.printStats(gridstats);
|
||||||
|
cachedStats = gridstats.str();
|
||||||
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
133
tests/ptest.cpp
Normal file
133
tests/ptest.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
|
||||||
|
#include "../util/noise/pnoise.cpp"
|
||||||
|
#include "planet.cpp"
|
||||||
|
#include "../util/basicdefines.hpp"
|
||||||
|
|
||||||
|
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#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), "StupidSim", 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);
|
||||||
|
|
||||||
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
|
planetSimUI planetApp;
|
||||||
|
NoisePreviewState noiseState;
|
||||||
|
|
||||||
|
if (noiseState.layers.empty()) {
|
||||||
|
NoiseLayer defaultLayer;
|
||||||
|
strcpy(defaultLayer.name, "Base Terrain");
|
||||||
|
defaultLayer.type = NoiseType::Fractal;
|
||||||
|
noiseState.layers.push_back(defaultLayer);
|
||||||
|
}
|
||||||
|
updateNoiseTexture(noiseState);
|
||||||
|
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
glfwPollEvents();
|
||||||
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
ImGui_ImplGlfw_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
ImGui::GetMainViewport();
|
||||||
|
drawNoiseLab(noiseState);
|
||||||
|
planetApp.renderUI(window);
|
||||||
|
|
||||||
|
ImGui::Begin("Integration Control");
|
||||||
|
ImGui::Text("Bridge: Noise Lab -> Planet Sim");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f), "Current Noise Layers: %zu", noiseState.layers.size());
|
||||||
|
|
||||||
|
if (ImGui::Button("APPLY CURRENT NOISE TO PLANET", ImVec2(-1, 50))) {
|
||||||
|
planetApp.applyNoise(noiseState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
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,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
|
|
||||||
305
util/basicdefines.hpp
Normal file
305
util/basicdefines.hpp
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
#include <limits>
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846f
|
||||||
|
#endif
|
||||||
|
#ifndef EPSILON
|
||||||
|
#define EPSILON 0.0000000000000000000000001f
|
||||||
|
#endif
|
||||||
|
#ifndef INF
|
||||||
|
#define INF 2 ^ 31 - 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MARCHTABLES
|
||||||
|
#define MARCHTABLES
|
||||||
|
int edgeTable[256] = {
|
||||||
|
0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
||||||
|
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
||||||
|
0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
||||||
|
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
||||||
|
0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
||||||
|
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
||||||
|
0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
||||||
|
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
||||||
|
0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
|
||||||
|
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
||||||
|
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
|
||||||
|
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
||||||
|
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
|
||||||
|
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
||||||
|
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
|
||||||
|
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
||||||
|
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
||||||
|
0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
||||||
|
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
||||||
|
0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
||||||
|
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
||||||
|
0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
||||||
|
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
||||||
|
0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
|
||||||
|
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
||||||
|
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
|
||||||
|
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
||||||
|
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
|
||||||
|
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
||||||
|
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
|
||||||
|
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
||||||
|
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0};
|
||||||
|
|
||||||
|
int triTable[256][16] =
|
||||||
|
{{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
||||||
|
{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
|
||||||
|
{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
|
||||||
|
{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
||||||
|
{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
|
||||||
|
{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
|
||||||
|
{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
|
||||||
|
{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
||||||
|
{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
|
||||||
|
{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
|
||||||
|
{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
||||||
|
{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
|
||||||
|
{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
|
||||||
|
{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
|
||||||
|
{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
|
||||||
|
{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
|
||||||
|
{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
|
||||||
|
{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
||||||
|
{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
|
||||||
|
{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
||||||
|
{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
|
||||||
|
{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
|
||||||
|
{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
|
||||||
|
{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
|
||||||
|
{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
|
||||||
|
{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
||||||
|
{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
|
||||||
|
{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
|
||||||
|
{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
|
||||||
|
{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
||||||
|
{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
|
||||||
|
{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
|
||||||
|
{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
||||||
|
{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
|
||||||
|
{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
|
||||||
|
{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
|
||||||
|
{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
|
||||||
|
{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
|
||||||
|
{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
||||||
|
{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
|
||||||
|
{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
||||||
|
{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
|
||||||
|
{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
|
||||||
|
{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
|
||||||
|
{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
|
||||||
|
{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
|
||||||
|
{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
|
||||||
|
{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
|
||||||
|
{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
|
||||||
|
{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
|
||||||
|
{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
|
||||||
|
{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
|
||||||
|
{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
|
||||||
|
{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
|
||||||
|
{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
|
||||||
|
{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
|
||||||
|
{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
|
||||||
|
{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
|
||||||
|
{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
|
||||||
|
{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
|
||||||
|
{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
|
||||||
|
{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
|
||||||
|
{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
|
||||||
|
{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
|
||||||
|
{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
|
||||||
|
{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
|
||||||
|
{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
|
||||||
|
{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
|
||||||
|
{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
|
||||||
|
{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
|
||||||
|
{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
|
||||||
|
{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
|
||||||
|
{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
|
||||||
|
{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
|
||||||
|
{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
|
||||||
|
{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
|
||||||
|
{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
|
||||||
|
{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
|
||||||
|
{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
|
||||||
|
{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
|
||||||
|
{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
|
||||||
|
{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
|
||||||
|
{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
|
||||||
|
{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
|
||||||
|
{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
|
||||||
|
{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
|
||||||
|
{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
|
||||||
|
{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
|
||||||
|
{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
|
||||||
|
{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
|
||||||
|
{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
|
||||||
|
{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
|
||||||
|
{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
|
||||||
|
{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
|
||||||
|
{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
|
||||||
|
{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
|
||||||
|
{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
|
||||||
|
{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
|
||||||
|
{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
|
||||||
|
{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
|
||||||
|
{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
|
||||||
|
{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
|
||||||
|
{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
|
||||||
|
{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
|
||||||
|
{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
|
||||||
|
{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
|
||||||
|
{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
|
||||||
|
{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
||||||
|
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
|
||||||
|
#endif
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
#include "zstd.hpp"
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// Implementation of ZSTD_CompressStream methods
|
|
||||||
size_t ZSTD_CompressStream::compress_continue(const void* src, void* dst, size_t srcSize) {
|
|
||||||
// For compatibility with the original interface where dst size is inferred
|
|
||||||
size_t dstCapacity = ZSTD_compressBound(srcSize);
|
|
||||||
return this->compress(src, srcSize, dst, dstCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation of ZSTD_DecompressStream methods
|
|
||||||
size_t ZSTD_DecompressStream::decompress_continue(const void* src, void* dst, size_t srcSize) {
|
|
||||||
// Note: srcSize parameter is actually the destination buffer size
|
|
||||||
// This matches the confusing usage in the original VoxelOctree code
|
|
||||||
return this->decompress(src, srcSize, dst, srcSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions for the compression system
|
|
||||||
namespace {
|
|
||||||
// Simple hash function for LZ77-style matching
|
|
||||||
uint32_t computeHash(const uint8_t* data) {
|
|
||||||
return (data[0] << 16) | (data[1] << 8) | data[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// More advanced compression implementation
|
|
||||||
size_t ZSTD_CompressStream::compress(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
if (srcSize == 0 || dstCapacity == 0) return 0;
|
|
||||||
|
|
||||||
const uint8_t* srcBytes = static_cast<const uint8_t*>(src);
|
|
||||||
uint8_t* dstBytes = static_cast<uint8_t*>(dst);
|
|
||||||
|
|
||||||
size_t dstPos = 0;
|
|
||||||
size_t srcPos = 0;
|
|
||||||
|
|
||||||
// Simple LZ77 compression
|
|
||||||
while (srcPos < srcSize && dstPos + 3 < dstCapacity) {
|
|
||||||
// Try to find a match in previous data
|
|
||||||
size_t bestMatchPos = 0;
|
|
||||||
size_t bestMatchLen = 0;
|
|
||||||
|
|
||||||
// Look for matches in recent data (simplified search window)
|
|
||||||
size_t searchStart = (srcPos > 1024) ? srcPos - 1024 : 0;
|
|
||||||
for (size_t i = searchStart; i < srcPos && i + 3 <= srcSize; i++) {
|
|
||||||
if (srcBytes[i] == srcBytes[srcPos]) {
|
|
||||||
size_t matchLen = 1;
|
|
||||||
while (srcPos + matchLen < srcSize &&
|
|
||||||
i + matchLen < srcPos &&
|
|
||||||
srcBytes[i + matchLen] == srcBytes[srcPos + matchLen] &&
|
|
||||||
matchLen < 255) {
|
|
||||||
matchLen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchLen > bestMatchLen && matchLen >= 4) {
|
|
||||||
bestMatchLen = matchLen;
|
|
||||||
bestMatchPos = srcPos - i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestMatchLen >= 4) {
|
|
||||||
// Encode match
|
|
||||||
dstBytes[dstPos++] = 0x80 | ((bestMatchLen - 4) & 0x7F);
|
|
||||||
dstBytes[dstPos++] = (bestMatchPos >> 8) & 0xFF;
|
|
||||||
dstBytes[dstPos++] = bestMatchPos & 0xFF;
|
|
||||||
srcPos += bestMatchLen;
|
|
||||||
} else {
|
|
||||||
// Encode literals
|
|
||||||
size_t literalStart = srcPos;
|
|
||||||
size_t maxLiteral = std::min(srcSize - srcPos, size_t(127));
|
|
||||||
|
|
||||||
// Find run of non-compressible data
|
|
||||||
while (srcPos - literalStart < maxLiteral) {
|
|
||||||
// Check if next few bytes could be compressed
|
|
||||||
bool canCompress = false;
|
|
||||||
if (srcPos + 3 < srcSize) {
|
|
||||||
// Simple heuristic: repeated bytes or patterns
|
|
||||||
if (srcBytes[srcPos] == srcBytes[srcPos + 1] &&
|
|
||||||
srcBytes[srcPos] == srcBytes[srcPos + 2]) {
|
|
||||||
canCompress = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (canCompress) break;
|
|
||||||
srcPos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t literalLength = srcPos - literalStart;
|
|
||||||
if (literalLength > 0) {
|
|
||||||
if (dstPos + literalLength + 1 > dstCapacity) {
|
|
||||||
// Not enough space
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dstBytes[dstPos++] = literalLength & 0x7F;
|
|
||||||
memcpy(dstBytes + dstPos, srcBytes + literalStart, literalLength);
|
|
||||||
dstPos += literalLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ZSTD_DecompressStream::decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
if (srcSize == 0 || dstCapacity == 0) return 0;
|
|
||||||
|
|
||||||
const uint8_t* srcBytes = static_cast<const uint8_t*>(src);
|
|
||||||
uint8_t* dstBytes = static_cast<uint8_t*>(dst);
|
|
||||||
|
|
||||||
size_t srcPos = 0;
|
|
||||||
size_t dstPos = 0;
|
|
||||||
|
|
||||||
while (srcPos < srcSize && dstPos < dstCapacity) {
|
|
||||||
uint8_t header = srcBytes[srcPos++];
|
|
||||||
|
|
||||||
if (header & 0x80) {
|
|
||||||
// Match
|
|
||||||
if (srcPos + 1 >= srcSize) break;
|
|
||||||
|
|
||||||
size_t matchLen = (header & 0x7F) + 4;
|
|
||||||
uint16_t matchDist = (srcBytes[srcPos] << 8) | srcBytes[srcPos + 1];
|
|
||||||
srcPos += 2;
|
|
||||||
|
|
||||||
if (matchDist == 0 || matchDist > dstPos) {
|
|
||||||
// Invalid match distance
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t toCopy = std::min(matchLen, dstCapacity - dstPos);
|
|
||||||
size_t matchPos = dstPos - matchDist;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < toCopy; i++) {
|
|
||||||
dstBytes[dstPos++] = dstBytes[matchPos + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toCopy < matchLen) {
|
|
||||||
break; // Output buffer full
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Literals
|
|
||||||
size_t literalLength = header;
|
|
||||||
if (srcPos + literalLength > srcSize) break;
|
|
||||||
|
|
||||||
size_t toCopy = std::min(literalLength, dstCapacity - dstPos);
|
|
||||||
memcpy(dstBytes + dstPos, srcBytes + srcPos, toCopy);
|
|
||||||
srcPos += toCopy;
|
|
||||||
dstPos += toCopy;
|
|
||||||
|
|
||||||
if (toCopy < literalLength) {
|
|
||||||
break; // Output buffer full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstPos;
|
|
||||||
}
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
#ifndef ZSTD_HPP
|
|
||||||
#define ZSTD_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Simple ZSTD compression implementation (simplified version)
|
|
||||||
class ZSTD_Stream {
|
|
||||||
public:
|
|
||||||
virtual ~ZSTD_Stream() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ZSTD_CompressStream : public ZSTD_Stream {
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> buffer;
|
|
||||||
size_t position;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ZSTD_CompressStream() : position(0) {
|
|
||||||
buffer.reserve(1024 * 1024); // 1MB initial capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
buffer.clear();
|
|
||||||
position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t compress(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
// Simplified compression - in reality this would use actual ZSTD algorithm
|
|
||||||
// For this example, we'll just copy with simple RLE-like compression
|
|
||||||
|
|
||||||
if (dstCapacity < srcSize) {
|
|
||||||
return 0; // Not enough space
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* srcBytes = static_cast<const uint8_t*>(src);
|
|
||||||
uint8_t* dstBytes = static_cast<uint8_t*>(dst);
|
|
||||||
|
|
||||||
size_t dstPos = 0;
|
|
||||||
size_t srcPos = 0;
|
|
||||||
|
|
||||||
while (srcPos < srcSize && dstPos + 2 < dstCapacity) {
|
|
||||||
// Simple RLE compression for repeated bytes
|
|
||||||
uint8_t current = srcBytes[srcPos];
|
|
||||||
size_t runLength = 1;
|
|
||||||
|
|
||||||
while (srcPos + runLength < srcSize &&
|
|
||||||
srcBytes[srcPos + runLength] == current &&
|
|
||||||
runLength < 127) {
|
|
||||||
runLength++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runLength > 3) {
|
|
||||||
// Encode as RLE
|
|
||||||
dstBytes[dstPos++] = 0x80 | (runLength & 0x7F);
|
|
||||||
dstBytes[dstPos++] = current;
|
|
||||||
srcPos += runLength;
|
|
||||||
} else {
|
|
||||||
// Encode as literal run
|
|
||||||
size_t literalStart = srcPos;
|
|
||||||
while (srcPos < srcSize &&
|
|
||||||
(srcPos - literalStart < 127) &&
|
|
||||||
(srcPos + 1 >= srcSize ||
|
|
||||||
srcBytes[srcPos] != srcBytes[srcPos + 1] ||
|
|
||||||
srcPos + 2 >= srcSize ||
|
|
||||||
srcBytes[srcPos] != srcBytes[srcPos + 2])) {
|
|
||||||
srcPos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t literalLength = srcPos - literalStart;
|
|
||||||
if (dstPos + literalLength + 1 > dstCapacity) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dstBytes[dstPos++] = literalLength & 0x7F;
|
|
||||||
memcpy(dstBytes + dstPos, srcBytes + literalStart, literalLength);
|
|
||||||
dstPos += literalLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t compress_continue(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
return compress(src, srcSize, dst, dstCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8_t>& getBuffer() const { return buffer; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ZSTD_DecompressStream : public ZSTD_Stream {
|
|
||||||
private:
|
|
||||||
size_t position;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ZSTD_DecompressStream() : position(0) {}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
const uint8_t* srcBytes = static_cast<const uint8_t*>(src);
|
|
||||||
uint8_t* dstBytes = static_cast<uint8_t*>(dst);
|
|
||||||
|
|
||||||
size_t srcPos = 0;
|
|
||||||
size_t dstPos = 0;
|
|
||||||
|
|
||||||
while (srcPos < srcSize && dstPos < dstCapacity) {
|
|
||||||
uint8_t header = srcBytes[srcPos++];
|
|
||||||
|
|
||||||
if (header & 0x80) {
|
|
||||||
// RLE encoded
|
|
||||||
size_t runLength = header & 0x7F;
|
|
||||||
if (srcPos >= srcSize) break;
|
|
||||||
|
|
||||||
uint8_t value = srcBytes[srcPos++];
|
|
||||||
|
|
||||||
size_t toCopy = std::min(runLength, dstCapacity - dstPos);
|
|
||||||
memset(dstBytes + dstPos, value, toCopy);
|
|
||||||
dstPos += toCopy;
|
|
||||||
|
|
||||||
if (toCopy < runLength) {
|
|
||||||
break; // Output buffer full
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Literal data
|
|
||||||
size_t literalLength = header;
|
|
||||||
if (srcPos + literalLength > srcSize) break;
|
|
||||||
|
|
||||||
size_t toCopy = std::min(literalLength, dstCapacity - dstPos);
|
|
||||||
memcpy(dstBytes + dstPos, srcBytes + srcPos, toCopy);
|
|
||||||
srcPos += toCopy;
|
|
||||||
dstPos += toCopy;
|
|
||||||
|
|
||||||
if (toCopy < literalLength) {
|
|
||||||
break; // Output buffer full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t decompress_continue(const void* src, size_t srcSize, void* dst, size_t dstCapacity) {
|
|
||||||
return decompress(src, srcSize, dst, dstCapacity);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Type definitions for compatibility with original code
|
|
||||||
using ZSTD_stream_t = ZSTD_CompressStream;
|
|
||||||
|
|
||||||
// Stream creation functions
|
|
||||||
inline ZSTD_CompressStream* ZSTD_createStream() {
|
|
||||||
return new ZSTD_CompressStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ZSTD_DecompressStream* ZSTD_createStreamDecode() {
|
|
||||||
return new ZSTD_DecompressStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream free functions
|
|
||||||
inline void ZSTD_freeStream(ZSTD_Stream* stream) {
|
|
||||||
delete stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ZSTD_freeStreamDecode(ZSTD_Stream* stream) {
|
|
||||||
delete stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream reset functions
|
|
||||||
inline void ZSTD_resetStream(ZSTD_CompressStream* stream) {
|
|
||||||
if (stream) stream->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ZSTD_setStreamDecode(ZSTD_DecompressStream* stream, const void* dict, size_t dictSize) {
|
|
||||||
if (stream) stream->reset();
|
|
||||||
// Note: dict parameter is ignored in this simplified version
|
|
||||||
(void)dict;
|
|
||||||
(void)dictSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compression functions
|
|
||||||
inline size_t ZSTD_compressBound(size_t srcSize) {
|
|
||||||
// Worst case: no compression + 1 byte header per 127 bytes
|
|
||||||
return srcSize + (srcSize / 127) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t ZSTD_Compress_continue(ZSTD_CompressStream* stream,
|
|
||||||
const void* src, void* dst,
|
|
||||||
size_t srcSize) {
|
|
||||||
if (!stream) return 0;
|
|
||||||
return stream->compress_continue(src, srcSize, dst, ZSTD_compressBound(srcSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t ZSTD_Decompress_continue(ZSTD_DecompressStream* stream,
|
|
||||||
const void* src, void* dst,
|
|
||||||
size_t srcSize) {
|
|
||||||
if (!stream) return 0;
|
|
||||||
// Note: srcSize is actually the destination size in the original code
|
|
||||||
// This is confusing but matches the usage in VoxelOctree
|
|
||||||
return stream->decompress_continue(src, srcSize, dst, srcSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ZSTD_HPP
|
|
||||||
268
util/fp8.hpp
268
util/fp8.hpp
@@ -1,268 +0,0 @@
|
|||||||
// fp8.hpp
|
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cmath>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#ifdef __CUDACC__
|
|
||||||
#include <cuda_fp16.h>
|
|
||||||
#include <cuda_runtime.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class fp8_e4m3 {
|
|
||||||
private:
|
|
||||||
uint8_t data;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructors
|
|
||||||
__host__ __device__ fp8_e4m3() : data(0) {}
|
|
||||||
|
|
||||||
__host__ __device__ explicit fp8_e4m3(uint8_t val) : data(val) {}
|
|
||||||
|
|
||||||
// Conversion from float32
|
|
||||||
__host__ __device__ explicit fp8_e4m3(float f) {
|
|
||||||
#ifdef __CUDACC__
|
|
||||||
data = float_to_fp8(f);
|
|
||||||
#else
|
|
||||||
data = cpu_float_to_fp8(f);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversion from float16 (CUDA only)
|
|
||||||
#ifdef __CUDACC__
|
|
||||||
__host__ __device__ explicit fp8_e4m3(__half h) {
|
|
||||||
data = half_to_fp8(h);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Conversion to float32
|
|
||||||
__host__ __device__ operator float() const {
|
|
||||||
#ifdef __CUDACC__
|
|
||||||
return fp8_to_float(data);
|
|
||||||
#else
|
|
||||||
return cpu_fp8_to_float(data);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic operators
|
|
||||||
__host__ __device__ fp8_e4m3 operator+(const fp8_e4m3& other) const {
|
|
||||||
return fp8_e4m3(float(*this) + float(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3 operator-(const fp8_e4m3& other) const {
|
|
||||||
return fp8_e4m3(float(*this) - float(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3 operator*(const fp8_e4m3& other) const {
|
|
||||||
return fp8_e4m3(float(*this) * float(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3 operator/(const fp8_e4m3& other) const {
|
|
||||||
return fp8_e4m3(float(*this) / float(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compound assignment operators
|
|
||||||
__host__ __device__ fp8_e4m3& operator+=(const fp8_e4m3& other) {
|
|
||||||
*this = fp8_e4m3(float(*this) + float(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3& operator-=(const fp8_e4m3& other) {
|
|
||||||
*this = fp8_e4m3(float(*this) - float(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3& operator*=(const fp8_e4m3& other) {
|
|
||||||
*this = fp8_e4m3(float(*this) * float(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ fp8_e4m3& operator/=(const fp8_e4m3& other) {
|
|
||||||
*this = fp8_e4m3(float(*this) / float(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparison operators
|
|
||||||
__host__ __device__ bool operator==(const fp8_e4m3& other) const {
|
|
||||||
// Handle NaN and ±0.0 cases
|
|
||||||
if ((data & 0x7F) == 0x7F) return false; // NaN
|
|
||||||
if (data == other.data) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ bool operator!=(const fp8_e4m3& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ bool operator<(const fp8_e4m3& other) const {
|
|
||||||
return float(*this) < float(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ bool operator>(const fp8_e4m3& other) const {
|
|
||||||
return float(*this) > float(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ bool operator<=(const fp8_e4m3& other) const {
|
|
||||||
return float(*this) <= float(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ bool operator>=(const fp8_e4m3& other) const {
|
|
||||||
return float(*this) >= float(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get raw data
|
|
||||||
__host__ __device__ uint8_t get_raw() const { return data; }
|
|
||||||
|
|
||||||
// Special values
|
|
||||||
__host__ __device__ static fp8_e4m3 zero() { return fp8_e4m3(0x00); }
|
|
||||||
__host__ __device__ static fp8_e4m3 one() { return fp8_e4m3(0x3C); } // 1.0
|
|
||||||
__host__ __device__ static fp8_e4m3 nan() { return fp8_e4m3(0x7F); }
|
|
||||||
__host__ __device__ static fp8_e4m3 inf() { return fp8_e4m3(0x78); } // +inf
|
|
||||||
__host__ __device__ static fp8_e4m3 neg_inf() { return fp8_e4m3(0xF8); } // -inf
|
|
||||||
|
|
||||||
// Memory operations
|
|
||||||
__host__ __device__ static void memcpy(void* dst, const void* src, size_t count) {
|
|
||||||
::memcpy(dst, src, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ static void memset(void* ptr, int value, size_t count) {
|
|
||||||
::memset(ptr, value, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// CPU implementation (fast bit manipulation)
|
|
||||||
__host__ __device__ static uint8_t cpu_float_to_fp8(float f) {
|
|
||||||
uint32_t f_bits;
|
|
||||||
memcpy(&f_bits, &f, sizeof(float));
|
|
||||||
|
|
||||||
uint32_t sign = (f_bits >> 31) & 0x1;
|
|
||||||
int32_t exp = ((f_bits >> 23) & 0xFF) - 127;
|
|
||||||
uint32_t mantissa = f_bits & 0x7FFFFF;
|
|
||||||
|
|
||||||
// Handle special cases
|
|
||||||
if (exp == 128) { // NaN or Inf
|
|
||||||
return (sign << 7) | 0x7F; // Preserve sign for NaN/Inf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Denormal handling
|
|
||||||
if (exp < -6) {
|
|
||||||
return sign << 7; // Underflow to zero
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clamp exponent to e4m3 range [-6, 7]
|
|
||||||
if (exp > 7) {
|
|
||||||
return (sign << 7) | 0x78; // Overflow to inf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to fp8 format
|
|
||||||
uint32_t fp8_exp = (exp + 6) & 0xF; // Bias: -6 -> 0, 7 -> 13
|
|
||||||
uint32_t fp8_mant = mantissa >> 20; // Keep top 3 bits
|
|
||||||
|
|
||||||
// Round to nearest even
|
|
||||||
uint32_t rounding_bit = (mantissa >> 19) & 1;
|
|
||||||
uint32_t sticky_bits = (mantissa & 0x7FFFF) ? 1 : 0;
|
|
||||||
if (rounding_bit && (fp8_mant & 1 || sticky_bits)) {
|
|
||||||
fp8_mant++;
|
|
||||||
if (fp8_mant > 0x7) { // Mantissa overflow
|
|
||||||
fp8_mant = 0;
|
|
||||||
fp8_exp++;
|
|
||||||
if (fp8_exp > 0xF) { // Exponent overflow
|
|
||||||
return (sign << 7) | 0x78; // Infinity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sign << 7) | (fp8_exp << 3) | (fp8_mant & 0x7);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ static float cpu_fp8_to_float(uint8_t fp8) {
|
|
||||||
uint32_t sign = (fp8 >> 7) & 0x1;
|
|
||||||
uint32_t exp = (fp8 >> 3) & 0xF;
|
|
||||||
uint32_t mant = fp8 & 0x7;
|
|
||||||
|
|
||||||
// Handle special cases
|
|
||||||
if (exp == 0xF) { // NaN or Inf
|
|
||||||
uint32_t f_bits = (sign << 31) | (0xFF << 23) | (mant << 20);
|
|
||||||
float result;
|
|
||||||
memcpy(&result, &f_bits, sizeof(float));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exp == 0) {
|
|
||||||
// Denormal/subnormal
|
|
||||||
if (mant == 0) return sign ? -0.0f : 0.0f;
|
|
||||||
// Convert denormal
|
|
||||||
exp = -6;
|
|
||||||
mant = mant << 1;
|
|
||||||
} else {
|
|
||||||
exp -= 6; // Remove bias
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to float32
|
|
||||||
uint32_t f_exp = (exp + 127) & 0xFF;
|
|
||||||
uint32_t f_mant = mant << 20;
|
|
||||||
uint32_t f_bits = (sign << 31) | (f_exp << 23) | f_mant;
|
|
||||||
|
|
||||||
float result;
|
|
||||||
memcpy(&result, &f_bits, sizeof(float));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CUDA implementation (using intrinsics when available)
|
|
||||||
#ifdef __CUDACC__
|
|
||||||
__device__ static uint8_t float_to_fp8(float f) {
|
|
||||||
#if __CUDA_ARCH__ >= 890 // Hopper+ has native FP8 support
|
|
||||||
return __float_to_fp8_rn(f);
|
|
||||||
#else
|
|
||||||
return cpu_float_to_fp8(f);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
__device__ static float fp8_to_float(uint8_t fp8) {
|
|
||||||
#if __CUDA_ARCH__ >= 890
|
|
||||||
return __fp8_to_float(fp8);
|
|
||||||
#else
|
|
||||||
return cpu_fp8_to_float(fp8);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
__device__ static uint8_t half_to_fp8(__half h) {
|
|
||||||
return float_to_fp8(__half2float(h));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// For non-CUDA, use CPU versions
|
|
||||||
__host__ __device__ static uint8_t float_to_fp8(float f) {
|
|
||||||
return cpu_float_to_fp8(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
__host__ __device__ static float fp8_to_float(uint8_t fp8) {
|
|
||||||
return cpu_fp8_to_float(fp8);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Vectorized operations for performance
|
|
||||||
namespace fp8_ops {
|
|
||||||
// Convert array of floats to fp8 (efficient batch conversion)
|
|
||||||
static void convert_float_to_fp8(uint8_t* dst, const float* src, size_t count) {
|
|
||||||
#pragma omp parallel for simd if(count > 1024)
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
|
||||||
dst[i] = fp8_e4m3(src[i]).get_raw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert array of fp8 to floats
|
|
||||||
static void convert_fp8_to_float(float* dst, const uint8_t* src, size_t count) {
|
|
||||||
#pragma omp parallel for simd if(count > 1024)
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
|
||||||
dst[i] = fp8_e4m3(src[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct memory operations
|
|
||||||
static void memset_fp8(void* ptr, fp8_e4m3 value, size_t count) {
|
|
||||||
uint8_t val = value.get_raw();
|
|
||||||
::memset(ptr, val, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
155
util/grid/camera.hpp
Normal file
155
util/grid/camera.hpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#ifndef camera_hpp
|
||||||
|
#define camera_hpp
|
||||||
|
|
||||||
|
#include "../../eigen/Eigen/Dense"
|
||||||
|
#include <cmath>
|
||||||
|
#include "../basicdefines.hpp"
|
||||||
|
|
||||||
|
using Eigen::Vector3f;
|
||||||
|
using Eigen::Matrix3f;
|
||||||
|
|
||||||
|
struct Camera {
|
||||||
|
Vector3f origin;
|
||||||
|
Vector3f direction;
|
||||||
|
Vector3f up;
|
||||||
|
float fov;
|
||||||
|
float movementSpeed;
|
||||||
|
float rotationSpeed;
|
||||||
|
|
||||||
|
Camera() : origin(Vector3f(0,0,0)), direction(Vector3f(0,0,0)), up(Vector3f(0,0,0)), fov(80), movementSpeed(10), rotationSpeed(10) {}
|
||||||
|
|
||||||
|
Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80,
|
||||||
|
float moveSpeed = 1.0f, float rotSpeed = 0.5f)
|
||||||
|
: origin(pos), direction(viewdir.normalized()), up(up.normalized()), fov(fov), movementSpeed(moveSpeed), rotationSpeed(rotSpeed) {}
|
||||||
|
|
||||||
|
void rotateYaw(float angle) {
|
||||||
|
angle *= rotationSpeed;
|
||||||
|
Matrix3f rotation;
|
||||||
|
rotation = Eigen::AngleAxisf(angle, up);
|
||||||
|
direction = rotation * direction;
|
||||||
|
direction.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotatePitch(float angle) {
|
||||||
|
angle *= rotationSpeed;
|
||||||
|
Vector3f right = direction.cross(up).normalized();
|
||||||
|
|
||||||
|
// Rotate around right vector (pitch)
|
||||||
|
Matrix3f rotation;
|
||||||
|
rotation = Eigen::AngleAxisf(angle, right);
|
||||||
|
direction = rotation * direction;
|
||||||
|
direction.normalize();
|
||||||
|
|
||||||
|
// Recalculate up vector to maintain orthogonality
|
||||||
|
up = right.cross(direction).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveForward(float distance) {
|
||||||
|
origin += forward() * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveBackward(float distance) {
|
||||||
|
origin -= forward() * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveRight(float distance) {
|
||||||
|
origin += right() * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveLeft(float distance) {
|
||||||
|
origin -= right() * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveUp(float distance) {
|
||||||
|
origin += up * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveDown(float distance) {
|
||||||
|
origin -= up * distance * movementSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f forward() const {
|
||||||
|
return direction.normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f right() const {
|
||||||
|
return forward().cross(up).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
float fovRad() const {
|
||||||
|
return fov * (M_PI / 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at a specific point
|
||||||
|
void lookAt(const Vector3f& target) {
|
||||||
|
direction = (target - origin).normalized();
|
||||||
|
|
||||||
|
// Recalculate up vector
|
||||||
|
Vector3f worldUp(0, 1, 0);
|
||||||
|
if (direction.cross(worldUp).norm() < 0.001f) {
|
||||||
|
worldUp = Vector3f(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f right = direction.cross(worldUp).normalized();
|
||||||
|
up = right.cross(direction).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set position directly
|
||||||
|
void setPosition(const Vector3f& pos) {
|
||||||
|
origin = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set view direction directly
|
||||||
|
void setDirection(const Vector3f& dir) {
|
||||||
|
direction = dir.normalized();
|
||||||
|
// Recalculate up
|
||||||
|
Vector3f worldUp(0, 1, 0);
|
||||||
|
Vector3f right = direction.cross(worldUp).normalized();
|
||||||
|
up = right.cross(direction).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get view matrix (lookAt matrix)
|
||||||
|
Eigen::Matrix4f getViewMatrix() const {
|
||||||
|
Vector3f f = forward();
|
||||||
|
Vector3f r = right();
|
||||||
|
Vector3f u = up;
|
||||||
|
|
||||||
|
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
|
||||||
|
|
||||||
|
view(0, 0) = r.x(); view(0, 1) = r.y(); view(0, 2) = r.z();
|
||||||
|
view(1, 0) = u.x(); view(1, 1) = u.y(); view(1, 2) = u.z();
|
||||||
|
view(2, 0) = -f.x(); view(2, 1) = -f.y(); view(2, 2) = -f.z();
|
||||||
|
|
||||||
|
view(0, 3) = -r.dot(origin);
|
||||||
|
view(1, 3) = -u.dot(origin);
|
||||||
|
view(2, 3) = f.dot(origin);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get projection matrix (perspective)
|
||||||
|
Eigen::Matrix4f getProjectionMatrix(float aspectRatio, float nearPlane = 0.1f, float farPlane = 1000.0f) const {
|
||||||
|
float fovrad = fovRad();
|
||||||
|
float tanHalfFov = tan(fovrad / 2.0f);
|
||||||
|
|
||||||
|
Eigen::Matrix4f projection = Eigen::Matrix4f::Zero();
|
||||||
|
|
||||||
|
projection(0, 0) = 1.0f / (aspectRatio * tanHalfFov);
|
||||||
|
projection(1, 1) = 1.0f / tanHalfFov;
|
||||||
|
projection(2, 2) = -(farPlane + nearPlane) / (farPlane - nearPlane);
|
||||||
|
projection(3, 2) = -1.0f;
|
||||||
|
projection(2, 3) = -(2.0f * farPlane * nearPlane) / (farPlane - nearPlane);
|
||||||
|
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseLook(float deltaX, float deltaY) {
|
||||||
|
float yaw = -deltaX * 0.001f;
|
||||||
|
float pitch = -deltaY * 0.001f;
|
||||||
|
|
||||||
|
rotateYaw(yaw);
|
||||||
|
rotatePitch(pitch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
1825
util/grid/grid2.hpp
1825
util/grid/grid2.hpp
File diff suppressed because it is too large
Load Diff
1705
util/grid/grid3.hpp
1705
util/grid/grid3.hpp
File diff suppressed because it is too large
Load Diff
@@ -1,118 +0,0 @@
|
|||||||
#ifndef GRID_HPP
|
|
||||||
#define GRID_HPP
|
|
||||||
|
|
||||||
#include "../vectorlogic/vec3.hpp"
|
|
||||||
#include "../vectorlogic/vec4.hpp"
|
|
||||||
#include "../noise/pnoise2.hpp"
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
//template <typename DataType, int Dimension>
|
|
||||||
class Node {
|
|
||||||
//static_assert(Dimension == 2 || Dimension == 3, "Dimensions must be 2 or 3");
|
|
||||||
private:
|
|
||||||
public:
|
|
||||||
NodeType type;
|
|
||||||
Vec3f minBounds;
|
|
||||||
Vec3f maxBounds;
|
|
||||||
Vec4ui8 color;
|
|
||||||
enum NodeType {
|
|
||||||
BRANCH,
|
|
||||||
LEAF
|
|
||||||
};
|
|
||||||
std::array<Node, 8> children;
|
|
||||||
Node(const Vec3f min, const Vec3f max, NodeType t = LEAF) : type(t), minBounds(min), maxBounds(max), color(0,0,0,0) {}
|
|
||||||
|
|
||||||
Vec3f getCenter() const {
|
|
||||||
return (minBounds + maxBounds) * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getChildIndex(const Vec3f& pos) const {
|
|
||||||
Vec3f c = getCenter();
|
|
||||||
int index = 0;
|
|
||||||
if (pos.x >= c.x) index |= 1;
|
|
||||||
if (pos.y >= c.y) index |= 2;
|
|
||||||
if (pos.z >= c.z) index |= 4;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Vec3f, Vec3f> getChildBounds(int childIndex) const {
|
|
||||||
Vec3f c = getCenter();
|
|
||||||
Vec3f cMin = minBounds;
|
|
||||||
Vec3f cMax = maxBounds;
|
|
||||||
if (childIndex & 1) cMin.x = c.x;
|
|
||||||
else cMax.x = c.x;
|
|
||||||
|
|
||||||
if (childIndex & 2) cMin.y = c.y;
|
|
||||||
else cMax.y = c.y;
|
|
||||||
|
|
||||||
if (childIndex & 4) cMin.z = c.z;
|
|
||||||
else cMax.z = c.z;
|
|
||||||
|
|
||||||
return {cMin, cMax};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Grid {
|
|
||||||
private:
|
|
||||||
Node root;
|
|
||||||
Vec4ui8 DefaultBackgroundColor;
|
|
||||||
PNoise2 noisegen;
|
|
||||||
std::unordered_map<Vec3f, Node, Vec3f::Hash> Cache;
|
|
||||||
public:
|
|
||||||
Grid() : {
|
|
||||||
root = Node(Vec3f(0), Vec3f(0), Node::NodeType::BRANCH);
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t insertVoxel(const Vec3f& pos, const Vec4ui8& color) {
|
|
||||||
if (!contains(pos)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return insertRecusive(root.get(), pos, color, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeVoxel(const Vec3f& pos) {
|
|
||||||
bool removed = removeRecursive(root.get(), pos, 0);
|
|
||||||
if (removed) {
|
|
||||||
Cache.erase(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec4ui8 getVoxel(const Vec3f& pos) const {
|
|
||||||
Node* node = findNode(root.get(), pos, 0);
|
|
||||||
if (node && node->isLeaf()) {
|
|
||||||
return node->color;
|
|
||||||
}
|
|
||||||
return DefaultBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasVoxel(const Vec3f& pos) const {
|
|
||||||
Node* node = findNode(root.get(), pos, 0);
|
|
||||||
return node && node->isLeaf();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rayIntersect(const Ray3& ray, Vec3f& hitPos, Vec4ui8& hitColor) const {
|
|
||||||
return rayintersectRecursive(root.get(), ray, hitPos, hitColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<Vec3f, Vec4ui8> queryRegion(const Vec3f& min, const Vec3f& max) const {
|
|
||||||
std::unordered_map<Vec3f, Vec4ui8> result;
|
|
||||||
queryRegionRecuse(root.get(), min, max, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Grid& noiseGen(const Vec3f& min, const Vec3f& max, float minc = 0.1f, float maxc = 1.0f, bool genColor = true, int noiseMod = 42) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
noisegen = PNoise2(noiseMod);
|
|
||||||
std::vector<Vec3f> poses;
|
|
||||||
std::vector<Vec4ui8> colors;
|
|
||||||
|
|
||||||
size_t estimatedSize = (max.x - min.x) * (max.y - min.y) * (max.z - min.z) * (maxc - minc);
|
|
||||||
poses.reserve(estimatedSize);
|
|
||||||
colors.reserve(estimatedSize);
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
2285
util/grid/grid3eigen.hpp
Normal file
2285
util/grid/grid3eigen.hpp
Normal file
File diff suppressed because it is too large
Load Diff
253
util/grid/gridmesh.hpp
Normal file
253
util/grid/gridmesh.hpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#ifndef GRIDMESH_HPP
|
||||||
|
#define GRIDMESH_HPP
|
||||||
|
|
||||||
|
#include "grid3eigen.hpp"
|
||||||
|
#include "mesh.hpp"
|
||||||
|
#include "../basicdefines.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
struct GridPoint {
|
||||||
|
Eigen::Vector3f pos;
|
||||||
|
float val;
|
||||||
|
Eigen::Vector3f color; // Interpolated color field
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridCell {
|
||||||
|
GridPoint p[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class MetaballMesher {
|
||||||
|
private:
|
||||||
|
float _resolution;
|
||||||
|
float _isoLevel;
|
||||||
|
float _influenceRadius;
|
||||||
|
|
||||||
|
void vertexInterp(float isolevel, GridPoint p1, GridPoint p2, Eigen::Vector3f& outPos, Eigen::Vector3f& outColor) {
|
||||||
|
if (std::abs(p1.val - p2.val) < 0.00001f) {
|
||||||
|
outPos = p1.pos;
|
||||||
|
outColor = p1.color;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float mu = (isolevel - p1.val) / (p2.val - p1.val);
|
||||||
|
outPos = p1.pos + mu * (p2.pos - p1.pos);
|
||||||
|
outColor = p1.color + mu * (p2.color - p1.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sampleField(const Eigen::Vector3f& pos, const std::vector<std::shared_ptr<typename Octree<T>::NodeData>>& nodes, float& outVal, Eigen::Vector3f& outColor) {
|
||||||
|
float sumVal = 0.0f;
|
||||||
|
Eigen::Vector3f sumColor = Eigen::Vector3f::Zero();
|
||||||
|
float totalWeight = 0.0f;
|
||||||
|
|
||||||
|
for (const auto& node : nodes) {
|
||||||
|
float r = node->size * _influenceRadius;
|
||||||
|
float distSq = (pos - node->position).squaredNorm();
|
||||||
|
|
||||||
|
if (distSq < r * r) {
|
||||||
|
float dist = std::sqrt(distSq);
|
||||||
|
float x = dist / r;
|
||||||
|
float val = std::pow(1.0f - x*x, 3.0f);
|
||||||
|
|
||||||
|
sumVal += val;
|
||||||
|
sumColor += node->color * val;
|
||||||
|
totalWeight += val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outVal = sumVal;
|
||||||
|
if (totalWeight > 0.0001f) {
|
||||||
|
outColor = sumColor / totalWeight;
|
||||||
|
} else {
|
||||||
|
outColor = Eigen::Vector3f(1.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
MetaballMesher(float resolution = 0.5f, float isoLevel = 0.5f, float influenceMult = 2.5f)
|
||||||
|
: _resolution(resolution), _isoLevel(isoLevel), _influenceRadius(influenceMult) {}
|
||||||
|
|
||||||
|
void polygonize(const std::vector<std::shared_ptr<typename Octree<T>::NodeData>>& nodes,
|
||||||
|
std::vector<Eigen::Vector3f>& outVerts,
|
||||||
|
std::vector<Eigen::Vector3f>& outColors,
|
||||||
|
std::vector<int>& outIndices,
|
||||||
|
int& startIndex) {
|
||||||
|
|
||||||
|
if (nodes.empty()) return;
|
||||||
|
|
||||||
|
Eigen::Vector3f minBounds(1e9, 1e9, 1e9);
|
||||||
|
Eigen::Vector3f maxBounds(-1e9, -1e9, -1e9);
|
||||||
|
float maxNodeSize = 0;
|
||||||
|
|
||||||
|
for (const auto& node : nodes) {
|
||||||
|
minBounds = minBounds.cwiseMin(node->position);
|
||||||
|
maxBounds = maxBounds.cwiseMax(node->position);
|
||||||
|
if (node->size > maxNodeSize) maxNodeSize = node->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
float padding = maxNodeSize * _influenceRadius;
|
||||||
|
minBounds -= Eigen::Vector3f(padding, padding, padding);
|
||||||
|
maxBounds += Eigen::Vector3f(padding, padding, padding);
|
||||||
|
|
||||||
|
float step = maxNodeSize * _resolution;
|
||||||
|
if (step <= 0.0001f) step = 0.1f;
|
||||||
|
|
||||||
|
int stepsX = static_cast<int>((maxBounds.x() - minBounds.x()) / step);
|
||||||
|
int stepsY = static_cast<int>((maxBounds.y() - minBounds.y()) / step);
|
||||||
|
int stepsZ = static_cast<int>((maxBounds.z() - minBounds.z()) / step);
|
||||||
|
|
||||||
|
for (int x = 0; x < stepsX; ++x) {
|
||||||
|
for (int y = 0; y < stepsY; ++y) {
|
||||||
|
for (int z = 0; z < stepsZ; ++z) {
|
||||||
|
|
||||||
|
Eigen::Vector3f basePos = minBounds + Eigen::Vector3f(x*step, y*step, z*step);
|
||||||
|
GridCell cell;
|
||||||
|
|
||||||
|
Eigen::Vector3f offsets[8] = {
|
||||||
|
{0,0,0}, {step,0,0}, {step,0,step}, {0,0,step},
|
||||||
|
{0,step,0}, {step,step,0}, {step,step,step}, {0,step,step}
|
||||||
|
};
|
||||||
|
|
||||||
|
int cubeIndex = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
cell.p[i].pos = basePos + offsets[i];
|
||||||
|
sampleField(cell.p[i].pos, nodes, cell.p[i].val, cell.p[i].color);
|
||||||
|
if (cell.p[i].val < _isoLevel) cubeIndex |= (1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up edges
|
||||||
|
if (edgeTable[cubeIndex] == 0) continue;
|
||||||
|
if (edgeTable[cubeIndex] == 255) continue; // Inside or Outside completely
|
||||||
|
|
||||||
|
Eigen::Vector3f vertList[12];
|
||||||
|
Eigen::Vector3f colorList[12];
|
||||||
|
|
||||||
|
if (edgeTable[cubeIndex] & 1) vertexInterp(_isoLevel, cell.p[0], cell.p[1], vertList[0], colorList[0]);
|
||||||
|
if (edgeTable[cubeIndex] & 2) vertexInterp(_isoLevel, cell.p[1], cell.p[2], vertList[1], colorList[1]);
|
||||||
|
if (edgeTable[cubeIndex] & 4) vertexInterp(_isoLevel, cell.p[2], cell.p[3], vertList[2], colorList[2]);
|
||||||
|
if (edgeTable[cubeIndex] & 8) vertexInterp(_isoLevel, cell.p[3], cell.p[0], vertList[3], colorList[3]);
|
||||||
|
if (edgeTable[cubeIndex] & 16) vertexInterp(_isoLevel, cell.p[4], cell.p[5], vertList[4], colorList[4]);
|
||||||
|
if (edgeTable[cubeIndex] & 32) vertexInterp(_isoLevel, cell.p[5], cell.p[6], vertList[5], colorList[5]);
|
||||||
|
if (edgeTable[cubeIndex] & 64) vertexInterp(_isoLevel, cell.p[6], cell.p[7], vertList[6], colorList[6]);
|
||||||
|
if (edgeTable[cubeIndex] & 128) vertexInterp(_isoLevel, cell.p[7], cell.p[4], vertList[7], colorList[7]);
|
||||||
|
if (edgeTable[cubeIndex] & 256) vertexInterp(_isoLevel, cell.p[0], cell.p[4], vertList[8], colorList[8]);
|
||||||
|
if (edgeTable[cubeIndex] & 512) vertexInterp(_isoLevel, cell.p[1], cell.p[5], vertList[9], colorList[9]);
|
||||||
|
if (edgeTable[cubeIndex] & 1024) vertexInterp(_isoLevel, cell.p[2], cell.p[6], vertList[10], colorList[10]);
|
||||||
|
if (edgeTable[cubeIndex] & 2048) vertexInterp(_isoLevel, cell.p[3], cell.p[7], vertList[11], colorList[11]);
|
||||||
|
|
||||||
|
for (int i = 0; triTable[cubeIndex][i] != -1; i += 3) {
|
||||||
|
outVerts.push_back(vertList[triTable[cubeIndex][i]]);
|
||||||
|
outVerts.push_back(vertList[triTable[cubeIndex][i+1]]);
|
||||||
|
outVerts.push_back(vertList[triTable[cubeIndex][i+2]]);
|
||||||
|
|
||||||
|
outColors.push_back(colorList[triTable[cubeIndex][i]]);
|
||||||
|
outColors.push_back(colorList[triTable[cubeIndex][i+1]]);
|
||||||
|
outColors.push_back(colorList[triTable[cubeIndex][i+2]]);
|
||||||
|
|
||||||
|
outIndices.push_back(startIndex++);
|
||||||
|
outIndices.push_back(startIndex++);
|
||||||
|
outIndices.push_back(startIndex++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class CachedGridMesh {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Mesh> mesh_;
|
||||||
|
int id_;
|
||||||
|
|
||||||
|
float _resolution = 0.5f;
|
||||||
|
float _isoLevel = 0.4f;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CachedGridMesh(int id = 0) : id_(id) {
|
||||||
|
mesh_ = std::make_unique<Mesh>(id_, std::vector<Vector3f>{}, std::vector<std::vector<int>>{}, std::vector<Color>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setResolution(float res) { _resolution = res; }
|
||||||
|
void setIsoLevel(float iso) { _isoLevel = iso; }
|
||||||
|
|
||||||
|
void update(Octree<T>& grid, const Eigen::Vector3f& center = Eigen::Vector3f::Zero(), float radius = std::numeric_limits<float>::max()) {
|
||||||
|
std::vector<Vector3f> allVertices;
|
||||||
|
std::vector<std::vector<int>> allPolys;
|
||||||
|
std::vector<Color> allColors;
|
||||||
|
|
||||||
|
auto nodes = grid.findInRadius(center, radius);
|
||||||
|
if (nodes.empty()) {
|
||||||
|
mesh_ = std::make_unique<Mesh>(id_, allVertices, allPolys, allColors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unordered_map<int, std::vector<std::shared_ptr<typename Octree<T>::NodeData>>> objectGroups;
|
||||||
|
|
||||||
|
for (const auto& node : nodes) {
|
||||||
|
if (!node->active || !node->visible) continue;
|
||||||
|
objectGroups[node->objectId].push_back(node);
|
||||||
|
}
|
||||||
|
std::cout << "object map made" << std::endl;
|
||||||
|
|
||||||
|
MetaballMesher<T> mesher(_resolution, _isoLevel);
|
||||||
|
|
||||||
|
int globalVertIndex = 0;
|
||||||
|
|
||||||
|
for (auto& [objId, groupNodes] : objectGroups) {
|
||||||
|
std::vector<Eigen::Vector3f> objVerts;
|
||||||
|
std::vector<Eigen::Vector3f> objColors;
|
||||||
|
std::vector<int> objIndices;
|
||||||
|
int startIndex = globalVertIndex;
|
||||||
|
int localVertCounter = 0;
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> mVerts;
|
||||||
|
std::vector<Eigen::Vector3f> mColors;
|
||||||
|
std::vector<int> mIndices;
|
||||||
|
int mIdxStart = 0;
|
||||||
|
|
||||||
|
mesher.polygonize(groupNodes, mVerts, mColors, mIndices, mIdxStart);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mVerts.size(); ++i) {
|
||||||
|
allVertices.push_back(mVerts[i]);
|
||||||
|
allColors.push_back(mColors[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mIndices.size(); i += 3) {
|
||||||
|
std::vector<int> tri = {
|
||||||
|
mIndices[i] + globalVertIndex,
|
||||||
|
mIndices[i+1] + globalVertIndex,
|
||||||
|
mIndices[i+2] + globalVertIndex
|
||||||
|
};
|
||||||
|
allPolys.push_back(tri);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalVertIndex += mVerts.size();
|
||||||
|
std::cout << "object done" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_ = std::make_unique<Mesh>(id_, allVertices, allPolys, allColors);
|
||||||
|
mesh_->triangulate();
|
||||||
|
}
|
||||||
|
|
||||||
|
frame render(Camera cam, int height, int width, float near = 0.1f, float far = 1000.0f, frame::colormap format = frame::colormap::RGB) {
|
||||||
|
if (!mesh_) {
|
||||||
|
return frame(width, height, format);
|
||||||
|
}
|
||||||
|
return mesh_->renderFrame(cam, height, width, near, far, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh* getMesh() {
|
||||||
|
return mesh_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getVertexCount() const {
|
||||||
|
return mesh_ ? mesh_->vertices().size() : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
462
util/grid/mesh.hpp
Normal file
462
util/grid/mesh.hpp
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
#ifndef MESH_HPP
|
||||||
|
#define MESH_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include "../../eigen/Eigen/Dense"
|
||||||
|
#include "../../eigen/Eigen/Geometry"
|
||||||
|
#include "./camera.hpp"
|
||||||
|
#include "../output/frame.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"
|
||||||
|
|
||||||
|
using Vector2f = Eigen::Vector2f;
|
||||||
|
using Vector3f = Eigen::Vector3f;
|
||||||
|
using Vector4f = Eigen::Vector4f;
|
||||||
|
using Matrix4f = Eigen::Matrix4f;
|
||||||
|
using Color = Eigen::Vector3f;
|
||||||
|
|
||||||
|
struct Triangle2D {
|
||||||
|
Vector2f a, b, c;
|
||||||
|
Color color;
|
||||||
|
float depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mesh {
|
||||||
|
private:
|
||||||
|
int id;
|
||||||
|
int _subId;
|
||||||
|
std::vector<Vector3f> _vertices;
|
||||||
|
std::vector<std::vector<int>> _polys;
|
||||||
|
std::vector<Color> _colors;
|
||||||
|
mutable std::vector<Eigen::Vector3i> _tris;
|
||||||
|
mutable std::vector<Vector3f> _triNormals;
|
||||||
|
mutable bool _needs_triangulation = true;
|
||||||
|
mutable bool _needs_norm_calc = true;
|
||||||
|
|
||||||
|
inline static float edgeFunction(const Vector2f& a, const Vector2f& b, const Vector2f& c) {
|
||||||
|
return (c.x() - a.x()) * (b.y() - a.y()) - (c.y() - a.y()) * (b.x() - a.x());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Mesh(int id, const std::vector<Vector3f>& verts, const std::vector<std::vector<int>>& polys, const std::vector<Color>& colors, int subId = 0)
|
||||||
|
: id(id), _subId(subId), _vertices(verts), _polys(polys), _colors(colors) {
|
||||||
|
_needs_triangulation = true;
|
||||||
|
_needs_norm_calc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getId() const {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSubId() const {
|
||||||
|
return _subId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSubId(int s) {
|
||||||
|
_subId = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vector3f> vertices() {
|
||||||
|
return _vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateVertex(size_t index, const Vector3f& newPos) {
|
||||||
|
if (index < _vertices.size()) {
|
||||||
|
_vertices[index] = newPos;
|
||||||
|
_needs_norm_calc = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate(const Vector3f& offset) {
|
||||||
|
for(auto& v : _vertices) {
|
||||||
|
v += offset;
|
||||||
|
}
|
||||||
|
_needs_norm_calc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replace(const std::vector<Vector3f>& verts, const std::vector<std::vector<int>>& polys, const std::vector<Color>& colors) {
|
||||||
|
_vertices = verts;
|
||||||
|
_polys = polys;
|
||||||
|
_colors = colors;
|
||||||
|
_needs_triangulation = true;
|
||||||
|
_needs_norm_calc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertices(std::vector<Vector3f> verts) {
|
||||||
|
if (verts.size() != _colors.size() && _colors.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_vertices = verts;
|
||||||
|
_needs_norm_calc = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> polys() {
|
||||||
|
return _polys;
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangulate() {
|
||||||
|
if (!_needs_triangulation) return;
|
||||||
|
std::vector<Eigen::Vector3i> newPols;
|
||||||
|
newPols.reserve(_polys.size() * 2);
|
||||||
|
for (auto& pol : _polys) {
|
||||||
|
if (pol.size() > 3) {
|
||||||
|
auto v0 = pol[0];
|
||||||
|
for (size_t i = 1; i < pol.size() - 1; i++) {
|
||||||
|
newPols.emplace_back(Eigen::Vector3i{v0, pol[i], pol[i+1]});
|
||||||
|
}
|
||||||
|
} else if (pol.size() == 3) {
|
||||||
|
Eigen::Vector3i tri{pol[0], pol[1], pol[2]};
|
||||||
|
newPols.emplace_back(tri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tris = newPols;
|
||||||
|
|
||||||
|
_polys.clear();
|
||||||
|
_polys.shrink_to_fit();
|
||||||
|
|
||||||
|
_needs_triangulation = false;
|
||||||
|
_needs_norm_calc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateNormals() {
|
||||||
|
if (!_needs_norm_calc) return;
|
||||||
|
|
||||||
|
_triNormals.clear();
|
||||||
|
_triNormals.reserve(_tris.size());
|
||||||
|
|
||||||
|
for (const auto& tri : _tris) {
|
||||||
|
Vector3f v0 = _vertices[tri.x()];
|
||||||
|
Vector3f v1 = _vertices[tri.y()];
|
||||||
|
Vector3f v2 = _vertices[tri.z()];
|
||||||
|
|
||||||
|
Vector3f edge1 = v1 - v0;
|
||||||
|
Vector3f edge2 = v2 - v0;
|
||||||
|
|
||||||
|
// Normalized cross product gives the face normal
|
||||||
|
Vector3f normal = edge1.cross(edge2).normalized();
|
||||||
|
_triNormals.push_back(normal);
|
||||||
|
}
|
||||||
|
_needs_norm_calc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Color> colors() {
|
||||||
|
return _colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool colors(std::vector<Color> colorlist) {
|
||||||
|
if (colorlist.size() == 1 || colorlist.size() == _vertices.size()) {
|
||||||
|
_colors = colorlist;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Triangle2D> project_2d(Camera cam, int height, int width, float near, float far) {
|
||||||
|
triangulate();
|
||||||
|
calculateNormals();
|
||||||
|
|
||||||
|
std::vector<Triangle2D> renderList;
|
||||||
|
renderList.reserve(_tris.size());
|
||||||
|
|
||||||
|
Vector3f forward = cam.forward();
|
||||||
|
Vector3f right = cam.right();
|
||||||
|
Vector3f up = cam.up;
|
||||||
|
|
||||||
|
Matrix4f viewMatrixa = Matrix4f::Identity();
|
||||||
|
viewMatrixa.block<3,1>(0,0) = right;
|
||||||
|
viewMatrixa.block<3,1>(0,1) = up;
|
||||||
|
viewMatrixa.block<3,1>(0,2) = -forward;
|
||||||
|
viewMatrixa.block<3,1>(0,3) = cam.origin;
|
||||||
|
|
||||||
|
Matrix4f viewMatrix = viewMatrixa.inverse();
|
||||||
|
|
||||||
|
float aspect = float(width) / float(height);
|
||||||
|
float fovrad = cam.fovRad();
|
||||||
|
float tanHalfFov = std::tan(fovrad / 2.0f);
|
||||||
|
|
||||||
|
Matrix4f projMatrix = Matrix4f::Zero();
|
||||||
|
projMatrix(0,0) = 1.0f / (aspect * tanHalfFov);
|
||||||
|
projMatrix(1,1) = 1.0f / tanHalfFov;
|
||||||
|
projMatrix(2,2) = -(far + near) / (far - near);
|
||||||
|
projMatrix(2,3) = -(2.0f * far * near) / (far - near);
|
||||||
|
projMatrix(3,2) = -1.0f;
|
||||||
|
|
||||||
|
int n = _vertices.size();
|
||||||
|
|
||||||
|
std::vector<Vector2f> screenCoords(n);
|
||||||
|
std::vector<float> linearDepths(n);
|
||||||
|
std::vector<bool> validVerts(n, false);
|
||||||
|
|
||||||
|
Eigen::MatrixXf homogeneousVerts(n, 4);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
homogeneousVerts.row(i).head<3>() = _vertices[i];
|
||||||
|
homogeneousVerts(i, 3) = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::MatrixXf viewVerts = homogeneousVerts * viewMatrix.transpose();
|
||||||
|
Eigen::MatrixXf clipVerts = viewVerts * projMatrix.transpose();
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
float w = clipVerts(i, 3);
|
||||||
|
|
||||||
|
if (w <= near) {
|
||||||
|
validVerts[i] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float x_ndc = clipVerts(i, 0) / w;
|
||||||
|
float y_ndc = clipVerts(i, 1) / w;
|
||||||
|
|
||||||
|
screenCoords[i].x() = (x_ndc + 1.0f) * 0.5f * width;
|
||||||
|
screenCoords[i].y() = (1.0f - (y_ndc + 1.0f) * 0.5f) * height;
|
||||||
|
|
||||||
|
linearDepths[i] = w;
|
||||||
|
validVerts[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f camPos = cam.origin;
|
||||||
|
size_t triCount = _tris.size();
|
||||||
|
|
||||||
|
for (size_t t = 0; t < triCount; ++t) {
|
||||||
|
const auto& triIdx = _tris[t];
|
||||||
|
|
||||||
|
Vector3f p0 = _vertices[triIdx.x()];
|
||||||
|
Vector3f viewDir = (p0 - camPos).normalized();
|
||||||
|
|
||||||
|
if (viewDir.dot(_triNormals[t]) >= 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i0 = triIdx.x();
|
||||||
|
int i1 = triIdx.y();
|
||||||
|
int i2 = triIdx.z();
|
||||||
|
|
||||||
|
if (!validVerts[i0] || !validVerts[i1] || !validVerts[i2]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangle2D t2d;
|
||||||
|
t2d.a = screenCoords[i0];
|
||||||
|
t2d.b = screenCoords[i1];
|
||||||
|
t2d.c = screenCoords[i2];
|
||||||
|
|
||||||
|
t2d.depth = (linearDepths[i0] + linearDepths[i1] + linearDepths[i2]) / 3.0f;
|
||||||
|
|
||||||
|
// Handle Coloring
|
||||||
|
if (_colors.size() == n) {
|
||||||
|
t2d.color = (_colors[i0] + _colors[i1] + _colors[i2]) / 3.0f;
|
||||||
|
} else if (_colors.size() == 1) {
|
||||||
|
t2d.color = _colors[0];
|
||||||
|
} else {
|
||||||
|
t2d.color = {1.0f, 0.0f, 1.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderList.push_back(t2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(renderList.begin(), renderList.end(), [](const Triangle2D& a, const Triangle2D& b) {
|
||||||
|
return a.depth > b.depth;
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderList;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame renderFrame(Camera cam, int height, int width, float near, float far, frame::colormap colorformat = frame::colormap::RGB) {
|
||||||
|
std::vector<Triangle2D> triangles = project_2d(cam, height, width, near, far);
|
||||||
|
return rasterizeTriangles(triangles, width, height, colorformat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static frame rasterizeTriangles(const std::vector<Triangle2D>& triangles, int width, int height, frame::colormap colorformat) {
|
||||||
|
frame outFrame(width, height, colorformat);
|
||||||
|
std::vector<float> buffer(width * height * 3);
|
||||||
|
|
||||||
|
for (const auto& tri : triangles) {
|
||||||
|
int minX = std::max(0, (int)std::floor(std::min({tri.a.x(), tri.b.x(), tri.c.x()})));
|
||||||
|
int maxX = std::min(width - 1, (int)std::ceil(std::max({tri.a.x(), tri.b.x(), tri.c.x()})));
|
||||||
|
int minY = std::max(0, (int)std::floor(std::min({tri.a.y(), tri.b.y(), tri.c.y()})));
|
||||||
|
int maxY = std::min(height - 1, (int)std::ceil(std::max({tri.a.y(), tri.b.y(), tri.c.y()})));
|
||||||
|
|
||||||
|
float area = edgeFunction(tri.a, tri.b, tri.c);
|
||||||
|
|
||||||
|
for (int y = minY; y <= maxY; ++y) {
|
||||||
|
for (int x = minX; x <= maxX; ++x) {
|
||||||
|
Vector2f p(x + 0.5f, y + 0.5f);
|
||||||
|
|
||||||
|
float w0 = edgeFunction(tri.b, tri.c, p);
|
||||||
|
float w1 = edgeFunction(tri.c, tri.a, p);
|
||||||
|
float w2 = edgeFunction(tri.a, tri.b, p);
|
||||||
|
bool inside = false;
|
||||||
|
|
||||||
|
if (area >= 0) {
|
||||||
|
if (w0 >= 0 && w1 >= 0 && w2 >= 0) inside = true;
|
||||||
|
} else {
|
||||||
|
if (w0 <= 0 && w1 <= 0 && w2 <= 0) inside = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inside) {
|
||||||
|
int index = (y * width + x) * 3;
|
||||||
|
if (index >= 0 && index < buffer.size() - 2) {
|
||||||
|
buffer[index ] = tri.color.x();
|
||||||
|
buffer[index + 1] = tri.color.y();
|
||||||
|
buffer[index + 2] = tri.color.z();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outFrame.setData(buffer, colorformat);
|
||||||
|
return outFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printStats(std::ostream& os = std::cout) const {
|
||||||
|
os << "========================================\n";
|
||||||
|
os << " Mesh STATS \n";
|
||||||
|
os << "========================================\n";
|
||||||
|
os << "Structure:\n";
|
||||||
|
os << " Total Vertices : " << _vertices.size() << "\n";
|
||||||
|
os << " Total Tris : " << _tris.size() << "\n";
|
||||||
|
os << " Tri Normals : " << _triNormals.size() << "\n";
|
||||||
|
if (_needs_triangulation) os << " Total Polys : " << _polys.size() << "\n";
|
||||||
|
else os << " Polys (Cleared) : " << 0 << "\n";
|
||||||
|
os << " colors : " << _colors.size() << "\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Scene {
|
||||||
|
private:
|
||||||
|
std::vector<std::shared_ptr<Mesh>> _meshes;
|
||||||
|
std::string cachedStats;
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
const std::chrono::seconds STATS_UPDATE_INTERVAL{60};
|
||||||
|
public:
|
||||||
|
Scene() {}
|
||||||
|
|
||||||
|
void addMesh(std::shared_ptr<Mesh> mesh) {
|
||||||
|
_meshes.push_back(mesh);
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMeshes(const std::vector<std::shared_ptr<Mesh>>& meshes) {
|
||||||
|
_meshes.insert(_meshes.end(), meshes.begin(), meshes.end());
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_meshes.clear();
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
frame render(Camera cam, int height, int width, float near, float far, frame::colormap colorformat = frame::colormap::RGB) {
|
||||||
|
std::vector<Triangle2D> allTriangles;
|
||||||
|
|
||||||
|
for (auto& mesh : _meshes) {
|
||||||
|
std::vector<Triangle2D> meshTris = mesh->project_2d(cam, height, width, near, far);
|
||||||
|
allTriangles.insert(allTriangles.end(), meshTris.begin(), meshTris.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(allTriangles.begin(), allTriangles.end(), [](const Triangle2D& a, const Triangle2D& b) {
|
||||||
|
return a.depth > b.depth;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Mesh::rasterizeTriangles(allTriangles, width, height, colorformat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawSceneWindow(const char* windowTitle, Camera& cam, float nearClip = 0.1f, float farClip = 1000.0f, bool showFps = true) {
|
||||||
|
ImGui::Begin(windowTitle);
|
||||||
|
|
||||||
|
ImVec2 canvas_p = ImGui::GetCursorScreenPos();
|
||||||
|
ImVec2 canvas_sz = ImGui::GetContentRegionAvail();
|
||||||
|
|
||||||
|
if (canvas_sz.x < 1.0f || canvas_sz.y < 1.0f) {
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
|
draw_list->PushClipRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), true);
|
||||||
|
|
||||||
|
draw_list->AddRectFilled(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(20, 20, 20, 255));
|
||||||
|
|
||||||
|
std::vector<Triangle2D> allTriangles;
|
||||||
|
for (auto& mesh : _meshes) {
|
||||||
|
std::vector<Triangle2D> meshTris = mesh->project_2d(cam, (int)canvas_sz.y, (int)canvas_sz.x, nearClip, farClip);
|
||||||
|
allTriangles.insert(allTriangles.end(), meshTris.begin(), meshTris.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(allTriangles.begin(), allTriangles.end(), [](const Triangle2D& a, const Triangle2D& b) {
|
||||||
|
return a.depth > b.depth;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw triangles
|
||||||
|
for (const auto& tri : allTriangles) {
|
||||||
|
ImVec2 p1(canvas_p.x + tri.a.x(), canvas_p.y + tri.a.y());
|
||||||
|
ImVec2 p2(canvas_p.x + tri.b.x(), canvas_p.y + tri.b.y());
|
||||||
|
ImVec2 p3(canvas_p.x + tri.c.x(), canvas_p.y + tri.c.y());
|
||||||
|
ImU32 col = ImGui::ColorConvertFloat4ToU32(ImVec4(tri.color.x(), tri.color.y(), tri.color.z(), 1.0f));
|
||||||
|
draw_list->AddTriangleFilled(p1, p2, p3, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showFps) {
|
||||||
|
char fpsText[32];
|
||||||
|
snprintf(fpsText, sizeof(fpsText), "FPS: %.1f", ImGui::GetIO().Framerate);
|
||||||
|
|
||||||
|
ImVec2 txtSz = ImGui::CalcTextSize(fpsText);
|
||||||
|
ImVec2 txtPos = ImVec2(canvas_p.x + canvas_sz.x - txtSz.x - 10.0f, canvas_p.y + 10.0f);
|
||||||
|
|
||||||
|
draw_list->AddRectFilled(ImVec2(txtPos.x - 5.0f, txtPos.y - 2.0f),
|
||||||
|
ImVec2(txtPos.x + txtSz.x + 5.0f, txtPos.y + txtSz.y + 2.0f),
|
||||||
|
IM_COL32(0, 0, 0, 150), 4.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_list->AddText(txtPos, IM_COL32(255, 255, 255, 255), fpsText);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_list->PopClipRect();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printStats(std::ostream& os = std::cout) const {
|
||||||
|
os << "========================================\n";
|
||||||
|
os << " Scene STATS \n";
|
||||||
|
os << "========================================\n";
|
||||||
|
os << "Total Meshes: " << _meshes.size() << "\n";
|
||||||
|
for (auto m : _meshes) {
|
||||||
|
m->printStats(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawGridStats() {
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
if ((now - lastStatsUpdate) > STATS_UPDATE_INTERVAL || statsNeedUpdate) {
|
||||||
|
std::stringstream meshStats;
|
||||||
|
printStats(meshStats);
|
||||||
|
cachedStats = meshStats.str();
|
||||||
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
}
|
||||||
|
void updateStats() {
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
#ifndef SPRITE2_HPP
|
|
||||||
#define SPRITE2_HPP
|
|
||||||
|
|
||||||
#include "grid2.hpp"
|
|
||||||
#include "../output/frame.hpp"
|
|
||||||
|
|
||||||
class SpriteMap2 : public Grid2 {
|
|
||||||
private:
|
|
||||||
// id, sprite
|
|
||||||
std::unordered_map<size_t, frame> spritesComped;
|
|
||||||
std::unordered_map<size_t, int> Layers;
|
|
||||||
std::unordered_map<size_t, float> Orientations;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Grid2::Grid2;
|
|
||||||
|
|
||||||
size_t addSprite(const Vec2& pos, frame sprite, int layer = 0, float orientation = 0.0f) {
|
|
||||||
size_t id = addObject(pos, Vec4(0,0,0,0));
|
|
||||||
spritesComped[id] = sprite;
|
|
||||||
Layers[id] = layer;
|
|
||||||
Orientations[id] = orientation;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame getSprite(size_t id) {
|
|
||||||
return spritesComped.at(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSprite(size_t id, const frame& sprite) {
|
|
||||||
spritesComped[id] = sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLayer(size_t id) {
|
|
||||||
return Layers.at(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t setLayer(size_t id, int layer) {
|
|
||||||
Layers[id] = layer;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getOrientation(size_t id) {
|
|
||||||
return Orientations.at(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t setOrientation(size_t id, float orientation) {
|
|
||||||
Orientations[id] = orientation;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getGridRegionAsBGR(const Vec2& minCorner, const Vec2& maxCorner, int& width, int& height, std::vector<uint8_t>& rgbData) const {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
|
|
||||||
// Calculate dimensions
|
|
||||||
width = static_cast<int>(maxCorner.x - minCorner.x);
|
|
||||||
height = static_cast<int>(maxCorner.y - minCorner.y);
|
|
||||||
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
width = height = 0;
|
|
||||||
rgbData.clear();
|
|
||||||
rgbData.shrink_to_fit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize RGBA buffer for compositing
|
|
||||||
std::vector<Vec4> rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
|
||||||
|
|
||||||
// Group sprites by layer for proper rendering order
|
|
||||||
std::vector<std::pair<int, size_t>> layeredSprites;
|
|
||||||
for (const auto& [id, pos] : Positions) {
|
|
||||||
if (spritesComped.find(id) != spritesComped.end()) {
|
|
||||||
layeredSprites.emplace_back(Layers.at(id), id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by layer (lower layers first)
|
|
||||||
std::sort(layeredSprites.begin(), layeredSprites.end(),
|
|
||||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
|
||||||
|
|
||||||
// Render each sprite in layer order
|
|
||||||
for (const auto& [layer, id] : layeredSprites) {
|
|
||||||
const Vec2& pos = Positions.at(id);
|
|
||||||
const frame& sprite = spritesComped.at(id);
|
|
||||||
float orientation = Orientations.at(id);
|
|
||||||
|
|
||||||
// Decompress sprite if needed
|
|
||||||
frame decompressedSprite = sprite;
|
|
||||||
if (sprite.isCompressed()) {
|
|
||||||
decompressedSprite.decompress();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8_t>& spriteData = decompressedSprite.getData();
|
|
||||||
size_t spriteWidth = decompressedSprite.getWidth();
|
|
||||||
size_t spriteHeight = decompressedSprite.getHeight();
|
|
||||||
|
|
||||||
if (spriteData.empty() || spriteWidth == 0 || spriteHeight == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate sprite bounds in world coordinates
|
|
||||||
float halfWidth = spriteWidth / 2.0f;
|
|
||||||
float halfHeight = spriteHeight / 2.0f;
|
|
||||||
|
|
||||||
// Apply rotation if needed
|
|
||||||
// TODO: Implement proper rotation transformation
|
|
||||||
int pixelXm = static_cast<int>(pos.x - halfWidth - minCorner.x);
|
|
||||||
int pixelXM = static_cast<int>(pos.x + halfWidth - minCorner.x);
|
|
||||||
int pixelYm = static_cast<int>(pos.y - halfHeight - minCorner.y);
|
|
||||||
int pixelYM = static_cast<int>(pos.y + halfHeight - minCorner.y);
|
|
||||||
|
|
||||||
// Clamp to output bounds
|
|
||||||
pixelXm = std::max(0, pixelXm);
|
|
||||||
pixelXM = std::min(width - 1, pixelXM);
|
|
||||||
pixelYm = std::max(0, pixelYm);
|
|
||||||
pixelYM = std::min(height - 1, pixelYM);
|
|
||||||
|
|
||||||
// Skip if completely outside bounds
|
|
||||||
if (pixelXm >= width || pixelXM < 0 || pixelYm >= height || pixelYM < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render sprite pixels
|
|
||||||
for (int py = pixelYm; py <= pixelYM; ++py) {
|
|
||||||
for (int px = pixelXm; px <= pixelXM; ++px) {
|
|
||||||
// Calculate sprite-relative coordinates
|
|
||||||
int spriteX = px - pixelXm;
|
|
||||||
int spriteY = py - pixelYm;
|
|
||||||
|
|
||||||
// Skip if outside sprite bounds
|
|
||||||
if (spriteX < 0 || spriteX >= spriteWidth || spriteY < 0 || spriteY >= spriteHeight) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get sprite pixel color based on color format
|
|
||||||
Vec4 spriteColor = getSpritePixelColor(spriteData, spriteX, spriteY, spriteWidth, spriteHeight, decompressedSprite.colorFormat);
|
|
||||||
|
|
||||||
// Alpha blending
|
|
||||||
int bufferIndex = py * width + px;
|
|
||||||
Vec4& dest = rgbaBuffer[bufferIndex];
|
|
||||||
|
|
||||||
float srcAlpha = spriteColor.a;
|
|
||||||
if (srcAlpha > 0.0f) {
|
|
||||||
float invSrcAlpha = 1.0f - srcAlpha;
|
|
||||||
dest.r = spriteColor.r * srcAlpha + dest.r * invSrcAlpha;
|
|
||||||
dest.g = spriteColor.g * srcAlpha + dest.g * invSrcAlpha;
|
|
||||||
dest.b = spriteColor.b * srcAlpha + dest.b * invSrcAlpha;
|
|
||||||
dest.a = srcAlpha + dest.a * invSrcAlpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also render regular colored objects (from base class)
|
|
||||||
for (const auto& [id, pos] : Positions) {
|
|
||||||
// Skip if this is a sprite (already rendered above)
|
|
||||||
if (spritesComped.find(id) != spritesComped.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size = Sizes.at(id);
|
|
||||||
|
|
||||||
// Calculate pixel coordinates for colored objects
|
|
||||||
int pixelXm = static_cast<int>(pos.x - size/2 - minCorner.x);
|
|
||||||
int pixelXM = static_cast<int>(pos.x + size/2 - minCorner.x);
|
|
||||||
int pixelYm = static_cast<int>(pos.y - size/2 - minCorner.y);
|
|
||||||
int pixelYM = static_cast<int>(pos.y + size/2 - minCorner.y);
|
|
||||||
|
|
||||||
pixelXm = std::max(0, pixelXm);
|
|
||||||
pixelXM = std::min(width - 1, pixelXM);
|
|
||||||
pixelYm = std::max(0, pixelYm);
|
|
||||||
pixelYM = std::min(height - 1, pixelYM);
|
|
||||||
|
|
||||||
// Ensure within bounds
|
|
||||||
if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) {
|
|
||||||
const Vec4& color = Colors.at(id);
|
|
||||||
float srcAlpha = color.a;
|
|
||||||
for (int py = pixelYm; py <= pixelYM; ++py) {
|
|
||||||
for (int px = pixelXm; px <= pixelXM; ++px) {
|
|
||||||
int index = py * width + px;
|
|
||||||
Vec4& dest = rgbaBuffer[index];
|
|
||||||
|
|
||||||
float invSrcAlpha = 1.0f - srcAlpha;
|
|
||||||
dest.r = color.r * srcAlpha + dest.r * invSrcAlpha;
|
|
||||||
dest.g = color.g * srcAlpha + dest.g * invSrcAlpha;
|
|
||||||
dest.b = color.b * srcAlpha + dest.b * invSrcAlpha;
|
|
||||||
dest.a = srcAlpha + dest.a * invSrcAlpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert RGBA buffer to BGR output
|
|
||||||
rgbData.resize(rgbaBuffer.size() * 3);
|
|
||||||
for (size_t i = 0; i < rgbaBuffer.size(); ++i) {
|
|
||||||
const Vec4& color = rgbaBuffer[i];
|
|
||||||
size_t bgrIndex = i * 3;
|
|
||||||
|
|
||||||
// Convert from [0,1] to [0,255] and store as BGR
|
|
||||||
rgbData[bgrIndex + 2] = static_cast<uint8_t>(color.r * 255); // R -> third position
|
|
||||||
rgbData[bgrIndex + 1] = static_cast<uint8_t>(color.g * 255); // G -> second position
|
|
||||||
rgbData[bgrIndex + 0] = static_cast<uint8_t>(color.b * 255); // B -> first position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t removeSprite(size_t id) {
|
|
||||||
spritesComped.erase(id);
|
|
||||||
Layers.erase(id);
|
|
||||||
Orientations.erase(id);
|
|
||||||
return removeID(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove sprite by position
|
|
||||||
size_t removeSprite(const Vec2& pos) {
|
|
||||||
size_t id = getPositionVec(pos);
|
|
||||||
return removeSprite(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
Grid2::clear();
|
|
||||||
spritesComped.clear();
|
|
||||||
Layers.clear();
|
|
||||||
Orientations.clear();
|
|
||||||
spritesComped.rehash(0);
|
|
||||||
Layers.rehash(0);
|
|
||||||
Orientations.rehash(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all sprite IDs
|
|
||||||
std::vector<size_t> getAllSpriteIDs() {
|
|
||||||
return getAllIDs();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ID has a sprite
|
|
||||||
bool hasSprite(size_t id) const {
|
|
||||||
return spritesComped.find(id) != spritesComped.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get number of sprites
|
|
||||||
size_t getSpriteCount() const {
|
|
||||||
return spritesComped.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Helper function to extract pixel color from sprite data based on color format
|
|
||||||
Vec4 getSpritePixelColor(const std::vector<uint8_t>& spriteData,
|
|
||||||
int x, int y,
|
|
||||||
size_t spriteWidth, size_t spriteHeight,
|
|
||||||
frame::colormap format) const {
|
|
||||||
size_t pixelIndex = y * spriteWidth + x;
|
|
||||||
size_t channels = 3; // Default to RGB
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case frame::colormap::RGB:
|
|
||||||
channels = 3;
|
|
||||||
if (pixelIndex * channels + 2 < spriteData.size()) {
|
|
||||||
return Vec4(spriteData[pixelIndex * channels] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 1] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 2] / 255.0f,
|
|
||||||
1.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case frame::colormap::RGBA:
|
|
||||||
channels = 4;
|
|
||||||
if (pixelIndex * channels + 3 < spriteData.size()) {
|
|
||||||
return Vec4(spriteData[pixelIndex * channels] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 1] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 2] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 3] / 255.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case frame::colormap::BGR:
|
|
||||||
channels = 3;
|
|
||||||
if (pixelIndex * channels + 2 < spriteData.size()) {
|
|
||||||
return Vec4(spriteData[pixelIndex * channels + 2] / 255.0f, // BGR -> RGB
|
|
||||||
spriteData[pixelIndex * channels + 1] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels] / 255.0f,
|
|
||||||
1.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case frame::colormap::BGRA:
|
|
||||||
channels = 4;
|
|
||||||
if (pixelIndex * channels + 3 < spriteData.size()) {
|
|
||||||
return Vec4(spriteData[pixelIndex * channels + 2] / 255.0f, // BGRA -> RGBA
|
|
||||||
spriteData[pixelIndex * channels + 1] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels] / 255.0f,
|
|
||||||
spriteData[pixelIndex * channels + 3] / 255.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case frame::colormap::B:
|
|
||||||
channels = 1;
|
|
||||||
if (pixelIndex < spriteData.size()) {
|
|
||||||
float value = spriteData[pixelIndex] / 255.0f;
|
|
||||||
return Vec4(value, value, value, 1.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return transparent black if out of bounds
|
|
||||||
return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
#ifndef VOXELOCTREE_HPP
|
|
||||||
#define VOXELOCTREE_HPP
|
|
||||||
|
|
||||||
#include "../vectorlogic/vec3.hpp"
|
|
||||||
#include "../compression/zstd.hpp"
|
|
||||||
#include "../inttypes.hpp"
|
|
||||||
#include "../utils.hpp"
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cmath>
|
|
||||||
#include <bit>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
class VoxelData {
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t BitCount[] = {
|
|
||||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
|
||||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
|
||||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
|
||||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
|
||||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr float EPSILON = 0.0000000000000000000000001;
|
|
||||||
static const size_t CompressionBlockSize = 64*1024*1024;
|
|
||||||
|
|
||||||
class VoxelOctree {
|
|
||||||
private:
|
|
||||||
static const size_t MaxScale = 23;
|
|
||||||
size_t _octSize;
|
|
||||||
std::vector<uint32_t> _octree;
|
|
||||||
VoxelData* _voxels;
|
|
||||||
Vec3f _center;
|
|
||||||
|
|
||||||
size_t buildOctree(ChunkedAllocator<uint32_t>& allocator, int x, int y, int z, int size, size_t descriptorIndex) {
|
|
||||||
_voxels->prepateDataAccess(x, y, z, size);
|
|
||||||
|
|
||||||
int halfSize = size >> 1;
|
|
||||||
const std::array<Vec3f, 8> childPositions = {
|
|
||||||
Vec3f{x + halfSize, y + halfSize, z + halfSize},
|
|
||||||
Vec3f{x, y + halfSize, z + halfSize},
|
|
||||||
Vec3f{x + halfSize, y, z + halfSize},
|
|
||||||
Vec3f{x, y, z + halfSize},
|
|
||||||
Vec3f{x + halfSize, y + halfSize, z},
|
|
||||||
Vec3f{x, y + halfSize, z},
|
|
||||||
Vec3f{x + halfSize, y, z},
|
|
||||||
Vec3f{x, y, z}
|
|
||||||
};
|
|
||||||
uint64_t childOffset = static_cast<uint64_t>(allocator.size()) - descriptorIndex;
|
|
||||||
|
|
||||||
int childCount = 0;
|
|
||||||
std::array<int, 8> childIndices{};
|
|
||||||
uint32_t childMask = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
if (_voxels->cubeContainsVoxelsDestructive(childPositions[i].x, childPositions[i].y, childPositions[i].z, halfSize)) {
|
|
||||||
childMask |= 128 >> i;
|
|
||||||
childIndices[childCount++] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasLargeChildren = false;
|
|
||||||
uint32_t leafMask;
|
|
||||||
if (halfSize == 1) {
|
|
||||||
leafMask = 0;
|
|
||||||
for (int i = 0; i < childCount; ++i) {
|
|
||||||
int idx = childIndices[childCount - i - 1];
|
|
||||||
allocator.pushBack(_voxels->getVoxelDestructive(childPositions[idx].x, childPositions[idx].y, childPositions[idx].z));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
leafMask = childMask;
|
|
||||||
for (int i = 0; i < childCount; ++i) allocator.pushBack(0);
|
|
||||||
std::array<uint64_t, 8> granChildOffsets{};
|
|
||||||
uint64_t delta = 0;
|
|
||||||
uint64_t insertionCount = allocator.insertionCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < childCount; ++i) {
|
|
||||||
int idx = childIndices[childCount - i - 1];
|
|
||||||
granChildOffsets[i] = delta + buildOctree(allocator, childPositions[idx].x, childPositions[idx].y, childPositions[idx].z, halfSize, descriptorIndex + childOffset + i);
|
|
||||||
delta += allocator.insertionCount() - insertionCount;
|
|
||||||
insertionCount = allocator.insertionCount();
|
|
||||||
if (granChildOffsets[i] > 0x3FFF) hasLargeChildren = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < childCount; ++i) {
|
|
||||||
uint64_t childIdx = descriptorIndex + childOffset + i;
|
|
||||||
uint64_t offset = granChildOffsets[i];
|
|
||||||
|
|
||||||
if (hasLargeChildren) {
|
|
||||||
offset += childCount - i;
|
|
||||||
allocator.insert(childIdx + 1, static_cast<uint32_t>(offset));
|
|
||||||
allocator[childIdx] |= 0x20000;
|
|
||||||
offset >>= 32;
|
|
||||||
}
|
|
||||||
allocator[childIdx] |= static_cast<uint32_t>(offset << 18);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator[descriptorIndex] = (childMask << 8) | leafMask;
|
|
||||||
if (hasLargeChildren) allocator[descriptorIndex] |= 0x10000;
|
|
||||||
return childOffset;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
VoxelOctree(const std::string& path) : _voxels(nullptr) {
|
|
||||||
std::ifstream file = std::ifstream(path, std::ios::binary);
|
|
||||||
if (!file.isopen()) {
|
|
||||||
throw std::runtime_error(std::string("failed to open: ") + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
float cd[3];
|
|
||||||
file.read(reinterpret_cast<char*>(cd), sizeof(float) * 3);
|
|
||||||
_center = Vec3f(cd);
|
|
||||||
|
|
||||||
uint64_t octreeSize;
|
|
||||||
file.read(reinterpret_cast<char*>(&octreeSize), sizeof(uint64_t));
|
|
||||||
_octSize = octreeSize;
|
|
||||||
|
|
||||||
_octree.resize(_octSize);
|
|
||||||
|
|
||||||
std::vector<uint8_t> compressionBuffer(zstd(static_cast<int>(CompressionBlockSize)));
|
|
||||||
|
|
||||||
std::unique_ptr<ZSTD_Stream, decltype(&ZSTD_freeStreamDecode)> stream(ZSTD_freeStreamDecode);
|
|
||||||
ZSTD_setStreamDecode(stream.get(), reinterpret_cast<char*>(_octree.data()), 0);
|
|
||||||
|
|
||||||
uint64_t compressedSize = 0;
|
|
||||||
const size_t elementSize = sizeof(uint32_t);
|
|
||||||
for (uint64_t offset = 0; offset < _octSize * elementSize; offset += CompressionBlockSize) {
|
|
||||||
uint64_t compsize;
|
|
||||||
file.read(reinterpret_cast<char*>(&compsize), sizeof(uint64_t));
|
|
||||||
|
|
||||||
if (compsize > compressionBuffer.size()) compressionBuffer.resize(compsize);
|
|
||||||
file.read(compressionBuffer.data(), static_cast<std::streamsize>(compsize));
|
|
||||||
|
|
||||||
int outsize = std::min(_octSize * elementSize - offset, CompressionBlockSize);
|
|
||||||
ZSTD_Decompress_continue(stream.get(), compressionBuffer.data(), reinterpret_cast<char*>(_octree.data()) + offset, outsize);
|
|
||||||
|
|
||||||
compressedSize += compsize + sizeof(uint64_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VoxelOctree(VoxelData* voxels) : _voxels(voxels) {
|
|
||||||
std::unique_ptr<ChunkedAllocator<uint32_t>> octreeAllocator = std::make_unique<ChunkedAllocator<uint32_t>>();
|
|
||||||
|
|
||||||
octreeAllocator->pushBack(0);
|
|
||||||
buildOctree(*octreeAllocator, 0, 0, 0, _voxels->sideLength(), 0);
|
|
||||||
(*octreeAllocator)[0] |= 1 << 18;
|
|
||||||
|
|
||||||
_octSize = octreeAllocator->size() + octreeAllocator-> insertionCount();
|
|
||||||
_octree = octreeAllocator->finalize();
|
|
||||||
_center = _voxels->getCenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void save(const char* path) {
|
|
||||||
std::ofstream file(path, std::iod::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
throw std::runtime_error(std::string("failed to write: ") + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
float cd[3] = {_center.x,_center.y, _center.z};
|
|
||||||
|
|
||||||
file.write(reinterpret_cast<const char*>(cd), sizeof(float) * 3);
|
|
||||||
|
|
||||||
file.write(reinterpret_cast<const char*>(static_cast<uint64_t>(_octSize)), sizeof(uint64_t));
|
|
||||||
std::vector<uint8_t> compressionBuffer(ZSTD_compressBound(static_cast<int>(CompressionBlockSize)));
|
|
||||||
std::unique_ptr<ZSTD_stream_t, decltype(&ZSTD_freeStream)> stream(ZSTD_createStream(), ZSTD_freeStream);
|
|
||||||
|
|
||||||
ZSTD_resetStream(stream.get());
|
|
||||||
|
|
||||||
uint64_t compressedSize = 0;
|
|
||||||
const size_t elementSize = sizeof(uint32_t);
|
|
||||||
const char* src = reinterpret_cast<const char*>(_octree.data());
|
|
||||||
|
|
||||||
for (uint64_t offset = 0; offset < _octSize * elementSize; offset += CompressionBlockSize) {
|
|
||||||
int outSize = _octSize * elementSize - offset, CompressionBlockSize;
|
|
||||||
uint64_t compSize = ZSTD_Compress_continue(stream.get(), src+offset, compressionBuffer.data(), outSize);
|
|
||||||
|
|
||||||
file.write(reinterpret_cast<const char*>(&compSize), sizeof(uint64_t));
|
|
||||||
file.write(compressionBuffer.data(), static_cast<std::streamsize>(compSize));
|
|
||||||
|
|
||||||
compressedSize += compSize + sizeof(uint64_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rayMarch(const Vec3f& origin, const Vec3f& dest, float rayScale, uint32_t& normal, float& t) {
|
|
||||||
struct StackEntry {
|
|
||||||
uint64_t offset;
|
|
||||||
float maxT;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<StackEntry, MaxScale + 1> rayStack;
|
|
||||||
|
|
||||||
Vec3 invAbsD = -dest.abs().safeInverse();
|
|
||||||
uint8_t octantMask = dest.calculateOctantMask();
|
|
||||||
Vec3f bT = invAbsD * origin;
|
|
||||||
if (dest.x > 0) { bT.x = 3.0f * invAbsD.x - bT.x;}
|
|
||||||
if (dest.y > 0) { bT.y = 3.0f * invAbsD.y - bT.y;}
|
|
||||||
if (dest.z > 0) { bT.z = 3.0f * invAbsD.z - bT.z;}
|
|
||||||
|
|
||||||
float minT = (2.0f * invAbsD - bT).maxComp();
|
|
||||||
float maxT = (invAbsD - bT).minComp();
|
|
||||||
minT = std::max(minT, 0.0f);
|
|
||||||
uint32_t curr = 0;
|
|
||||||
uint64_t par = 0;
|
|
||||||
|
|
||||||
Vec3 pos(1.0f);
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
Vec3 centerT = 1.5f * invAbsD - bT;
|
|
||||||
if (centerT.x > minT) { idx ^= 1; pos.x = 1.5f; }
|
|
||||||
if (centerT.y > minT) { idx ^= 2; pos.y = 1.5f; }
|
|
||||||
if (centerT.z > minT) { idx ^= 4; pos.z = 1.5f; }
|
|
||||||
|
|
||||||
int scale = MaxScale - 1;
|
|
||||||
float scaleExp2 = 0.5f;
|
|
||||||
|
|
||||||
while (scale < MaxScale) {
|
|
||||||
if (curr == 0) curr = _octree[par];
|
|
||||||
|
|
||||||
Vec3 cornerT = pos * invAbsD - bT;
|
|
||||||
float maxTC = cornerT.minComp();
|
|
||||||
|
|
||||||
int childShift = idx ^ octantMask;
|
|
||||||
uint32_t childMasks = curr << childShift;
|
|
||||||
if ((childMasks & 0x8000) && minT <= maxT) {
|
|
||||||
if (maxTC * rayScale >= scaleExp2) {
|
|
||||||
t = maxTC;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float maxTV = std::min(maxTC, maxT);
|
|
||||||
float half = scaleExp2 * 0.5f;
|
|
||||||
Vec3f centerT = Vec3(half) * invAbsD + cornerT;
|
|
||||||
|
|
||||||
if (minT <= maxTV) {
|
|
||||||
uint64_t childOffset = curr >> 18;
|
|
||||||
if (curr & 0x20000) childOffset = (childOffset << 32) | static_cast<uint64_t>(_octree[par+1]);
|
|
||||||
if (!(childMasks & 0x80)) {
|
|
||||||
uint32_t maskIndex = ((childMasks >> (8 + childShift)) << childShift) & 127;
|
|
||||||
normal = _octree[childOffset + par + BitCount[maskIndex]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rayStack[scale].offset = par;
|
|
||||||
rayStack[scale].maxT = maxT;
|
|
||||||
uint32_t siblingCount = BitCount[childMasks & 127];
|
|
||||||
|
|
||||||
par += childOffset + siblingCount;
|
|
||||||
if (curr & 0x10000) par += siblingCount;
|
|
||||||
idx = 0;
|
|
||||||
|
|
||||||
--scale;
|
|
||||||
scaleExp2 = half;
|
|
||||||
if (centerT.x > minT) {
|
|
||||||
idx ^= 1;
|
|
||||||
pos.x += scaleExp2;
|
|
||||||
}
|
|
||||||
if (centerT.y > minT) {
|
|
||||||
idx ^= 1;
|
|
||||||
pos.y += scaleExp2;
|
|
||||||
}
|
|
||||||
if (centerT.z > minT) {
|
|
||||||
idx ^= 1;
|
|
||||||
pos.z += scaleExp2;
|
|
||||||
}
|
|
||||||
|
|
||||||
maxT = maxTV;
|
|
||||||
curr = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int stepMask = 0;
|
|
||||||
if (cornerT.x <= maxTC) {
|
|
||||||
stepMask ^= 1;
|
|
||||||
pos.x -= scaleExp2;
|
|
||||||
}
|
|
||||||
if (cornerT.y <= maxTC) {
|
|
||||||
stepMask ^= 1;
|
|
||||||
pos.y -= scaleExp2;
|
|
||||||
}
|
|
||||||
if (cornerT.z <= maxTC) {
|
|
||||||
stepMask ^= 1;
|
|
||||||
pos.z -= scaleExp2;
|
|
||||||
}
|
|
||||||
|
|
||||||
minT = maxTC;
|
|
||||||
idx ^= stepMask;
|
|
||||||
|
|
||||||
if ((idx & stepMask) != 0) {
|
|
||||||
uint32_t differingBits = 0;
|
|
||||||
if (stepMask & 1) {
|
|
||||||
differingBits |= std::bit_cast<uint32_t>(pos.x) ^ std::bit_cast<uint32_t>(pos.x + scaleExp2);
|
|
||||||
}
|
|
||||||
if (stepMask & 2) {
|
|
||||||
differingBits |= std::bit_cast<uint32_t>(pos.y) ^ std::bit_cast<uint32_t>(pos.y + scaleExp2);
|
|
||||||
}
|
|
||||||
if (stepMask & 4) {
|
|
||||||
differingBits |= std::bit_cast<uint32_t>(pos.z) ^ std::bit_cast<uint32_t>(pos.z + scaleExp2);
|
|
||||||
}
|
|
||||||
|
|
||||||
scale = (differingBits >> 23) - 127;
|
|
||||||
scale = std::bit_cast<float>(static_cast<uint32_t>((scale - MaxScale + 127) << 23));
|
|
||||||
|
|
||||||
par = rayStack[scale].offset;
|
|
||||||
maxT = rayStack[scale].maxT;
|
|
||||||
|
|
||||||
int shX = std::bit_cast<uint32_t>(pos.x) >> scale;
|
|
||||||
int shY = std::bit_cast<uint32_t>(pos.y) >> scale;
|
|
||||||
int shZ = std::bit_cast<uint32_t>(pos.z) >> scale;
|
|
||||||
|
|
||||||
pos.x = std::bit_cast<float>(shX << scale);
|
|
||||||
pos.y = std::bit_cast<float>(shY << scale);
|
|
||||||
pos.z = std::bit_cast<float>(shZ << scale);
|
|
||||||
|
|
||||||
idx = (shX & 1) | ((shY & 1) << 1) | ((shZ & 1) << 2);
|
|
||||||
curr = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (scale >=MaxScale) return false;
|
|
||||||
t = minT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3f center() const {
|
|
||||||
return _center;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
93
util/jsonhelper.hpp
Normal file
93
util/jsonhelper.hpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#ifndef JSONHELPER_HPP
|
||||||
|
#define JSONHELPER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace JsonHelper {
|
||||||
|
|
||||||
|
// Helper to get string value between quotes
|
||||||
|
inline std::string parseString(const std::string& json, const std::string& key) {
|
||||||
|
size_t pos = json.find("\"" + key + "\"");
|
||||||
|
if (pos == std::string::npos) return "";
|
||||||
|
pos = json.find(":", pos);
|
||||||
|
if (pos == std::string::npos) return "";
|
||||||
|
size_t start = json.find("\"", pos);
|
||||||
|
if (start == std::string::npos) return "";
|
||||||
|
size_t end = json.find("\"", start + 1);
|
||||||
|
if (end == std::string::npos) return "";
|
||||||
|
return json.substr(start + 1, end - start - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to get raw non-string value (int, float, bool)
|
||||||
|
inline std::string parseRaw(const std::string& json, const std::string& key) {
|
||||||
|
size_t pos = json.find("\"" + key + "\"");
|
||||||
|
if (pos == std::string::npos) return "";
|
||||||
|
pos = json.find(":", pos);
|
||||||
|
if (pos == std::string::npos) return "";
|
||||||
|
pos++; // skip ':'
|
||||||
|
|
||||||
|
while (pos < json.length() && std::isspace(json[pos])) pos++;
|
||||||
|
size_t end = pos;
|
||||||
|
while (end < json.length() && json[end] != ',' && json[end] != '}' && json[end] != ']' && !std::isspace(json[end])) end++;
|
||||||
|
|
||||||
|
return json.substr(pos, end - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int parseInt(const std::string& json, const std::string& key, int defaultVal = 0) {
|
||||||
|
std::string raw = parseRaw(json, key);
|
||||||
|
if (raw.empty()) return defaultVal;
|
||||||
|
try { return std::stoi(raw); } catch(...) { return defaultVal; }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float parseFloat(const std::string& json, const std::string& key, float defaultVal = 0.0f) {
|
||||||
|
std::string raw = parseRaw(json, key);
|
||||||
|
if (raw.empty()) return defaultVal;
|
||||||
|
try { return std::stof(raw); } catch(...) { return defaultVal; }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool parseBool(const std::string& json, const std::string& key, bool defaultVal = false) {
|
||||||
|
std::string raw = parseRaw(json, key);
|
||||||
|
if (raw.empty()) return defaultVal;
|
||||||
|
return raw == "true" || raw == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to extract JSON objects out of a JSON array
|
||||||
|
inline std::vector<std::string> parseArray(const std::string& json, const std::string& key) {
|
||||||
|
std::vector<std::string> items;
|
||||||
|
size_t pos = json.find("\"" + key + "\"");
|
||||||
|
if (pos == std::string::npos) return items;
|
||||||
|
pos = json.find(":", pos);
|
||||||
|
if (pos == std::string::npos) return items;
|
||||||
|
pos = json.find("[", pos);
|
||||||
|
if (pos == std::string::npos) return items;
|
||||||
|
|
||||||
|
int depth = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
bool inString = false;
|
||||||
|
|
||||||
|
for (size_t i = pos + 1; i < json.length(); ++i) {
|
||||||
|
if (json[i] == '"' && (i == 0 || json[i-1] != '\\')) {
|
||||||
|
inString = !inString;
|
||||||
|
}
|
||||||
|
if (!inString) {
|
||||||
|
if (json[i] == '{') {
|
||||||
|
if (depth == 0) start = i;
|
||||||
|
depth++;
|
||||||
|
} else if (json[i] == '}') {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0) {
|
||||||
|
items.push_back(json.substr(start, i - start + 1));
|
||||||
|
}
|
||||||
|
} else if (json[i] == ']') {
|
||||||
|
if (depth == 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
166
util/mat2.hpp
166
util/mat2.hpp
@@ -1,166 +0,0 @@
|
|||||||
#ifndef MAT2_HPP
|
|
||||||
#define MAT2_HPP
|
|
||||||
|
|
||||||
#include "Vec2.hpp"
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
class Mat2 {
|
|
||||||
public:
|
|
||||||
union {
|
|
||||||
struct { float m00, m01, m10, m11; };
|
|
||||||
struct { float a, b, c, d; };
|
|
||||||
float data[4];
|
|
||||||
float m[2][2];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructors
|
|
||||||
Mat2() : m00(1), m01(0), m10(0), m11(1) {}
|
|
||||||
Mat2(float scalar) : m00(scalar), m01(scalar), m10(scalar), m11(scalar) {}
|
|
||||||
Mat2(float m00, float m01, float m10, float m11) : m00(m00), m01(m01), m10(m10), m11(m11) {}
|
|
||||||
|
|
||||||
// Identity matrix
|
|
||||||
static Mat2 identity() { return Mat2(1, 0, 0, 1); }
|
|
||||||
|
|
||||||
// Zero matrix
|
|
||||||
static Mat2 zero() { return Mat2(0, 0, 0, 0); }
|
|
||||||
|
|
||||||
// Rotation matrix
|
|
||||||
static Mat2 rotation(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat2(cosA, -sinA, sinA, cosA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scaling matrix
|
|
||||||
static Mat2 scaling(const Vec2& scale) {
|
|
||||||
return Mat2(scale.x, 0, 0, scale.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic operations
|
|
||||||
Mat2 operator+(const Mat2& other) const {
|
|
||||||
return Mat2(m00 + other.m00, m01 + other.m01,
|
|
||||||
m10 + other.m10, m11 + other.m11);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 operator-(const Mat2& other) const {
|
|
||||||
return Mat2(m00 - other.m00, m01 - other.m01,
|
|
||||||
m10 - other.m10, m11 - other.m11);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 operator*(const Mat2& other) const {
|
|
||||||
return Mat2(
|
|
||||||
m00 * other.m00 + m01 * other.m10,
|
|
||||||
m00 * other.m01 + m01 * other.m11,
|
|
||||||
m10 * other.m00 + m11 * other.m10,
|
|
||||||
m10 * other.m01 + m11 * other.m11
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 operator*(float scalar) const {
|
|
||||||
return Mat2(m00 * scalar, m01 * scalar,
|
|
||||||
m10 * scalar, m11 * scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 operator/(float scalar) const {
|
|
||||||
return Mat2(m00 / scalar, m01 / scalar,
|
|
||||||
m10 / scalar, m11 / scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec2 operator*(const Vec2& vec) const {
|
|
||||||
return Vec2(
|
|
||||||
m00 * vec.x + m01 * vec.y,
|
|
||||||
m10 * vec.x + m11 * vec.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2& operator+=(const Mat2& other) {
|
|
||||||
m00 += other.m00; m01 += other.m01;
|
|
||||||
m10 += other.m10; m11 += other.m11;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2& operator-=(const Mat2& other) {
|
|
||||||
m00 -= other.m00; m01 -= other.m01;
|
|
||||||
m10 -= other.m10; m11 -= other.m11;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2& operator*=(const Mat2& other) {
|
|
||||||
*this = *this * other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2& operator*=(float scalar) {
|
|
||||||
m00 *= scalar; m01 *= scalar;
|
|
||||||
m10 *= scalar; m11 *= scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2& operator/=(float scalar) {
|
|
||||||
m00 /= scalar; m01 /= scalar;
|
|
||||||
m10 /= scalar; m11 /= scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Mat2& other) const {
|
|
||||||
return m00 == other.m00 && m01 == other.m01 &&
|
|
||||||
m10 == other.m10 && m11 == other.m11;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Mat2& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matrix operations
|
|
||||||
float determinant() const {
|
|
||||||
return m00 * m11 - m01 * m10;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 transposed() const {
|
|
||||||
return Mat2(m00, m10, m01, m11);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat2 inverse() const {
|
|
||||||
float det = determinant();
|
|
||||||
if (std::abs(det) < 1e-10f) {
|
|
||||||
return Mat2(); // Return identity if not invertible
|
|
||||||
}
|
|
||||||
float invDet = 1.0f / det;
|
|
||||||
return Mat2( m11 * invDet, -m01 * invDet,
|
|
||||||
-m10 * invDet, m00 * invDet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access operators
|
|
||||||
float& operator()(int row, int col) {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator()(int row, int col) const {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
float& operator[](int index) {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator[](int index) const {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string toString() const {
|
|
||||||
return "Mat2([" + std::to_string(m00) + ", " + std::to_string(m01) + "],\n" +
|
|
||||||
" [" + std::to_string(m10) + ", " + std::to_string(m11) + "])";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Mat2& mat) {
|
|
||||||
os << mat.toString();
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Mat2 operator*(float scalar, const Mat2& mat) {
|
|
||||||
return mat * scalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
232
util/mat3.hpp
232
util/mat3.hpp
@@ -1,232 +0,0 @@
|
|||||||
#ifndef MAT3_HPP
|
|
||||||
#define MAT3_HPP
|
|
||||||
|
|
||||||
#include "Vec3.hpp"
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
class Mat3 {
|
|
||||||
public:
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
float m00, m01, m02,
|
|
||||||
m10, m11, m12,
|
|
||||||
m20, m21, m22;
|
|
||||||
};
|
|
||||||
float data[9];
|
|
||||||
float m[3][3];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructors
|
|
||||||
Mat3() : m00(1), m01(0), m02(0),
|
|
||||||
m10(0), m11(1), m12(0),
|
|
||||||
m20(0), m21(0), m22(1) {}
|
|
||||||
|
|
||||||
Mat3(float scalar) : m00(scalar), m01(scalar), m02(scalar),
|
|
||||||
m10(scalar), m11(scalar), m12(scalar),
|
|
||||||
m20(scalar), m21(scalar), m22(scalar) {}
|
|
||||||
|
|
||||||
Mat3(float m00, float m01, float m02,
|
|
||||||
float m10, float m11, float m12,
|
|
||||||
float m20, float m21, float m22) :
|
|
||||||
m00(m00), m01(m01), m02(m02),
|
|
||||||
m10(m10), m11(m11), m12(m12),
|
|
||||||
m20(m20), m21(m21), m22(m22) {}
|
|
||||||
|
|
||||||
// Identity matrix
|
|
||||||
static Mat3 identity() {
|
|
||||||
return Mat3(1, 0, 0,
|
|
||||||
0, 1, 0,
|
|
||||||
0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero matrix
|
|
||||||
static Mat3 zero() { return Mat3(0); }
|
|
||||||
|
|
||||||
// Rotation matrices
|
|
||||||
static Mat3 rotationX(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat3(1, 0, 0,
|
|
||||||
0, cosA, -sinA,
|
|
||||||
0, sinA, cosA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mat3 rotationY(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat3(cosA, 0, sinA,
|
|
||||||
0, 1, 0,
|
|
||||||
-sinA, 0, cosA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mat3 rotationZ(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat3(cosA, -sinA, 0,
|
|
||||||
sinA, cosA, 0,
|
|
||||||
0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scaling matrix
|
|
||||||
static Mat3 scaling(const Vec3& scale) {
|
|
||||||
return Mat3(scale.x, 0, 0,
|
|
||||||
0, scale.y, 0,
|
|
||||||
0, 0, scale.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic operations
|
|
||||||
Mat3 operator+(const Mat3& other) const {
|
|
||||||
return Mat3(m00 + other.m00, m01 + other.m01, m02 + other.m02,
|
|
||||||
m10 + other.m10, m11 + other.m11, m12 + other.m12,
|
|
||||||
m20 + other.m20, m21 + other.m21, m22 + other.m22);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 operator-(const Mat3& other) const {
|
|
||||||
return Mat3(m00 - other.m00, m01 - other.m01, m02 - other.m02,
|
|
||||||
m10 - other.m10, m11 - other.m11, m12 - other.m12,
|
|
||||||
m20 - other.m20, m21 - other.m21, m22 - other.m22);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 operator*(const Mat3& other) const {
|
|
||||||
return Mat3(
|
|
||||||
m00 * other.m00 + m01 * other.m10 + m02 * other.m20,
|
|
||||||
m00 * other.m01 + m01 * other.m11 + m02 * other.m21,
|
|
||||||
m00 * other.m02 + m01 * other.m12 + m02 * other.m22,
|
|
||||||
|
|
||||||
m10 * other.m00 + m11 * other.m10 + m12 * other.m20,
|
|
||||||
m10 * other.m01 + m11 * other.m11 + m12 * other.m21,
|
|
||||||
m10 * other.m02 + m11 * other.m12 + m12 * other.m22,
|
|
||||||
|
|
||||||
m20 * other.m00 + m21 * other.m10 + m22 * other.m20,
|
|
||||||
m20 * other.m01 + m21 * other.m11 + m22 * other.m21,
|
|
||||||
m20 * other.m02 + m21 * other.m12 + m22 * other.m22
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 operator*(float scalar) const {
|
|
||||||
return Mat3(m00 * scalar, m01 * scalar, m02 * scalar,
|
|
||||||
m10 * scalar, m11 * scalar, m12 * scalar,
|
|
||||||
m20 * scalar, m21 * scalar, m22 * scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 operator/(float scalar) const {
|
|
||||||
return Mat3(m00 / scalar, m01 / scalar, m02 / scalar,
|
|
||||||
m10 / scalar, m11 / scalar, m12 / scalar,
|
|
||||||
m20 / scalar, m21 / scalar, m22 / scalar);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 operator*(const Vec3& vec) const {
|
|
||||||
return Vec3(
|
|
||||||
m00 * vec.x + m01 * vec.y + m02 * vec.z,
|
|
||||||
m10 * vec.x + m11 * vec.y + m12 * vec.z,
|
|
||||||
m20 * vec.x + m21 * vec.y + m22 * vec.z
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3& operator+=(const Mat3& other) {
|
|
||||||
*this = *this + other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3& operator-=(const Mat3& other) {
|
|
||||||
*this = *this - other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3& operator*=(const Mat3& other) {
|
|
||||||
*this = *this * other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3& operator*=(float scalar) {
|
|
||||||
*this = *this * scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3& operator/=(float scalar) {
|
|
||||||
*this = *this / scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Mat3& other) const {
|
|
||||||
for (int i = 0; i < 9; ++i) {
|
|
||||||
if (data[i] != other.data[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Mat3& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matrix operations
|
|
||||||
float determinant() const {
|
|
||||||
return m00 * (m11 * m22 - m12 * m21)
|
|
||||||
- m01 * (m10 * m22 - m12 * m20)
|
|
||||||
+ m02 * (m10 * m21 - m11 * m20);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 transposed() const {
|
|
||||||
return Mat3(m00, m10, m20,
|
|
||||||
m01, m11, m21,
|
|
||||||
m02, m12, m22);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 inverse() const {
|
|
||||||
float det = determinant();
|
|
||||||
if (std::abs(det) < 1e-10f) {
|
|
||||||
return Mat3(); // Return identity if not invertible
|
|
||||||
}
|
|
||||||
|
|
||||||
float invDet = 1.0f / det;
|
|
||||||
|
|
||||||
return Mat3(
|
|
||||||
(m11 * m22 - m12 * m21) * invDet,
|
|
||||||
(m02 * m21 - m01 * m22) * invDet,
|
|
||||||
(m01 * m12 - m02 * m11) * invDet,
|
|
||||||
|
|
||||||
(m12 * m20 - m10 * m22) * invDet,
|
|
||||||
(m00 * m22 - m02 * m20) * invDet,
|
|
||||||
(m02 * m10 - m00 * m12) * invDet,
|
|
||||||
|
|
||||||
(m10 * m21 - m11 * m20) * invDet,
|
|
||||||
(m01 * m20 - m00 * m21) * invDet,
|
|
||||||
(m00 * m11 - m01 * m10) * invDet
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access operators
|
|
||||||
float& operator()(int row, int col) {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator()(int row, int col) const {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
float& operator[](int index) {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator[](int index) const {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string toString() const {
|
|
||||||
return "Mat3([" + std::to_string(m00) + ", " + std::to_string(m01) + ", " + std::to_string(m02) + "],\n" +
|
|
||||||
" [" + std::to_string(m10) + ", " + std::to_string(m11) + ", " + std::to_string(m12) + "],\n" +
|
|
||||||
" [" + std::to_string(m20) + ", " + std::to_string(m21) + ", " + std::to_string(m22) + "])";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Mat3& mat) {
|
|
||||||
os << mat.toString();
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Mat3 operator*(float scalar, const Mat3& mat) {
|
|
||||||
return mat * scalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
310
util/mat4.hpp
310
util/mat4.hpp
@@ -1,310 +0,0 @@
|
|||||||
#ifndef MAT4_HPP
|
|
||||||
#define MAT4_HPP
|
|
||||||
|
|
||||||
#include "Vec3.hpp"
|
|
||||||
#include "Vec4.hpp"
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
class Mat4 {
|
|
||||||
public:
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
float m00, m01, m02, m03,
|
|
||||||
m10, m11, m12, m13,
|
|
||||||
m20, m21, m22, m23,
|
|
||||||
m30, m31, m32, m33;
|
|
||||||
};
|
|
||||||
float data[16];
|
|
||||||
float m[4][4];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructors
|
|
||||||
Mat4() : m00(1), m01(0), m02(0), m03(0),
|
|
||||||
m10(0), m11(1), m12(0), m13(0),
|
|
||||||
m20(0), m21(0), m22(1), m23(0),
|
|
||||||
m30(0), m31(0), m32(0), m33(1) {}
|
|
||||||
|
|
||||||
Mat4(float scalar) : m00(scalar), m01(scalar), m02(scalar), m03(scalar),
|
|
||||||
m10(scalar), m11(scalar), m12(scalar), m13(scalar),
|
|
||||||
m20(scalar), m21(scalar), m22(scalar), m23(scalar),
|
|
||||||
m30(scalar), m31(scalar), m32(scalar), m33(scalar) {}
|
|
||||||
|
|
||||||
Mat4(float m00, float m01, float m02, float m03,
|
|
||||||
float m10, float m11, float m12, float m13,
|
|
||||||
float m20, float m21, float m22, float m23,
|
|
||||||
float m30, float m31, float m32, float m33) :
|
|
||||||
m00(m00), m01(m01), m02(m02), m03(m03),
|
|
||||||
m10(m10), m11(m11), m12(m12), m13(m13),
|
|
||||||
m20(m20), m21(m21), m22(m22), m23(m23),
|
|
||||||
m30(m30), m31(m31), m32(m32), m33(m33) {}
|
|
||||||
|
|
||||||
// Identity matrix
|
|
||||||
static Mat4 identity() {
|
|
||||||
return Mat4(1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero matrix
|
|
||||||
static Mat4 zero() { return Mat4(0); }
|
|
||||||
|
|
||||||
// Translation matrix
|
|
||||||
static Mat4 translation(const Vec3& translation) {
|
|
||||||
return Mat4(1, 0, 0, translation.x,
|
|
||||||
0, 1, 0, translation.y,
|
|
||||||
0, 0, 1, translation.z,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotation matrices
|
|
||||||
static Mat4 rotationX(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat4(1, 0, 0, 0,
|
|
||||||
0, cosA, -sinA, 0,
|
|
||||||
0, sinA, cosA, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mat4 rotationY(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat4(cosA, 0, sinA, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
-sinA, 0, cosA, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mat4 rotationZ(float angle) {
|
|
||||||
float cosA = std::cos(angle);
|
|
||||||
float sinA = std::sin(angle);
|
|
||||||
return Mat4(cosA, -sinA, 0, 0,
|
|
||||||
sinA, cosA, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scaling matrix
|
|
||||||
static Mat4 scaling(const Vec3& scale) {
|
|
||||||
return Mat4(scale.x, 0, 0, 0,
|
|
||||||
0, scale.y, 0, 0,
|
|
||||||
0, 0, scale.z, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perspective projection matrix
|
|
||||||
static Mat4 perspective(float fov, float aspect, float near, float far) {
|
|
||||||
float tanHalfFov = std::tan(fov / 2.0f);
|
|
||||||
float range = near - far;
|
|
||||||
|
|
||||||
return Mat4(1.0f / (aspect * tanHalfFov), 0, 0, 0,
|
|
||||||
0, 1.0f / tanHalfFov, 0, 0,
|
|
||||||
0, 0, (-near - far) / range, 2.0f * far * near / range,
|
|
||||||
0, 0, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Orthographic projection matrix
|
|
||||||
static Mat4 orthographic(float left, float right, float bottom, float top, float near, float far) {
|
|
||||||
return Mat4(2.0f / (right - left), 0, 0, -(right + left) / (right - left),
|
|
||||||
0, 2.0f / (top - bottom), 0, -(top + bottom) / (top - bottom),
|
|
||||||
0, 0, -2.0f / (far - near), -(far + near) / (far - near),
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookAt matrix (view matrix)
|
|
||||||
static Mat4 lookAt(const Vec3& eye, const Vec3& target, const Vec3& up) {
|
|
||||||
Vec3 z = (eye - target).normalized();
|
|
||||||
Vec3 x = up.cross(z).normalized();
|
|
||||||
Vec3 y = z.cross(x);
|
|
||||||
|
|
||||||
return Mat4(x.x, x.y, x.z, -x.dot(eye),
|
|
||||||
y.x, y.y, y.z, -y.dot(eye),
|
|
||||||
z.x, z.y, z.z, -z.dot(eye),
|
|
||||||
0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic operations
|
|
||||||
Mat4 operator+(const Mat4& other) const {
|
|
||||||
Mat4 result;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
result.data[i] = data[i] + other.data[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 operator-(const Mat4& other) const {
|
|
||||||
Mat4 result;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
result.data[i] = data[i] - other.data[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 operator*(const Mat4& other) const {
|
|
||||||
Mat4 result;
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
for (int j = 0; j < 4; ++j) {
|
|
||||||
result.m[i][j] = 0;
|
|
||||||
for (int k = 0; k < 4; ++k) {
|
|
||||||
result.m[i][j] += m[i][k] * other.m[k][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 operator*(float scalar) const {
|
|
||||||
Mat4 result;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
result.data[i] = data[i] * scalar;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 operator/(float scalar) const {
|
|
||||||
Mat4 result;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
result.data[i] = data[i] / scalar;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec4 operator*(const Vec4& vec) const {
|
|
||||||
return Vec4(
|
|
||||||
m00 * vec.x + m01 * vec.y + m02 * vec.z + m03 * vec.w,
|
|
||||||
m10 * vec.x + m11 * vec.y + m12 * vec.z + m13 * vec.w,
|
|
||||||
m20 * vec.x + m21 * vec.y + m22 * vec.z + m23 * vec.w,
|
|
||||||
m30 * vec.x + m31 * vec.y + m32 * vec.z + m33 * vec.w
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 transformPoint(const Vec3& point) const {
|
|
||||||
Vec4 result = *this * Vec4(point, 1.0f);
|
|
||||||
return result.xyz() / result.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 transformDirection(const Vec3& direction) const {
|
|
||||||
Vec4 result = *this * Vec4(direction, 0.0f);
|
|
||||||
return result.xyz();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4& operator+=(const Mat4& other) {
|
|
||||||
*this = *this + other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4& operator-=(const Mat4& other) {
|
|
||||||
*this = *this - other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4& operator*=(const Mat4& other) {
|
|
||||||
*this = *this * other;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4& operator*=(float scalar) {
|
|
||||||
*this = *this * scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4& operator/=(float scalar) {
|
|
||||||
*this = *this / scalar;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Mat4& other) const {
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
if (data[i] != other.data[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Mat4& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matrix operations
|
|
||||||
float determinant() const {
|
|
||||||
// Using Laplace expansion for 4x4 determinant
|
|
||||||
float det = 0;
|
|
||||||
det += m00 * (m11 * (m22 * m33 - m23 * m32) - m12 * (m21 * m33 - m23 * m31) + m13 * (m21 * m32 - m22 * m31));
|
|
||||||
det -= m01 * (m10 * (m22 * m33 - m23 * m32) - m12 * (m20 * m33 - m23 * m30) + m13 * (m20 * m32 - m22 * m30));
|
|
||||||
det += m02 * (m10 * (m21 * m33 - m23 * m31) - m11 * (m20 * m33 - m23 * m30) + m13 * (m20 * m31 - m21 * m30));
|
|
||||||
det -= m03 * (m10 * (m21 * m32 - m22 * m31) - m11 * (m20 * m32 - m22 * m30) + m12 * (m20 * m31 - m21 * m30));
|
|
||||||
return det;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 transposed() const {
|
|
||||||
return Mat4(m00, m10, m20, m30,
|
|
||||||
m01, m11, m21, m31,
|
|
||||||
m02, m12, m22, m32,
|
|
||||||
m03, m13, m23, m33);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 inverse() const {
|
|
||||||
// This is a simplified inverse implementation
|
|
||||||
// For production use, consider a more robust implementation
|
|
||||||
float det = determinant();
|
|
||||||
if (std::abs(det) < 1e-10f) {
|
|
||||||
return Mat4(); // Return identity if not invertible
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat4 result;
|
|
||||||
// Calculate inverse using adjugate matrix divided by determinant
|
|
||||||
// This is a placeholder - full implementation would be quite lengthy
|
|
||||||
float invDet = 1.0f / det;
|
|
||||||
|
|
||||||
// Note: This is a simplified version - full implementation would calculate all 16 cofactors
|
|
||||||
result.m00 = (m11 * (m22 * m33 - m23 * m32) - m12 * (m21 * m33 - m23 * m31) + m13 * (m21 * m32 - m22 * m31)) * invDet;
|
|
||||||
// ... continue for all 16 elements
|
|
||||||
|
|
||||||
return result.transposed() * invDet;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access operators
|
|
||||||
float& operator()(int row, int col) {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator()(int row, int col) const {
|
|
||||||
return m[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
float& operator[](int index) {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const float& operator[](int index) const {
|
|
||||||
return data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string toString() const {
|
|
||||||
return "Mat4([" + std::to_string(m00) + ", " + std::to_string(m01) + ", " + std::to_string(m02) + ", " + std::to_string(m03) + "],\n" +
|
|
||||||
" [" + std::to_string(m10) + ", " + std::to_string(m11) + ", " + std::to_string(m12) + ", " + std::to_string(m13) + "],\n" +
|
|
||||||
" [" + std::to_string(m20) + ", " + std::to_string(m21) + ", " + std::to_string(m22) + ", " + std::to_string(m23) + "],\n" +
|
|
||||||
" [" + std::to_string(m30) + ", " + std::to_string(m31) + ", " + std::to_string(m32) + ", " + std::to_string(m33) + "])";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Mat4& mat) {
|
|
||||||
os << mat.toString();
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Mat4 operator*(float scalar, const Mat4& mat) {
|
|
||||||
return mat * scalar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now you can implement the Ray3 transform method
|
|
||||||
#include "ray3.hpp"
|
|
||||||
|
|
||||||
inline Ray3 Ray3::transform(const Mat4& matrix) const {
|
|
||||||
Vec3 transformedOrigin = matrix.transformPoint(origin);
|
|
||||||
Vec3 transformedDirection = matrix.transformDirection(direction);
|
|
||||||
return Ray3(transformedOrigin, transformedDirection.normalized());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
83
util/materials/colors.hpp
Normal file
83
util/materials/colors.hpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#ifndef COLORS
|
||||||
|
#define COLORS
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace ColorUtils {
|
||||||
|
inline float cieX(float wavelength) {
|
||||||
|
float t1 = (wavelength - 442.0f) * ((wavelength < 442.0f) ? 0.0624f : 0.0374f);
|
||||||
|
float t2 = (wavelength - 599.8f) * ((wavelength < 599.8f) ? 0.0264f : 0.0323f);
|
||||||
|
float t3 = (wavelength - 501.1f) * ((wavelength < 501.1f) ? 0.0490f : 0.0382f);
|
||||||
|
|
||||||
|
return 0.362f * expf(-0.5f * t1 * t1) + 1.056f * expf(-0.5f * t2 * t2) - 0.065f * expf(-0.5f * t3 * t3);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float cieY(float wavelength) {
|
||||||
|
float t1 = (wavelength - 568.8f) * ((wavelength < 568.8f) ? 0.0213f : 0.0247f);
|
||||||
|
float t2 = (wavelength - 530.9f) * ((wavelength < 530.9f) ? 0.0613f : 0.0322f);
|
||||||
|
|
||||||
|
return 0.821f * expf(-0.5f * t1 * t1) + 0.286f * expf(-0.5f * t2 * t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float cieZ(float wavelength) {
|
||||||
|
float t1 = (wavelength - 437.0f) * ((wavelength < 437.0f) ? 0.0845f : 0.0278f);
|
||||||
|
float t2 = (wavelength - 459.0f) * ((wavelength < 459.0f) ? 0.0385f : 0.0725f);
|
||||||
|
|
||||||
|
return 1.217f * expf(-0.5f * t1 * t1) + 0.681f * expf(-0.5f * t2 * t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float gammaCorrect(float c) {
|
||||||
|
return (c <= 0.0031308f) ? (12.92f * c) : (1.055f * powf(c, 1.0f / 2.4f) - 0.055f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void wavelengthToRGB(float wavelength, float* rgbOut) {
|
||||||
|
float x = cieX(wavelength);
|
||||||
|
float y = cieY(wavelength);
|
||||||
|
float z = cieZ(wavelength);
|
||||||
|
|
||||||
|
float r = 3.2404542f * x - 1.5371385f * y - 0.4985314f * z;
|
||||||
|
float g = -0.9692660f * x + 1.8760108f * y + 0.0415560f * z;
|
||||||
|
float b = 0.0556434f * x - 0.2040259f * y + 1.0572252f * z;
|
||||||
|
|
||||||
|
rgbOut[0] = gammaCorrect(std::max(0.0f, std::min(1.0f, r)));
|
||||||
|
rgbOut[1] = gammaCorrect(std::max(0.0f, std::min(1.0f, g)));
|
||||||
|
rgbOut[2] = gammaCorrect(std::max(0.0f, std::min(1.0f, b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float rgbToHue(float r, float g, float b) {
|
||||||
|
float maxVal = std::max({r, g, b});
|
||||||
|
float minVal = std::min({r, g, b});
|
||||||
|
float delta = maxVal - minVal;
|
||||||
|
|
||||||
|
if (delta < 0.00001f) return 0.0f;
|
||||||
|
|
||||||
|
float hue = 0.0f;
|
||||||
|
if (maxVal == r) {
|
||||||
|
hue = (g - b) / delta + (g < b ? 6.0f : 0.0f);
|
||||||
|
} else if (maxVal == g) {
|
||||||
|
hue = (b - r) / delta + 2.0f;
|
||||||
|
} else {
|
||||||
|
hue = (r - g) / delta + 4.0f;
|
||||||
|
}
|
||||||
|
return hue * 60.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float hueToWavelength(float hue) {
|
||||||
|
float wl = 0.0f;
|
||||||
|
if (hue >= 260.0f) {
|
||||||
|
wl = 380.0f + (hue - 260.0f) * 0.5f;
|
||||||
|
} else if (hue < 120.0f) {
|
||||||
|
wl = 650.0f - (hue / 120.0f) * (650.0f - 510.0f);
|
||||||
|
} else if (hue < 240.0f) {
|
||||||
|
wl = 510.0f - ((hue - 120.0f) / 120.0f) * (510.0f - 440.0f);
|
||||||
|
} else {
|
||||||
|
wl = 440.0f - ((hue - 240.0f) / 120.0f) * (440.0f - 380.0f);
|
||||||
|
}
|
||||||
|
return wl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
164
util/materials/materials.hpp
Normal file
164
util/materials/materials.hpp
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
#ifndef G3_MATERIALS_HPP
|
||||||
|
#define G3_MATERIALS_HPP
|
||||||
|
|
||||||
|
#include "../../eigen/Eigen/Dense"
|
||||||
|
#include "colors.hpp"
|
||||||
|
|
||||||
|
struct Material {
|
||||||
|
float ior;
|
||||||
|
float dispersion;
|
||||||
|
float chromaticity;
|
||||||
|
float bandwidth;
|
||||||
|
float transmission;
|
||||||
|
float roughness;
|
||||||
|
float emittance;
|
||||||
|
bool light;
|
||||||
|
float density;
|
||||||
|
float speedOfSound;
|
||||||
|
float audioAbsorption;
|
||||||
|
Eigen::Vector3f rgb; //for the fast version.
|
||||||
|
|
||||||
|
// Constructor with sensible defaults
|
||||||
|
Material(float ior = 1.5f, float dispersion = 0.0f, float chromaticity = 550.0f,
|
||||||
|
float bandwidth = 100.0f, float transmission = 0.0f, float roughness = 0.8f,
|
||||||
|
float emittance = 0.0f, float density = 1000.0f, float speedOfSound = 343.0f,
|
||||||
|
float audioAbsorption = 0.1f, bool light = false)
|
||||||
|
: ior(ior), dispersion(dispersion), chromaticity(chromaticity), bandwidth(bandwidth),
|
||||||
|
transmission(transmission), roughness(roughness), emittance(emittance),
|
||||||
|
density(density), speedOfSound(speedOfSound), audioAbsorption(audioAbsorption), light(light) {}
|
||||||
|
|
||||||
|
|
||||||
|
static Material fromRGB(float r, float g, float b, float ior = 1.5f, float transmission = 0.0f,
|
||||||
|
float roughness = 0.8f, float emittance = 0.0f, bool isLight = false) {
|
||||||
|
float hue = ColorUtils::rgbToHue(r, g, b);
|
||||||
|
float wavelength = ColorUtils::hueToWavelength(hue);
|
||||||
|
|
||||||
|
float maxVal = std::max({r, g, b});
|
||||||
|
float minVal = std::min({r, g, b});
|
||||||
|
float delta = maxVal - minVal;
|
||||||
|
|
||||||
|
float saturation = (maxVal > 0.0f) ? (delta / maxVal) : 0.0f;
|
||||||
|
|
||||||
|
float bandwidth = 10.0f + (1.0f - saturation) * 290.0f;
|
||||||
|
|
||||||
|
Material outerial(ior, 0.02f, wavelength, bandwidth, transmission, roughness, emittance, 2.0f, 3000.0f, 0.1f, isLight);
|
||||||
|
outerial.rgb = Eigen::Vector3f(r, g, b);
|
||||||
|
return outerial;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Materials {
|
||||||
|
// Metals
|
||||||
|
static const Material Gold(0.47f, 0.25f, 580.0f, 150.0f, 0.95f, 0.2f, 0.0f, 19300.0f, 3240.0f, 0.01f, false);
|
||||||
|
static const Material Silver(0.18f, 0.35f, 450.0f, 200.0f, 0.98f, 0.15f, 0.0f, 10500.0f, 3650.0f, 0.008f, false);
|
||||||
|
static const Material Copper(0.64f, 0.22f, 620.0f, 100.0f, 0.90f, 0.3f, 0.0f, 8960.0f, 4760.0f, 0.012f, false);
|
||||||
|
static const Material Iron(2.91f, 0.15f, 500.0f, 180.0f, 0.65f, 0.4f, 0.0f, 7870.0f, 5130.0f, 0.015f, false);
|
||||||
|
static const Material Aluminum(1.44f, 0.20f, 500.0f, 200.0f, 0.92f, 0.25f, 0.0f, 2700.0f, 6320.0f, 0.009f, false);
|
||||||
|
static const Material Platinum(2.06f, 0.18f, 500.0f, 180.0f, 0.90f, 0.2f, 0.0f, 21450.0f, 3960.0f, 0.01f, false);
|
||||||
|
static const Material Bronze(1.15f, 0.12f, 590.0f, 120.0f, 0.85f, 0.35f, 0.0f, 8700.0f, 3580.0f, 0.02f, false);
|
||||||
|
static const Material Brass(0.85f, 0.15f, 570.0f, 130.0f, 0.88f, 0.3f, 0.0f, 8520.0f, 4700.0f, 0.018f, false);
|
||||||
|
|
||||||
|
// Glass
|
||||||
|
static const Material Glass(1.52f, 0.008f, 550.0f, 80.0f, 0.92f, 0.05f, 0.0f, 2500.0f, 5300.0f, 0.05f, false);
|
||||||
|
static const Material FlintGlass(1.62f, 0.028f, 550.0f, 100.0f, 0.90f, 0.05f, 0.0f, 3600.0f, 5100.0f, 0.06f, false);
|
||||||
|
static const Material Borosilicate(1.47f, 0.005f, 550.0f, 70.0f, 0.95f, 0.04f, 0.0f, 2230.0f, 5600.0f, 0.04f, false);
|
||||||
|
static const Material FusedSilica(1.46f, 0.004f, 550.0f, 60.0f, 0.98f, 0.02f, 0.0f, 2200.0f, 5968.0f, 0.03f, false);
|
||||||
|
static const Material LeadCrystal(1.57f, 0.025f, 550.0f, 90.0f, 0.93f, 0.03f, 0.0f, 3800.0f, 4200.0f, 0.07f, false);
|
||||||
|
static const Material Obsidian(1.48f, 0.010f, 500.0f, 150.0f, 0.30f, 0.4f, 0.0f, 2600.0f, 4800.0f, 0.15f, false);
|
||||||
|
static const Material StainedGlassRed(1.52f, 0.008f, 650.0f, 50.0f, 0.60f, 0.08f, 0.0f, 2550.0f, 5200.0f, 0.08f, false);
|
||||||
|
static const Material StainedGlassBlue(1.52f, 0.008f, 470.0f, 50.0f, 0.65f, 0.08f, 0.0f, 2550.0f, 5200.0f, 0.08f, false);
|
||||||
|
static const Material StainedGlassGreen(1.52f, 0.008f, 530.0f, 50.0f, 0.60f, 0.08f, 0.0f, 2550.0f, 5200.0f, 0.08f, false);
|
||||||
|
static const Material StainedGlassYellow(1.52f, 0.008f, 580.0f, 50.0f, 0.70f, 0.08f, 0.0f, 2550.0f, 5200.0f, 0.08f, false);
|
||||||
|
static const Material FrostedGlass(1.52f, 0.008f, 550.0f, 80.0f, 0.85f, 0.7f, 0.0f, 2500.0f, 5300.0f, 0.25f, false);
|
||||||
|
static const Material TemperedGlass(1.52f, 0.008f, 550.0f, 80.0f, 0.91f, 0.06f, 0.0f, 2500.0f, 5400.0f, 0.06f, false);
|
||||||
|
|
||||||
|
// Water
|
||||||
|
static const Material FreshWater(1.333f, 0.018f, 475.0f, 200.0f, 0.99f, 0.1f, 0.0f, 1000.0f, 1480.0f, 0.002f, false);
|
||||||
|
static const Material SaltWater(1.339f, 0.020f, 475.0f, 250.0f, 0.85f, 0.15f, 0.0f, 1025.0f, 1530.0f, 0.003f, false);
|
||||||
|
static const Material BrackishWater(1.336f, 0.019f, 480.0f, 220.0f, 0.70f, 0.2f, 0.0f, 1010.0f, 1500.0f, 0.005f, false);
|
||||||
|
static const Material DistilledWater(1.333f, 0.017f, 475.0f, 180.0f, 0.995f, 0.05f, 0.0f, 1000.0f, 1480.0f, 0.001f, false);
|
||||||
|
static const Material MurkyWater(1.340f, 0.022f, 550.0f, 300.0f, 0.30f, 0.4f, 0.0f, 1030.0f, 1450.0f, 0.05f, false);
|
||||||
|
|
||||||
|
// Gems
|
||||||
|
static const Material Diamond(2.42f, 0.044f, 550.0f, 50.0f, 0.99f, 0.01f, 0.0f, 3520.0f, 12000.0f, 0.005f, false);
|
||||||
|
static const Material Emerald(1.58f, 0.014f, 530.0f, 40.0f, 0.85f, 0.1f, 0.0f, 2750.0f, 8500.0f, 0.02f, false);
|
||||||
|
static const Material Ruby(1.77f, 0.018f, 690.0f, 30.0f, 0.80f, 0.15f, 0.0f, 4000.0f, 8800.0f, 0.015f, false);
|
||||||
|
static const Material Sapphire(1.77f, 0.018f, 460.0f, 40.0f, 0.85f, 0.12f, 0.0f, 3980.0f, 8700.0f, 0.015f, false);
|
||||||
|
static const Material Amethyst(1.54f, 0.013f, 420.0f, 60.0f, 0.75f, 0.2f, 0.0f, 2650.0f, 7200.0f, 0.025f, false);
|
||||||
|
static const Material Topaz(1.63f, 0.014f, 590.0f, 50.0f, 0.88f, 0.15f, 0.0f, 3560.0f, 8000.0f, 0.018f, false);
|
||||||
|
static const Material Opal(1.45f, 0.010f, 550.0f, 200.0f, 0.70f, 0.3f, 0.0f, 2100.0f, 4500.0f, 0.03f, false);
|
||||||
|
|
||||||
|
// Natural Materials
|
||||||
|
static const Material PlantLeaf(1.45f, 0.05f, 550.0f, 100.0f, 0.15f, 0.7f, 0.0f, 800.0f, 350.0f, 0.4f, false);
|
||||||
|
static const Material Wood(1.50f, 0.10f, 650.0f, 150.0f, 0.10f, 0.9f, 0.0f, 700.0f, 3300.0f, 0.35f, false);
|
||||||
|
static const Material Soil(1.55f, 0.08f, 600.0f, 200.0f, 0.05f, 0.95f, 0.0f, 1600.0f, 300.0f, 0.6f, false);
|
||||||
|
static const Material Snow(1.31f, 0.015f, 450.0f, 100.0f, 0.98f, 0.6f, 0.0f, 250.0f, 320.0f, 0.5f, false);
|
||||||
|
static const Material Ice(1.31f, 0.016f, 475.0f, 120.0f, 0.90f, 0.3f, 0.0f, 917.0f, 3980.0f, 0.08f, false);
|
||||||
|
static const Material Sand(1.54f, 0.06f, 580.0f, 180.0f, 0.20f, 0.85f, 0.0f, 1600.0f, 200.0f, 0.7f, false);
|
||||||
|
static const Material Rock(1.55f, 0.07f, 550.0f, 200.0f, 0.08f, 0.8f, 0.0f, 2750.0f, 5500.0f, 0.2f, false);
|
||||||
|
static const Material Marble(1.50f, 0.005f, 550.0f, 150.0f, 0.40f, 0.4f, 0.0f, 2710.0f, 6100.0f, 0.15f, false);
|
||||||
|
|
||||||
|
// Stars and Lights
|
||||||
|
static const Material Sun(1.0f, 0.0f, 580.0f, 300.0f, 1.0f, 0.0f, 1.0f, 1408.0f, 0.0f, 0.0f, true);
|
||||||
|
static const Material RedStar(1.0f, 0.0f, 700.0f, 200.0f, 1.0f, 0.0f, 0.8f, 0.1f, 0.0f, 0.0f, true);
|
||||||
|
static const Material YellowStar(1.0f, 0.0f, 580.0f, 250.0f, 1.0f, 0.0f, 0.9f, 0.2f, 0.0f, 0.0f, true);
|
||||||
|
static const Material BlueStar(1.0f, 0.0f, 450.0f, 150.0f, 1.0f, 0.0f, 1.2f, 0.3f, 0.0f, 0.0f, true);
|
||||||
|
static const Material BrownStar(1.0f, 0.0f, 1000.0f, 400.0f, 1.0f, 0.0f, 0.3f, 0.05f, 0.0f, 0.0f, true);
|
||||||
|
static const Material WhiteStar(1.0f, 0.0f, 550.0f, 200.0f, 1.0f, 0.0f, 1.1f, 0.4f, 0.0f, 0.0f, true);
|
||||||
|
static const Material Moon(1.0f, 0.0f, 580.0f, 150.0f, 1.0f, 0.0f, 0.015f, 3340.0f, 0.0f, 0.0f, true);
|
||||||
|
static const Material LEDWhite(1.0f, 0.0f, 550.0f, 100.0f, 1.0f, 0.0f, 0.7f, 2000.0f, 0.0f, 0.0f, true);
|
||||||
|
static const Material LEDWarm(1.0f, 0.0f, 600.0f, 80.0f, 1.0f, 0.0f, 0.6f, 2000.0f, 0.0f, 0.0f, true);
|
||||||
|
static const Material NeonRed(1.0f, 0.0f, 640.0f, 10.0f, 1.0f, 0.0f, 0.5f, 0.9f, 0.0f, 0.0f, true);
|
||||||
|
static const Material NeonBlue(1.0f, 0.0f, 480.0f, 15.0f, 1.0f, 0.0f, 0.5f, 0.9f, 0.0f, 0.0f, true);
|
||||||
|
|
||||||
|
// Flames
|
||||||
|
static const Material CandleFlame(1.0002f, 0.002f, 650.0f, 100.0f, 0.7f, 0.9f, 0.8f, 0.2f, 343.0f, 0.8f, true);
|
||||||
|
static const Material WoodFire(1.0003f, 0.003f, 620.0f, 150.0f, 0.6f, 0.95f, 1.2f, 0.3f, 350.0f, 0.7f, true);
|
||||||
|
static const Material GasFlame(1.0002f, 0.001f, 580.0f, 80.0f, 0.8f, 0.8f, 1.0f, 0.25f, 345.0f, 0.75f, true);
|
||||||
|
static const Material PropaneFlame(1.0002f, 0.0015f, 590.0f, 120.0f, 0.75f, 0.85f, 1.1f, 0.28f, 348.0f, 0.72f, true);
|
||||||
|
static const Material AlcoholFlame(1.0002f, 0.002f, 610.0f, 90.0f, 0.85f, 0.7f, 0.9f, 0.22f, 340.0f, 0.78f, true);
|
||||||
|
static const Material Plasma(1.0001f, 0.005f, 450.0f, 200.0f, 0.9f, 0.5f, 2.0f, 0.001f, 1000.0f, 0.3f, true);
|
||||||
|
|
||||||
|
// Organic materials
|
||||||
|
static const Material Skin(1.38f, 0.03f, 580.0f, 120.0f, 0.25f, 0.6f, 0.0f, 1100.0f, 1540.0f, 0.25f, false);
|
||||||
|
static const Material Hair(1.55f, 0.04f, 600.0f, 100.0f, 0.10f, 0.8f, 0.0f, 1300.0f, 100.0f, 0.6f, false);
|
||||||
|
static const Material Cotton(1.52f, 0.06f, 580.0f, 150.0f, 0.30f, 0.9f, 0.0f, 1520.0f, 200.0f, 0.55f, false);
|
||||||
|
static const Material Silk(1.54f, 0.05f, 570.0f, 130.0f, 0.40f, 0.7f, 0.0f, 1320.0f, 150.0f, 0.45f, false);
|
||||||
|
static const Material Leather(1.50f, 0.08f, 630.0f, 140.0f, 0.15f, 0.85f, 0.0f, 860.0f, 400.0f, 0.5f, false);
|
||||||
|
|
||||||
|
// Plastics and Polymers
|
||||||
|
static const Material Acrylic(1.49f, 0.008f, 550.0f, 90.0f, 0.92f, 0.1f, 0.0f, 1180.0f, 2670.0f, 0.12f, false);
|
||||||
|
static const Material Polycarbonate(1.58f, 0.010f, 550.0f, 95.0f, 0.88f, 0.15f, 0.0f, 1200.0f, 2270.0f, 0.15f, false);
|
||||||
|
static const Material PVC(1.54f, 0.012f, 550.0f, 120.0f, 0.70f, 0.3f, 0.0f, 1380.0f, 2380.0f, 0.18f, false);
|
||||||
|
static const Material Rubber(1.52f, 0.020f, 600.0f, 200.0f, 0.10f, 0.9f, 0.0f, 1100.0f, 1500.0f, 0.4f, false);
|
||||||
|
|
||||||
|
// Atmosphere Layers
|
||||||
|
static const Material Troposphere(1.00029f, 0.00015f, 475.0f, 350.0f, 0.95f, 0.3f, 0.0f, 1.225f, 343.0f, 0.01f, false);
|
||||||
|
static const Material Tropopause(1.00027f, 0.00012f, 470.0f, 320.0f, 0.97f, 0.2f, 0.0f, 0.66f, 295.0f, 0.008f, false);
|
||||||
|
static const Material Stratosphere(1.00025f, 0.00010f, 450.0f, 300.0f, 0.92f, 0.1f, 0.0f, 0.12f, 300.0f, 0.006f, false);
|
||||||
|
static const Material OzoneLayer(1.00026f, 0.00011f, 300.0f, 200.0f, 0.85f, 0.05f, 0.0f, 0.08f, 310.0f, 0.01f, false);
|
||||||
|
static const Material Stratopause(1.00022f, 0.00009f, 460.0f, 280.0f, 0.96f, 0.08f, 0.0f, 0.04f, 315.0f, 0.005f, false);
|
||||||
|
static const Material Mesosphere(1.00018f, 0.00008f, 490.0f, 250.0f, 0.88f, 0.15f, 0.0f, 0.008f, 280.0f, 0.004f, false);
|
||||||
|
static const Material Mesopause(1.00015f, 0.00007f, 500.0f, 230.0f, 0.94f, 0.12f, 0.0f, 0.005f, 275.0f, 0.003f, false);
|
||||||
|
static const Material Thermosphere(1.00010f, 0.00006f, 520.0f, 200.0f, 0.82f, 0.05f, 0.02f, 0.0002f, 500.0f, 0.001f, false);
|
||||||
|
static const Material Ionosphere(1.00008f, 0.00020f, 430.0f, 400.0f, 0.75f, 0.08f, 0.05f, 0.0001f, 1000.0f, 0.002f, false);
|
||||||
|
static const Material Exosphere(1.00001f, 0.00005f, 550.0f, 150.0f, 0.90f, 0.02f, 0.0f, 0.000001f, 0.0f, 0.0f, false);
|
||||||
|
static const Material KarmanLine(1.000005f, 0.00004f, 560.0f, 120.0f, 0.98f, 0.01f, 0.0f, 0.0000005f, 0.0f, 0.0f, false);
|
||||||
|
|
||||||
|
// Atmospheric Conditions
|
||||||
|
static const Material ClearSky(1.00028f, 0.00013f, 475.0f, 320.0f, 0.97f, 0.1f, 0.0f, 1.2f, 343.0f, 0.02f, false);
|
||||||
|
static const Material HazySky(1.00030f, 0.00018f, 550.0f, 400.0f, 0.85f, 0.5f, 0.0f, 1.3f, 343.0f, 0.1f, false);
|
||||||
|
static const Material FoggyAir(1.00032f, 0.00025f, 580.0f, 500.0f, 0.60f, 0.9f, 0.0f, 1.4f, 343.0f, 0.3f, false);
|
||||||
|
static const Material RainyAir(1.00031f, 0.00022f, 500.0f, 450.0f, 0.70f, 0.7f, 0.0f, 1.35f, 343.0f, 0.15f, false);
|
||||||
|
static const Material MartianAtmosphere(1.00015f, 0.00030f, 650.0f, 200.0f, 0.80f, 0.6f, 0.0f, 0.02f, 240.0f, 0.08f, false);
|
||||||
|
static const Material VenusianAtmosphere(1.00050f, 0.00040f, 580.0f, 600.0f, 0.30f, 0.8f, 0.1f, 67.0f, 400.0f, 0.4f, false);
|
||||||
|
static const Material TitanAtmosphere(1.00040f, 0.00035f, 620.0f, 400.0f, 0.40f, 0.7f, 0.0f, 5.3f, 200.0f, 0.25f, false);
|
||||||
|
static const Material AuroraBorealis(1.00012f, 0.00050f, 557.7f, 50.0f, 0.90f, 0.3f, 0.3f, 0.001f, 0.0f, 0.0f, true);
|
||||||
|
static const Material AuroraRed(1.00012f, 0.00050f, 630.0f, 40.0f, 0.90f, 0.3f, 0.2f, 0.001f, 0.0f, 0.0f, true);
|
||||||
|
static const Material AuroraBlue(1.00012f, 0.00050f, 428.0f, 60.0f, 0.90f, 0.3f, 0.25f, 0.001f, 0.0f, 0.0f, true);
|
||||||
|
static const Material SeaLevelAir(1.00029f, 0.00015f, 550.0f, 300.0f, 0.95f, 0.1f, 0.0f, 1.225f, 343.0f, 0.01f, false);
|
||||||
|
static const Material MountainAir(1.00025f, 0.00012f, 500.0f, 280.0f, 0.97f, 0.05f, 0.0f, 0.96f, 330.0f, 0.008f, false);
|
||||||
|
static const Material HighAltitudeAir(1.00020f, 0.00010f, 480.0f, 250.0f, 0.98f, 0.03f, 0.0f, 0.74f, 320.0f, 0.006f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,403 +0,0 @@
|
|||||||
// noisegui.cpp
|
|
||||||
#include "pnoise.hpp"
|
|
||||||
#include "../bmpwriter.hpp"
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_impl_glfw.h>
|
|
||||||
#include <imgui_impl_opengl3.h>
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Convert noise value to grayscale color
|
|
||||||
Vec3 noiseToColor(double noiseValue) {
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
return Vec3(value, value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert noise value to color using a blue-to-red colormap
|
|
||||||
Vec3 noiseToHeatmap(double noiseValue) {
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
|
|
||||||
if (value < 0.25f) {
|
|
||||||
float t = value / 0.25f;
|
|
||||||
return Vec3(0.0f, t, 1.0f);
|
|
||||||
} else if (value < 0.5f) {
|
|
||||||
float t = (value - 0.25f) / 0.25f;
|
|
||||||
return Vec3(0.0f, 1.0f, 1.0f - t);
|
|
||||||
} else if (value < 0.75f) {
|
|
||||||
float t = (value - 0.5f) / 0.25f;
|
|
||||||
return Vec3(t, 1.0f, 0.0f);
|
|
||||||
} else {
|
|
||||||
float t = (value - 0.75f) / 0.25f;
|
|
||||||
return Vec3(1.0f, 1.0f - t, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert noise value to terrain-like colors
|
|
||||||
Vec3 noiseToTerrain(double noiseValue) {
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
|
|
||||||
if (value < 0.3f) {
|
|
||||||
return Vec3(0.0f, 0.0f, 0.3f + value * 0.4f);
|
|
||||||
} else if (value < 0.4f) {
|
|
||||||
return Vec3(0.76f, 0.70f, 0.50f);
|
|
||||||
} else if (value < 0.6f) {
|
|
||||||
float t = (value - 0.4f) / 0.2f;
|
|
||||||
return Vec3(0.0f, 0.4f + t * 0.3f, 0.0f);
|
|
||||||
} else if (value < 0.8f) {
|
|
||||||
return Vec3(0.0f, 0.3f, 0.0f);
|
|
||||||
} else {
|
|
||||||
float t = (value - 0.8f) / 0.2f;
|
|
||||||
return Vec3(0.8f + t * 0.2f, 0.8f + t * 0.2f, 0.8f + t * 0.2f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoiseTexture {
|
|
||||||
private:
|
|
||||||
GLuint textureID;
|
|
||||||
int width, height;
|
|
||||||
std::vector<unsigned char> pixelData;
|
|
||||||
|
|
||||||
public:
|
|
||||||
NoiseTexture(int w, int h) : width(w), height(h) {
|
|
||||||
pixelData.resize(width * height * 3);
|
|
||||||
glGenTextures(1, &textureID);
|
|
||||||
updateTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
~NoiseTexture() {
|
|
||||||
glDeleteTextures(1, &textureID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void generateNoise(const PerlinNoise& pn, double scale, int octaves,
|
|
||||||
const std::string& noiseType, const std::string& colorMap) {
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = 0.0;
|
|
||||||
|
|
||||||
if (noiseType == "Basic") {
|
|
||||||
noise = pn.noise(x * scale, y * scale);
|
|
||||||
} else if (noiseType == "FBM") {
|
|
||||||
noise = pn.fractal(octaves, x * scale, y * scale);
|
|
||||||
} else if (noiseType == "Turbulence") {
|
|
||||||
noise = pn.turbulence(octaves, x * scale, y * scale);
|
|
||||||
} else if (noiseType == "Ridged") {
|
|
||||||
noise = pn.ridgedMultiFractal(octaves, x * scale, y * scale, 0.0, 2.0, 0.5, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 color;
|
|
||||||
if (colorMap == "Grayscale") {
|
|
||||||
color = noiseToColor(noise);
|
|
||||||
} else if (colorMap == "Heatmap") {
|
|
||||||
color = noiseToHeatmap(noise);
|
|
||||||
} else if (colorMap == "Terrain") {
|
|
||||||
color = noiseToTerrain(noise);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = (y * width + x) * 3;
|
|
||||||
pixelData[index] = static_cast<unsigned char>(color.x * 255);
|
|
||||||
pixelData[index + 1] = static_cast<unsigned char>(color.y * 255);
|
|
||||||
pixelData[index + 2] = static_cast<unsigned char>(color.z * 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTexture() {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixelData.data());
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(const char* label, const ImVec2& size) {
|
|
||||||
ImGui::Image((void*)(intptr_t)textureID, size);
|
|
||||||
ImGui::Text("%s", label);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint getTextureID() const { return textureID; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class NoiseComparisonApp {
|
|
||||||
private:
|
|
||||||
GLFWwindow* window;
|
|
||||||
int windowWidth, windowHeight;
|
|
||||||
|
|
||||||
// Noise parameters
|
|
||||||
double scale;
|
|
||||||
int octaves;
|
|
||||||
unsigned int seed;
|
|
||||||
std::string noiseType;
|
|
||||||
std::string colorMap;
|
|
||||||
|
|
||||||
// Comparison views
|
|
||||||
struct ComparisonView {
|
|
||||||
std::unique_ptr<NoiseTexture> texture;
|
|
||||||
double scale;
|
|
||||||
int octaves;
|
|
||||||
unsigned int seed;
|
|
||||||
std::string noiseType;
|
|
||||||
std::string colorMap;
|
|
||||||
std::string label;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ComparisonView> views;
|
|
||||||
int textureSize;
|
|
||||||
|
|
||||||
// Preset management
|
|
||||||
struct Preset {
|
|
||||||
std::string name;
|
|
||||||
double scale;
|
|
||||||
int octaves;
|
|
||||||
std::string noiseType;
|
|
||||||
std::string colorMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Preset> presets;
|
|
||||||
int selectedPreset;
|
|
||||||
|
|
||||||
public:
|
|
||||||
NoiseComparisonApp() : windowWidth(1400), windowHeight(900), scale(0.01), octaves(4),
|
|
||||||
seed(42), noiseType("FBM"), colorMap("Grayscale"), textureSize(256) {
|
|
||||||
initializePresets();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initialize() {
|
|
||||||
if (!glfwInit()) return false;
|
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
||||||
|
|
||||||
window = glfwCreateWindow(windowWidth, windowHeight, "Perlin Noise Comparison Tool", NULL, NULL);
|
|
||||||
if (!window) {
|
|
||||||
glfwTerminate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(window);
|
|
||||||
glfwSwapInterval(1);
|
|
||||||
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
||||||
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
||||||
ImGui_ImplOpenGL3_Init("#version 130");
|
|
||||||
|
|
||||||
// Initialize with some default views
|
|
||||||
addComparisonView("FBM", "Grayscale", 0.01, 4, 42, "FBM Grayscale");
|
|
||||||
addComparisonView("FBM", "Heatmap", 0.01, 4, 42, "FBM Heatmap");
|
|
||||||
addComparisonView("FBM", "Terrain", 0.01, 4, 42, "FBM Terrain");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initializePresets() {
|
|
||||||
presets = {
|
|
||||||
{"Basic Grayscale", 0.01, 1, "Basic", "Grayscale"},
|
|
||||||
{"FBM Grayscale", 0.01, 4, "FBM", "Grayscale"},
|
|
||||||
{"FBM Terrain", 0.01, 4, "FBM", "Terrain"},
|
|
||||||
{"Turbulence", 0.01, 4, "Turbulence", "Grayscale"},
|
|
||||||
{"Ridged Multi", 0.01, 4, "Ridged", "Grayscale"},
|
|
||||||
{"Large Scale", 0.002, 4, "FBM", "Grayscale"},
|
|
||||||
{"Small Scale", 0.05, 4, "FBM", "Grayscale"},
|
|
||||||
{"High Octaves", 0.01, 8, "FBM", "Grayscale"}
|
|
||||||
};
|
|
||||||
selectedPreset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addComparisonView(const std::string& type, const std::string& cmap,
|
|
||||||
double sc, int oct, unsigned int sd, const std::string& lbl) {
|
|
||||||
ComparisonView view;
|
|
||||||
view.texture = std::make_unique<NoiseTexture>(textureSize, textureSize);
|
|
||||||
view.scale = sc;
|
|
||||||
view.octaves = oct;
|
|
||||||
view.seed = sd;
|
|
||||||
view.noiseType = type;
|
|
||||||
view.colorMap = cmap;
|
|
||||||
view.label = lbl;
|
|
||||||
|
|
||||||
PerlinNoise pn(view.seed);
|
|
||||||
view.texture->generateNoise(pn, view.scale, view.octaves, view.noiseType, view.colorMap);
|
|
||||||
|
|
||||||
views.push_back(std::move(view));
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
|
||||||
glfwPollEvents();
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
|
||||||
ImGui_ImplGlfw_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
renderUI();
|
|
||||||
|
|
||||||
ImGui::Render();
|
|
||||||
|
|
||||||
int display_w, display_h;
|
|
||||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
|
||||||
glViewport(0, 0, display_w, display_h);
|
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
||||||
|
|
||||||
glfwSwapBuffers(window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderUI() {
|
|
||||||
// Main control panel
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(300, windowHeight));
|
|
||||||
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);
|
|
||||||
|
|
||||||
ImGui::Text("Noise Parameters");
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::SliderDouble("Scale", &scale, 0.001, 0.1, "%.3f");
|
|
||||||
ImGui::SliderInt("Octaves", &octaves, 1, 8);
|
|
||||||
ImGui::InputInt("Seed", (int*)&seed);
|
|
||||||
|
|
||||||
const char* noiseTypes[] = {"Basic", "FBM", "Turbulence", "Ridged"};
|
|
||||||
ImGui::Combo("Noise Type", [](void* data, int idx, const char** out_text) {
|
|
||||||
*out_text = noiseTypes[idx];
|
|
||||||
return true;
|
|
||||||
}, nullptr, IM_ARRAYSIZE(noiseTypes));
|
|
||||||
noiseType = noiseTypes[ImGui::GetStateStorage()->GetInt(ImGui::GetID("Noise Type"), 0)];
|
|
||||||
|
|
||||||
const char* colorMaps[] = {"Grayscale", "Heatmap", "Terrain"};
|
|
||||||
ImGui::Combo("Color Map", [](void* data, int idx, const char** out_text) {
|
|
||||||
*out_text = colorMaps[idx];
|
|
||||||
return true;
|
|
||||||
}, nullptr, IM_ARRAYSIZE(colorMaps));
|
|
||||||
colorMap = colorMaps[ImGui::GetStateStorage()->GetInt(ImGui::GetID("Color Map"), 0)];
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Texture Size: %d", textureSize);
|
|
||||||
ImGui::SliderInt("##TexSize", &textureSize, 64, 512);
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
if (ImGui::Button("Generate Current")) {
|
|
||||||
std::string label = noiseType + " " + colorMap;
|
|
||||||
addComparisonView(noiseType, colorMap, scale, octaves, seed, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Clear All")) {
|
|
||||||
views.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Presets");
|
|
||||||
|
|
||||||
std::vector<const char*> presetNames;
|
|
||||||
for (const auto& preset : presets) {
|
|
||||||
presetNames.push_back(preset.name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Combo("##Presets", &selectedPreset, presetNames.data(), presetNames.size());
|
|
||||||
|
|
||||||
if (ImGui::Button("Add Preset")) {
|
|
||||||
if (selectedPreset >= 0 && selectedPreset < presets.size()) {
|
|
||||||
const auto& preset = presets[selectedPreset];
|
|
||||||
addComparisonView(preset.noiseType, preset.colorMap,
|
|
||||||
preset.scale, preset.octaves, seed, preset.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Add All Presets")) {
|
|
||||||
for (const auto& preset : presets) {
|
|
||||||
addComparisonView(preset.noiseType, preset.colorMap,
|
|
||||||
preset.scale, preset.octaves, seed, preset.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Quick Comparisons");
|
|
||||||
|
|
||||||
if (ImGui::Button("Scale Comparison")) {
|
|
||||||
std::vector<double> scales = {0.002, 0.005, 0.01, 0.02, 0.05};
|
|
||||||
for (double sc : scales) {
|
|
||||||
std::string label = "Scale " + std::to_string(sc);
|
|
||||||
addComparisonView("FBM", "Grayscale", sc, 4, seed, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Octave Comparison")) {
|
|
||||||
for (int oct = 1; oct <= 6; ++oct) {
|
|
||||||
std::string label = oct + " Octaves";
|
|
||||||
addComparisonView("FBM", "Grayscale", 0.01, oct, seed, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
|
|
||||||
// Main view area
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(300, 0));
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(windowWidth - 300, windowHeight));
|
|
||||||
ImGui::Begin("Noise Comparison", nullptr,
|
|
||||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBringToFrontOnFocus);
|
|
||||||
|
|
||||||
ImVec2 imageSize(textureSize, textureSize);
|
|
||||||
int itemsPerRow = std::max(1, (int)((windowWidth - 320) / (textureSize + 20)));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < views.size(); ++i) {
|
|
||||||
auto& view = views[i];
|
|
||||||
|
|
||||||
if (i % itemsPerRow != 0) ImGui::SameLine();
|
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
view.texture->draw(view.label.c_str(), imageSize);
|
|
||||||
|
|
||||||
// Mini controls for each view
|
|
||||||
ImGui::PushID(static_cast<int>(i));
|
|
||||||
if (ImGui::SmallButton("Regenerate")) {
|
|
||||||
PerlinNoise pn(view.seed);
|
|
||||||
view.texture->generateNoise(pn, view.scale, view.octaves, view.noiseType, view.colorMap);
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::SmallButton("Remove")) {
|
|
||||||
views.erase(views.begin() + i);
|
|
||||||
ImGui::PopID();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui::PopID();
|
|
||||||
|
|
||||||
ImGui::EndGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
~NoiseComparisonApp() {
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
|
||||||
ImGui_ImplGlfw_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
|
|
||||||
glfwDestroyWindow(window);
|
|
||||||
glfwTerminate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
NoiseComparisonApp app;
|
|
||||||
|
|
||||||
if (!app.initialize()) {
|
|
||||||
std::cerr << "Failed to initialize application!" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.run();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,306 +0,0 @@
|
|||||||
// noisetest.cpp
|
|
||||||
#include "pnoise.hpp"
|
|
||||||
#include "../bmpwriter.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// Convert noise value to grayscale color
|
|
||||||
Vec3 noiseToColor(double noiseValue) {
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
return Vec3(value, value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert noise value to color using a blue-to-red colormap
|
|
||||||
Vec3 noiseToHeatmap(double noiseValue) {
|
|
||||||
// Blue (0.0) -> Cyan -> Green -> Yellow -> Red (1.0)
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
|
|
||||||
if (value < 0.25f) {
|
|
||||||
// Blue to Cyan
|
|
||||||
float t = value / 0.25f;
|
|
||||||
return Vec3(0.0f, t, 1.0f);
|
|
||||||
} else if (value < 0.5f) {
|
|
||||||
// Cyan to Green
|
|
||||||
float t = (value - 0.25f) / 0.25f;
|
|
||||||
return Vec3(0.0f, 1.0f, 1.0f - t);
|
|
||||||
} else if (value < 0.75f) {
|
|
||||||
// Green to Yellow
|
|
||||||
float t = (value - 0.5f) / 0.25f;
|
|
||||||
return Vec3(t, 1.0f, 0.0f);
|
|
||||||
} else {
|
|
||||||
// Yellow to Red
|
|
||||||
float t = (value - 0.75f) / 0.25f;
|
|
||||||
return Vec3(1.0f, 1.0f - t, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert noise value to terrain-like colors
|
|
||||||
Vec3 noiseToTerrain(double noiseValue) {
|
|
||||||
float value = static_cast<float>(noiseValue);
|
|
||||||
|
|
||||||
if (value < 0.3f) {
|
|
||||||
// Deep water to shallow water
|
|
||||||
return Vec3(0.0f, 0.0f, 0.3f + value * 0.4f);
|
|
||||||
} else if (value < 0.4f) {
|
|
||||||
// Sand
|
|
||||||
return Vec3(0.76f, 0.70f, 0.50f);
|
|
||||||
} else if (value < 0.6f) {
|
|
||||||
// Grass
|
|
||||||
float t = (value - 0.4f) / 0.2f;
|
|
||||||
return Vec3(0.0f, 0.4f + t * 0.3f, 0.0f);
|
|
||||||
} else if (value < 0.8f) {
|
|
||||||
// Forest
|
|
||||||
return Vec3(0.0f, 0.3f, 0.0f);
|
|
||||||
} else {
|
|
||||||
// Mountain to snow
|
|
||||||
float t = (value - 0.8f) / 0.2f;
|
|
||||||
return Vec3(0.8f + t * 0.2f, 0.8f + t * 0.2f, 0.8f + t * 0.2f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate basic 2D noise map
|
|
||||||
void generateBasicNoise(const std::string& filename, int width, int height,
|
|
||||||
double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
std::cout << "Generating basic noise: " << filename << std::endl;
|
|
||||||
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.noise(x * scale, y * scale);
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate fractal Brownian motion noise
|
|
||||||
void generateFBMNoise(const std::string& filename, int width, int height,
|
|
||||||
size_t octaves, double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
std::cout << "Generating FBM noise (" << octaves << " octaves): " << filename << std::endl;
|
|
||||||
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.fractal(octaves, x * scale, y * scale);
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate turbulence noise
|
|
||||||
void generateTurbulenceNoise(const std::string& filename, int width, int height,
|
|
||||||
size_t octaves, double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
std::cout << "Generating turbulence noise (" << octaves << " octaves): " << filename << std::endl;
|
|
||||||
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.turbulence(octaves, x * scale, y * scale);
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate ridged multi-fractal noise
|
|
||||||
void generateRidgedNoise(const std::string& filename, int width, int height,
|
|
||||||
size_t octaves, double scale = 0.01, unsigned int seed = 42,
|
|
||||||
double lacunarity = 2.0, double gain = 0.5, double offset = 1.0) {
|
|
||||||
std::cout << "Generating ridged multi-fractal noise (" << octaves << " octaves): " << filename << std::endl;
|
|
||||||
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.ridgedMultiFractal(octaves, x * scale, y * scale,
|
|
||||||
0.0, lacunarity, gain, offset);
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate noise with different color mappings
|
|
||||||
void generateColoredNoise(const std::string& filename, int width, int height,
|
|
||||||
double scale = 0.01, unsigned int seed = 42,
|
|
||||||
const std::string& colorMap = "heatmap") {
|
|
||||||
std::cout << "Generating colored noise (" << colorMap << "): " << filename << std::endl;
|
|
||||||
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.noise(x * scale, y * scale);
|
|
||||||
|
|
||||||
if (colorMap == "heatmap") {
|
|
||||||
pixels[y][x] = noiseToHeatmap(noise);
|
|
||||||
} else if (colorMap == "terrain") {
|
|
||||||
pixels[y][x] = noiseToTerrain(noise);
|
|
||||||
} else {
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate multi-octave comparison
|
|
||||||
void generateOctaveComparison(const std::string& baseFilename, int width, int height,
|
|
||||||
double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
for (size_t octaves = 1; octaves <= 6; ++octaves) {
|
|
||||||
std::string filename = baseFilename + "_octaves_" + std::to_string(octaves) + ".bmp";
|
|
||||||
generateFBMNoise(filename, width, height, octaves, scale, seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate scale comparison
|
|
||||||
void generateScaleComparison(const std::string& baseFilename, int width, int height,
|
|
||||||
unsigned int seed = 42) {
|
|
||||||
std::vector<double> scales = {0.002, 0.005, 0.01, 0.02, 0.05, 0.1};
|
|
||||||
|
|
||||||
for (double scale : scales) {
|
|
||||||
std::string filename = baseFilename + "_scale_" + std::to_string(scale) + ".bmp";
|
|
||||||
generateBasicNoise(filename, width, height, scale, seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate seed comparison
|
|
||||||
void generateSeedComparison(const std::string& baseFilename, int width, int height,
|
|
||||||
double scale = 0.01) {
|
|
||||||
std::vector<unsigned int> seeds = {42, 123, 456, 789, 1000};
|
|
||||||
|
|
||||||
for (unsigned int seed : seeds) {
|
|
||||||
std::string filename = baseFilename + "_seed_" + std::to_string(seed) + ".bmp";
|
|
||||||
generateBasicNoise(filename, width, height, scale, seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate combined effects (FBM with different color maps)
|
|
||||||
void generateCombinedEffects(const std::string& baseFilename, int width, int height,
|
|
||||||
double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
|
|
||||||
// FBM with grayscale
|
|
||||||
std::vector<std::vector<Vec3>> pixels1(height, std::vector<Vec3>(width));
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.fractal(4, x * scale, y * scale);
|
|
||||||
pixels1[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BMPWriter::saveBMP(baseFilename + "_fbm_grayscale.bmp", pixels1);
|
|
||||||
|
|
||||||
// FBM with heatmap
|
|
||||||
std::vector<std::vector<Vec3>> pixels2(height, std::vector<Vec3>(width));
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.fractal(4, x * scale, y * scale);
|
|
||||||
pixels2[y][x] = noiseToHeatmap(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BMPWriter::saveBMP(baseFilename + "_fbm_heatmap.bmp", pixels2);
|
|
||||||
|
|
||||||
// FBM with terrain
|
|
||||||
std::vector<std::vector<Vec3>> pixels3(height, std::vector<Vec3>(width));
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.fractal(4, x * scale, y * scale);
|
|
||||||
pixels3[y][x] = noiseToTerrain(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BMPWriter::saveBMP(baseFilename + "_fbm_terrain.bmp", pixels3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate 3D slice noise (showing different Z slices)
|
|
||||||
void generate3DSlices(const std::string& baseFilename, int width, int height,
|
|
||||||
double scale = 0.01, unsigned int seed = 42) {
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
|
|
||||||
std::vector<double> zSlices = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < zSlices.size(); ++i) {
|
|
||||||
std::vector<std::vector<Vec3>> pixels(height, std::vector<Vec3>(width));
|
|
||||||
double z = zSlices[i] * 10.0; // Scale Z for meaningful variation
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
double noise = pn.noise(x * scale, y * scale, z);
|
|
||||||
pixels[y][x] = noiseToColor(noise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filename = baseFilename + "_zslice_" + std::to_string(i) + ".bmp";
|
|
||||||
BMPWriter::saveBMP(filename, pixels);
|
|
||||||
std::cout << "Generated 3D slice " << i << ": " << filename << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
const int WIDTH = 512;
|
|
||||||
const int HEIGHT = 512;
|
|
||||||
|
|
||||||
std::cout << "Generating Perlin noise variations..." << std::endl;
|
|
||||||
std::cout << "=====================================" << std::endl;
|
|
||||||
|
|
||||||
// 1. Basic noise variations
|
|
||||||
std::cout << "\n1. Basic Noise Variations:" << std::endl;
|
|
||||||
generateBasicNoise("output/basic_noise.bmp", WIDTH, HEIGHT, 0.01, 42);
|
|
||||||
|
|
||||||
// 2. Fractal Brownian Motion with different octaves
|
|
||||||
std::cout << "\n2. FBM Noise (Multiple Octaves):" << std::endl;
|
|
||||||
generateOctaveComparison("output/fbm", WIDTH, HEIGHT, 0.01, 42);
|
|
||||||
|
|
||||||
// 3. Turbulence noise
|
|
||||||
std::cout << "\n3. Turbulence Noise:" << std::endl;
|
|
||||||
generateTurbulenceNoise("output/turbulence_4oct.bmp", WIDTH, HEIGHT, 4, 0.01, 42);
|
|
||||||
generateTurbulenceNoise("output/turbulence_6oct.bmp", WIDTH, HEIGHT, 6, 0.01, 42);
|
|
||||||
|
|
||||||
// 4. Ridged multi-fractal noise
|
|
||||||
std::cout << "\n4. Ridged Multi-Fractal Noise:" << std::endl;
|
|
||||||
generateRidgedNoise("output/ridged_4oct.bmp", WIDTH, HEIGHT, 4, 0.01, 42, 2.0, 0.5, 1.0);
|
|
||||||
generateRidgedNoise("output/ridged_6oct.bmp", WIDTH, HEIGHT, 6, 0.01, 42, 2.0, 0.5, 1.0);
|
|
||||||
|
|
||||||
// 5. Different color mappings
|
|
||||||
std::cout << "\n5. Color Mappings:" << std::endl;
|
|
||||||
generateColoredNoise("output/heatmap_noise.bmp", WIDTH, HEIGHT, 0.01, 42, "heatmap");
|
|
||||||
generateColoredNoise("output/terrain_noise.bmp", WIDTH, HEIGHT, 0.01, 42, "terrain");
|
|
||||||
|
|
||||||
// 6. Scale variations
|
|
||||||
std::cout << "\n6. Scale Variations:" << std::endl;
|
|
||||||
generateScaleComparison("output/scale_test", WIDTH, HEIGHT, 42);
|
|
||||||
|
|
||||||
// 7. Seed variations
|
|
||||||
std::cout << "\n7. Seed Variations:" << std::endl;
|
|
||||||
generateSeedComparison("output/seed_test", WIDTH, HEIGHT, 0.01);
|
|
||||||
|
|
||||||
// 8. Combined effects
|
|
||||||
std::cout << "\n8. Combined Effects:" << std::endl;
|
|
||||||
generateCombinedEffects("output/combined", WIDTH, HEIGHT, 0.01, 42);
|
|
||||||
|
|
||||||
// 9. 3D slices
|
|
||||||
std::cout << "\n9. 3D Slices:" << std::endl;
|
|
||||||
generate3DSlices("output/3d_slice", WIDTH, HEIGHT, 0.01, 42);
|
|
||||||
|
|
||||||
std::cout << "\n=====================================" << std::endl;
|
|
||||||
std::cout << "All noise maps generated successfully!" << std::endl;
|
|
||||||
std::cout << "Check the 'output' directory for BMP files." << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
389
util/noise/pnoise.cpp
Normal file
389
util/noise/pnoise.cpp
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
#ifndef PNOISE_CPP
|
||||||
|
#define PNOISE_CPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "./pnoise2.hpp"
|
||||||
|
#include "../jsonhelper.hpp"
|
||||||
|
#include "../timing_decorator.hpp"
|
||||||
|
#include "../../imgui/imgui.h"
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
enum class NoiseType {
|
||||||
|
Perlin = 0,
|
||||||
|
Value = 1,
|
||||||
|
Fractal = 2,
|
||||||
|
Turbulence = 3,
|
||||||
|
Ridged = 4,
|
||||||
|
Billow = 5,
|
||||||
|
WhiteNoise = 6,
|
||||||
|
WorleyNoise = 7,
|
||||||
|
VoronoiNoise = 8,
|
||||||
|
CrystalNoise = 9,
|
||||||
|
DomainWarp = 10,
|
||||||
|
CurlNoise = 11
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BlendMode {
|
||||||
|
Add = 0,
|
||||||
|
Subtract = 1,
|
||||||
|
Multiply = 2,
|
||||||
|
Min = 3,
|
||||||
|
Max = 4,
|
||||||
|
Replace = 5,
|
||||||
|
DomainWarp = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoiseLayer {
|
||||||
|
bool enabled = true;
|
||||||
|
char name[32] = "Layer";
|
||||||
|
|
||||||
|
NoiseType type = NoiseType::Perlin;
|
||||||
|
BlendMode blend = BlendMode::Add;
|
||||||
|
|
||||||
|
int seedOffset = 0;
|
||||||
|
float scale = 0.02f;
|
||||||
|
float strength = 1.0f;
|
||||||
|
int octaves = 4;
|
||||||
|
float persistence = 0.5f;
|
||||||
|
float lacunarity = 2.0f;
|
||||||
|
float ridgeOffset = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoisePreviewState {
|
||||||
|
int width = 512;
|
||||||
|
int height = 512;
|
||||||
|
int masterSeed = 1337;
|
||||||
|
float offset[2] = {0.0f, 0.0f};
|
||||||
|
std::vector<NoiseLayer> layers;
|
||||||
|
GLuint textureId = 0;
|
||||||
|
std::vector<uint8_t> pixelBuffer;
|
||||||
|
bool needsUpdate = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float sampleNoiseLayer(PNoise2& gen, NoiseType type, Eigen::Vector2f point, const NoiseLayer& layer) {
|
||||||
|
float val = 0.0f;
|
||||||
|
switch (type) {
|
||||||
|
case NoiseType::Perlin:
|
||||||
|
val = gen.permute(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::Value:
|
||||||
|
val = gen.valueNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::Fractal:
|
||||||
|
val = gen.fractalNoise(point, layer.octaves, layer.persistence, layer.lacunarity);
|
||||||
|
break;
|
||||||
|
case NoiseType::Turbulence:
|
||||||
|
val = gen.turbulence(point, layer.octaves);
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Ridged:
|
||||||
|
val = gen.ridgedNoise(point, layer.octaves, layer.ridgeOffset);
|
||||||
|
val = (val * 0.5f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::Billow:
|
||||||
|
val = gen.billowNoise(point, layer.octaves);
|
||||||
|
val = (val * 2.0f) - 1.0f;
|
||||||
|
break;
|
||||||
|
case NoiseType::WhiteNoise:
|
||||||
|
val = gen.whiteNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::WorleyNoise:
|
||||||
|
val = gen.worleyNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::VoronoiNoise:
|
||||||
|
val = gen.voronoiNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::CrystalNoise:
|
||||||
|
val = gen.crystalNoise(point);
|
||||||
|
break;
|
||||||
|
case NoiseType::DomainWarp:
|
||||||
|
val = gen.domainWarp(point, 1.0f);
|
||||||
|
break;
|
||||||
|
case NoiseType::CurlNoise:
|
||||||
|
Eigen::Vector2f flow = gen.curlNoise(point);
|
||||||
|
val = flow.norm();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void updateNoiseTexture(NoisePreviewState& state) {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
if (state.textureId == 0) glGenTextures(1, &state.textureId);
|
||||||
|
|
||||||
|
state.pixelBuffer.resize(state.width * state.height * 3);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for (int y = 0; y < state.height; ++y) {
|
||||||
|
PNoise2 generator(state.masterSeed);
|
||||||
|
|
||||||
|
for (int x = 0; x < state.width; ++x) {
|
||||||
|
|
||||||
|
float nx = (x + state.offset[0]);
|
||||||
|
float ny = (y + state.offset[1]);
|
||||||
|
Eigen::Vector2f point(nx, ny);
|
||||||
|
|
||||||
|
float finalValue = 0.0f;
|
||||||
|
|
||||||
|
for (const auto& layer : state.layers) {
|
||||||
|
if (!layer.enabled) continue;
|
||||||
|
|
||||||
|
Eigen::Vector2f samplePoint = point * layer.scale;
|
||||||
|
|
||||||
|
samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f);
|
||||||
|
|
||||||
|
if (layer.blend == BlendMode::DomainWarp) {
|
||||||
|
if (layer.type == NoiseType::CurlNoise) {
|
||||||
|
Eigen::Vector2f flow = generator.curlNoise(samplePoint);
|
||||||
|
point += flow * layer.strength * 100.0f;
|
||||||
|
} else {
|
||||||
|
float warpX = sampleNoiseLayer(generator, layer.type, samplePoint, layer);
|
||||||
|
float warpY = sampleNoiseLayer(generator, layer.type, samplePoint + Eigen::Vector2f(5.2f, 1.3f), layer);
|
||||||
|
point += Eigen::Vector2f(warpX, warpY) * layer.strength * 100.0f;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float nVal = sampleNoiseLayer(generator, layer.type, samplePoint, layer);
|
||||||
|
|
||||||
|
switch (layer.blend) {
|
||||||
|
case BlendMode::Replace:
|
||||||
|
finalValue = nVal * layer.strength;
|
||||||
|
break;
|
||||||
|
case BlendMode::Add:
|
||||||
|
finalValue += nVal * layer.strength;
|
||||||
|
break;
|
||||||
|
case BlendMode::Subtract:
|
||||||
|
finalValue -= nVal * layer.strength;
|
||||||
|
break;
|
||||||
|
case BlendMode::Multiply:
|
||||||
|
finalValue *= (nVal * layer.strength);
|
||||||
|
break;
|
||||||
|
case BlendMode::Max:
|
||||||
|
finalValue = std::max(finalValue, nVal * layer.strength);
|
||||||
|
break;
|
||||||
|
case BlendMode::Min:
|
||||||
|
finalValue = std::min(finalValue, nVal * layer.strength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float norm = std::tanh(finalValue);
|
||||||
|
norm = (norm + 1.0f) * 0.5f;
|
||||||
|
norm = std::clamp(norm, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
uint8_t color = static_cast<uint8_t>(norm * 255);
|
||||||
|
int idx = (y * state.width + x) * 3;
|
||||||
|
state.pixelBuffer[idx] = color;
|
||||||
|
state.pixelBuffer[idx+1] = color;
|
||||||
|
state.pixelBuffer[idx+2] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, state.textureId);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, state.width, state.height,
|
||||||
|
0, GL_RGB, GL_UNSIGNED_BYTE, state.pixelBuffer.data());
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
state.needsUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void saveNoiseState(const NoisePreviewState& state, const std::string& filename) {
|
||||||
|
std::ofstream out(filename);
|
||||||
|
if (!out) return;
|
||||||
|
|
||||||
|
out << "{\n";
|
||||||
|
out << " \"masterSeed\": " << state.masterSeed << ",\n";
|
||||||
|
out << " \"offsetX\": " << state.offset[0] << ",\n";
|
||||||
|
out << " \"offsetY\": " << state.offset[1] << ",\n";
|
||||||
|
out << " \"layers\": [\n";
|
||||||
|
|
||||||
|
for (size_t i = 0; i < state.layers.size(); ++i) {
|
||||||
|
const auto& l = state.layers[i];
|
||||||
|
out << " {\n";
|
||||||
|
out << " \"enabled\": " << (l.enabled ? "true" : "false") << ",\n";
|
||||||
|
out << " \"name\": \"" << l.name << "\",\n";
|
||||||
|
out << " \"type\": " << (int)l.type << ",\n";
|
||||||
|
out << " \"blend\": " << (int)l.blend << ",\n";
|
||||||
|
out << " \"seedOffset\": " << l.seedOffset << ",\n";
|
||||||
|
out << " \"scale\": " << l.scale << ",\n";
|
||||||
|
out << " \"strength\": " << l.strength << ",\n";
|
||||||
|
out << " \"octaves\": " << l.octaves << ",\n";
|
||||||
|
out << " \"persistence\": " << l.persistence << ",\n";
|
||||||
|
out << " \"lacunarity\": " << l.lacunarity << ",\n";
|
||||||
|
out << " \"ridgeOffset\": " << l.ridgeOffset << "\n";
|
||||||
|
out << " }" << (i < state.layers.size() - 1 ? "," : "") << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << " ]\n";
|
||||||
|
out << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void loadNoiseState(NoisePreviewState& state, const std::string& filename) {
|
||||||
|
std::ifstream in(filename);
|
||||||
|
if (!in) return;
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << in.rdbuf();
|
||||||
|
std::string json = buffer.str();
|
||||||
|
|
||||||
|
state.masterSeed = JsonHelper::parseInt(json, "masterSeed", 1337);
|
||||||
|
state.offset[0] = JsonHelper::parseFloat(json, "offsetX", 0.0f);
|
||||||
|
state.offset[1] = JsonHelper::parseFloat(json, "offsetY", 0.0f);
|
||||||
|
|
||||||
|
auto layerStrs = JsonHelper::parseArray(json, "layers");
|
||||||
|
state.layers.clear();
|
||||||
|
|
||||||
|
for (const auto& lStr : layerStrs) {
|
||||||
|
NoiseLayer l;
|
||||||
|
l.enabled = JsonHelper::parseBool(lStr, "enabled", true);
|
||||||
|
std::string name = JsonHelper::parseString(lStr, "name");
|
||||||
|
if (!name.empty()) {
|
||||||
|
std::strncpy(l.name, name.c_str(), 31);
|
||||||
|
l.name[31] = '\0';
|
||||||
|
}
|
||||||
|
l.type = (NoiseType)JsonHelper::parseInt(lStr, "type", 0);
|
||||||
|
l.blend = (BlendMode)JsonHelper::parseInt(lStr, "blend", 0);
|
||||||
|
l.seedOffset = JsonHelper::parseInt(lStr, "seedOffset", 0);
|
||||||
|
l.scale = JsonHelper::parseFloat(lStr, "scale", 0.02f);
|
||||||
|
l.strength = JsonHelper::parseFloat(lStr, "strength", 1.0f);
|
||||||
|
l.octaves = JsonHelper::parseInt(lStr, "octaves", 4);
|
||||||
|
l.persistence = JsonHelper::parseFloat(lStr, "persistence", 0.5f);
|
||||||
|
l.lacunarity = JsonHelper::parseFloat(lStr, "lacunarity", 2.0f);
|
||||||
|
l.ridgeOffset = JsonHelper::parseFloat(lStr, "ridgeOffset", 1.0f);
|
||||||
|
|
||||||
|
state.layers.push_back(l);
|
||||||
|
}
|
||||||
|
state.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void drawNoiseLab(NoisePreviewState& noiseState) {
|
||||||
|
ImGui::Begin("2D Noise Lab");
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
static char filenameBuffer[128] = "output/noise_preset.json";
|
||||||
|
ImGui::InputText("File", filenameBuffer, sizeof(filenameBuffer));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Save JSON")) {
|
||||||
|
saveNoiseState(noiseState, filenameBuffer);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Load JSON")) {
|
||||||
|
loadNoiseState(noiseState, filenameBuffer);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
changed |= ImGui::InputInt("Master Seed", &noiseState.masterSeed);
|
||||||
|
changed |= ImGui::DragFloat2("Pan Offset", noiseState.offset, 1.0f);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Add Layer")) {
|
||||||
|
NoiseLayer l;
|
||||||
|
sprintf(l.name, "Layer %zu", noiseState.layers.size());
|
||||||
|
noiseState.layers.push_back(l);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Clear All")) {
|
||||||
|
noiseState.layers.clear();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginChild("LayersScroll", ImVec2(0, 300), true);
|
||||||
|
|
||||||
|
for (int i = 0; i < noiseState.layers.size(); ++i) {
|
||||||
|
NoiseLayer& layer = noiseState.layers[i];
|
||||||
|
|
||||||
|
ImGui::PushID(i);
|
||||||
|
|
||||||
|
bool open = ImGui::CollapsingHeader(layer.name, ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextItem()) {
|
||||||
|
if (ImGui::MenuItem("Move Up", nullptr, false, i > 0)) {
|
||||||
|
std::swap(noiseState.layers[i], noiseState.layers[i-1]);
|
||||||
|
changed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("Move Down", nullptr, false, i < noiseState.layers.size()-1)) {
|
||||||
|
std::swap(noiseState.layers[i], noiseState.layers[i+1]);
|
||||||
|
changed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("Duplicate")) {
|
||||||
|
noiseState.layers.insert(noiseState.layers.begin() + i, layer);
|
||||||
|
changed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("Delete")) {
|
||||||
|
noiseState.layers.erase(noiseState.layers.begin() + i);
|
||||||
|
changed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
ImGui::PopID();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
if (ImGui::Checkbox("##enabled", &layer.enabled)) changed = true;
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::InputText("##name", layer.name, 32);
|
||||||
|
|
||||||
|
const char* types[] = { "Perlin", "Value", "Fractal", "Turbulence", "Ridged", "Billow", "White", "Worley", "Voronoi", "Crystal", "Domain Warp", "Curl" };
|
||||||
|
const char* blends[] = { "Add", "Subtract", "Multiply", "Min", "Max", "Replace", "Domain Warp (Coord)" };
|
||||||
|
|
||||||
|
if (ImGui::Combo("Type", (int*)&layer.type, types, IM_ARRAYSIZE(types))) changed = true;
|
||||||
|
if (ImGui::Combo("Blend", (int*)&layer.blend, blends, IM_ARRAYSIZE(blends))) changed = true;
|
||||||
|
|
||||||
|
if (ImGui::SliderFloat("Scale", &layer.scale, 0.0001f, 0.2f, "%.5f")) changed = true;
|
||||||
|
if (ImGui::SliderFloat("Strength/Weight", &layer.strength, 0.0f, 5.0f)) changed = true;
|
||||||
|
if (ImGui::SliderInt("Seed Offset", &layer.seedOffset, 0, 100)) changed = true;
|
||||||
|
|
||||||
|
if (layer.type == NoiseType::Fractal || layer.type == NoiseType::Turbulence ||
|
||||||
|
layer.type == NoiseType::Ridged || layer.type == NoiseType::Billow) {
|
||||||
|
|
||||||
|
if (ImGui::SliderInt("Octaves", &layer.octaves, 1, 10)) changed = true;
|
||||||
|
if (layer.type == NoiseType::Fractal) {
|
||||||
|
if (ImGui::SliderFloat("Persistence", &layer.persistence, 0.0f, 1.0f)) changed = true;
|
||||||
|
if (ImGui::SliderFloat("Lacunarity", &layer.lacunarity, 1.0f, 4.0f)) changed = true;
|
||||||
|
}
|
||||||
|
if (layer.type == NoiseType::Ridged) {
|
||||||
|
if (ImGui::SliderFloat("Ridge Offset", &layer.ridgeOffset, 0.0f, 2.0f)) changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Text("Preview Output");
|
||||||
|
ImGui::Image((void*)(intptr_t)noiseState.textureId, ImVec2((float)noiseState.width, (float)noiseState.height));
|
||||||
|
|
||||||
|
if (changed || noiseState.needsUpdate) {
|
||||||
|
updateNoiseTexture(noiseState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PERLIN_NOISE_HPP
|
|
||||||
#define PERLIN_NOISE_HPP
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
#include <random>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class PerlinNoise {
|
|
||||||
private:
|
|
||||||
std::vector<int> permutation;
|
|
||||||
|
|
||||||
// Fade function as defined by Ken Perlin
|
|
||||||
static double fade(double t) {
|
|
||||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linear interpolation
|
|
||||||
static double lerp(double t, double a, double b) {
|
|
||||||
return a + t * (b - a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gradient function
|
|
||||||
static double grad(int hash, double x, double y, double z) {
|
|
||||||
int h = hash & 15;
|
|
||||||
double u = h < 8 ? x : y;
|
|
||||||
double v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
|
|
||||||
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructor with optional seed
|
|
||||||
PerlinNoise(unsigned int seed = 0) {
|
|
||||||
permutation.resize(256);
|
|
||||||
// Initialize with values 0-255
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
permutation[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle using the seed
|
|
||||||
std::shuffle(permutation.begin(), permutation.end(), std::default_random_engine(seed));
|
|
||||||
|
|
||||||
// Duplicate the permutation vector
|
|
||||||
permutation.insert(permutation.end(), permutation.begin(), permutation.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1D Perlin noise
|
|
||||||
double noise(double x) const {
|
|
||||||
return noise(x, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2D Perlin noise
|
|
||||||
double noise(double x, double y) const {
|
|
||||||
return noise(x, y, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3D Perlin noise (main implementation)
|
|
||||||
double noise(double x, double y, double z) const {
|
|
||||||
// Find unit cube that contains the point
|
|
||||||
int X = (int)floor(x) & 255;
|
|
||||||
int Y = (int)floor(y) & 255;
|
|
||||||
int Z = (int)floor(z) & 255;
|
|
||||||
|
|
||||||
// Find relative x, y, z of point in cube
|
|
||||||
x -= floor(x);
|
|
||||||
y -= floor(y);
|
|
||||||
z -= floor(z);
|
|
||||||
|
|
||||||
// Compute fade curves for x, y, z
|
|
||||||
double u = fade(x);
|
|
||||||
double v = fade(y);
|
|
||||||
double w = fade(z);
|
|
||||||
|
|
||||||
// Hash coordinates of the 8 cube corners
|
|
||||||
int A = permutation[X] + Y;
|
|
||||||
int AA = permutation[A] + Z;
|
|
||||||
int AB = permutation[A + 1] + Z;
|
|
||||||
int B = permutation[X + 1] + Y;
|
|
||||||
int BA = permutation[B] + Z;
|
|
||||||
int BB = permutation[B + 1] + Z;
|
|
||||||
|
|
||||||
// Add blended results from 8 corners of cube
|
|
||||||
double res = lerp(w, lerp(v, lerp(u, grad(permutation[AA], x, y, z),
|
|
||||||
grad(permutation[BA], x - 1, y, z)),
|
|
||||||
lerp(u, grad(permutation[AB], x, y - 1, z),
|
|
||||||
grad(permutation[BB], x - 1, y - 1, z))),
|
|
||||||
lerp(v, lerp(u, grad(permutation[AA + 1], x, y, z - 1),
|
|
||||||
grad(permutation[BA + 1], x - 1, y, z - 1)),
|
|
||||||
lerp(u, grad(permutation[AB + 1], x, y - 1, z - 1),
|
|
||||||
grad(permutation[BB + 1], x - 1, y - 1, z - 1))));
|
|
||||||
return (res + 1.0) / 2.0; // Normalize to [0,1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fractal Brownian Motion (fBm) - multiple octaves of noise
|
|
||||||
double fractal(size_t octaves, double x, double y = 0.0, double z = 0.0) const {
|
|
||||||
double value = 0.0;
|
|
||||||
double amplitude = 1.0;
|
|
||||||
double frequency = 1.0;
|
|
||||||
double maxValue = 0.0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < octaves; ++i) {
|
|
||||||
value += amplitude * noise(x * frequency, y * frequency, z * frequency);
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= 0.5;
|
|
||||||
frequency *= 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value / maxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turbulence - absolute value of noise for more dramatic effects
|
|
||||||
double turbulence(size_t octaves, double x, double y = 0.0, double z = 0.0) const {
|
|
||||||
double value = 0.0;
|
|
||||||
double amplitude = 1.0;
|
|
||||||
double frequency = 1.0;
|
|
||||||
double maxValue = 0.0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < octaves; ++i) {
|
|
||||||
value += amplitude * std::abs(noise(x * frequency, y * frequency, z * frequency));
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= 0.5;
|
|
||||||
frequency *= 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value / maxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ridged multi-fractal - creates ridge-like patterns
|
|
||||||
double ridgedMultiFractal(size_t octaves, double x, double y = 0.0, double z = 0.0,
|
|
||||||
double lacunarity = 2.0, double gain = 0.5, double offset = 1.0) const {
|
|
||||||
double value = 0.0;
|
|
||||||
double amplitude = 1.0;
|
|
||||||
double frequency = 1.0;
|
|
||||||
double prev = 1.0;
|
|
||||||
double weight;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < octaves; ++i) {
|
|
||||||
double signal = offset - std::abs(noise(x * frequency, y * frequency, z * frequency));
|
|
||||||
signal *= signal;
|
|
||||||
signal *= prev;
|
|
||||||
|
|
||||||
weight = std::clamp(signal * gain, 0.0, 1.0);
|
|
||||||
value += signal * amplitude;
|
|
||||||
prev = weight;
|
|
||||||
amplitude *= weight;
|
|
||||||
frequency *= lacunarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utility functions for common noise operations
|
|
||||||
namespace PerlinUtils {
|
|
||||||
// Create a 1D noise array
|
|
||||||
static std::vector<double> generate1DNoise(int width, double scale = 1.0, unsigned int seed = 0) {
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<double> result(width);
|
|
||||||
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
result[x] = pn.noise(x * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a 2D noise array
|
|
||||||
static std::vector<std::vector<double>> generate2DNoise(int width, int height,
|
|
||||||
double scale = 1.0, unsigned int seed = 0) {
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<double>> result(height, std::vector<double>(width));
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
result[y][x] = pn.noise(x * scale, y * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a 3D noise array
|
|
||||||
static std::vector<std::vector<std::vector<double>>> generate3DNoise(int width, int height, int depth,
|
|
||||||
double scale = 1.0, unsigned int seed = 0) {
|
|
||||||
PerlinNoise pn(seed);
|
|
||||||
std::vector<std::vector<std::vector<double>>> result(
|
|
||||||
depth, std::vector<std::vector<double>>(
|
|
||||||
height, std::vector<double>(width)));
|
|
||||||
|
|
||||||
for (int z = 0; z < depth; ++z) {
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
result[z][y][x] = pn.noise(x * scale, y * scale, z * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -6,43 +6,80 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include "../vectorlogic/vec2.hpp"
|
#include <limits>
|
||||||
#include "../vectorlogic/vec3.hpp"
|
#include "../../eigen/Eigen/Core"
|
||||||
#include "../timing_decorator.hpp"
|
#include "../timing_decorator.hpp"
|
||||||
|
#include "../basicdefines.hpp"
|
||||||
|
|
||||||
class PNoise2 {
|
class PNoise2 {
|
||||||
private:
|
private:
|
||||||
std::vector<int> permutation;
|
std::vector<int> permutation;
|
||||||
std::default_random_engine rng;
|
std::default_random_engine rng;
|
||||||
|
|
||||||
|
using Vector2f = Eigen::Vector2f;
|
||||||
|
using Vector3f = Eigen::Vector3f;
|
||||||
|
using Vector4f = Eigen::Vector4f;
|
||||||
|
|
||||||
|
/// @brief Linear interpolation between two values
|
||||||
|
/// @param t Interpolation factor [0,1]
|
||||||
|
/// @param a1 First value
|
||||||
|
/// @param a2 Second value
|
||||||
|
/// @return Interpolated value between a1 and a2
|
||||||
|
/// @note Changing interpolation method affects noise smoothness
|
||||||
float lerp(float t, float a1, float a2) {
|
float lerp(float t, float a1, float a2) {
|
||||||
return a1 + t * (a2 - a1);
|
return a1 + t * (a2 - a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Fade function for smooth interpolation
|
||||||
|
/// @param t Input parameter
|
||||||
|
/// @return Smoothed t value using 6t^5 - 15t^4 + 10t^3
|
||||||
|
/// @note Critical for gradient continuity; changes affect noise smoothness
|
||||||
static double fade(double t) {
|
static double fade(double t) {
|
||||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 GetConstantVector(int v) {
|
/// @brief Get constant gradient vector for 2D Perlin noise
|
||||||
|
/// @param v Hash value (0-3)
|
||||||
|
/// @return One of four 2D gradient vectors
|
||||||
|
/// @note Changing vectors affects noise pattern orientation
|
||||||
|
Vector2f GetConstantVector(int v) {
|
||||||
int h = v & 3;
|
int h = v & 3;
|
||||||
if (h == 0) return Vec2(1,1);
|
if (h == 0) return Vector2f(1,1);
|
||||||
else if (h == 1) return Vec2(-1,1);
|
else if (h == 1) return Vector2f(-1,1);
|
||||||
else if (h == 2) return Vec2(-1,-1);
|
else if (h == 2) return Vector2f(-1,-1);
|
||||||
else return Vec2(1,-1);
|
else return Vector2f(1,-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3ui8 GetConstantVector3(int v) {
|
/// @brief Get constant gradient vector for 3D Perlin noise
|
||||||
int h = v & 7;
|
/// @param v Hash value (0-11)
|
||||||
if (h == 0) return Vec3ui8(1,1,1);
|
/// @return One of twelve 3D gradient vectors
|
||||||
else if (h == 1) return Vec3ui8(-1,1, 1);
|
/// @note Vector selection affects 3D noise patterns
|
||||||
else if (h == 2) return Vec3ui8(-1,-1, 1);
|
Vector3f GetConstantVector3(int v) {
|
||||||
else if (h == 3) return Vec3ui8(-1,-1, 1);
|
int h = v & 11;
|
||||||
else if (h == 4) return Vec3ui8(-1,-1,-1);
|
switch(h) {
|
||||||
else if (h == 5) return Vec3ui8(-1,-1, -1);
|
case 0: return Vector3f( 1, 1, 0);
|
||||||
else if (h == 6) return Vec3ui8(-1,-1, -1);
|
case 1: return Vector3f(-1, 1, 0);
|
||||||
else return Vec3ui8(1,-1, -1);
|
case 2: return Vector3f( 1,-1, 0);
|
||||||
|
case 3: return Vector3f(-1,-1, 0);
|
||||||
|
case 4: return Vector3f( 1, 0, 1);
|
||||||
|
case 5: return Vector3f(-1, 0, 1);
|
||||||
|
case 6: return Vector3f( 1, 0,-1);
|
||||||
|
case 7: return Vector3f(-1, 0,-1);
|
||||||
|
case 8: return Vector3f( 0, 1, 1);
|
||||||
|
case 9: return Vector3f( 0,-1, 1);
|
||||||
|
case 10: return Vector3f( 0, 1,-1);
|
||||||
|
case 11: return Vector3f( 0,-1,-1);
|
||||||
|
default: return Vector3f(0,0,0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Gradient function for 2D/3D Perlin noise
|
||||||
|
/// @param hash Hash value for gradient selection
|
||||||
|
/// @param x X coordinate
|
||||||
|
/// @param y Y coordinate
|
||||||
|
/// @param z Z coordinate (default 0 for 2D)
|
||||||
|
/// @return Dot product of gradient vector and distance vector
|
||||||
|
/// @note Core of Perlin noise; changes affect basic noise character
|
||||||
static double grad(int hash, double x, double y, double z = 0.0) {
|
static double grad(int hash, double x, double y, double z = 0.0) {
|
||||||
int h = hash & 15;
|
int h = hash & 15;
|
||||||
double u = h < 8 ? x : y;
|
double u = h < 8 ? x : y;
|
||||||
@@ -50,6 +87,21 @@ private:
|
|||||||
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Gradient function for 2D only Perlin noise
|
||||||
|
/// @param hash Hash value for gradient selection
|
||||||
|
/// @param x X coordinate
|
||||||
|
/// @param y Y coordinate
|
||||||
|
/// @return Dot product of gradient vector and distance vector
|
||||||
|
/// @note Core of Perlin noise; changes affect basic noise character
|
||||||
|
inline static float grad2(int hash, float x, float y) {
|
||||||
|
int h = hash & 15;
|
||||||
|
float u = h < 8 ? x : y;
|
||||||
|
float v = h < 4 ? y : ((h == 12 || h == 14) ? x : 0.0f);
|
||||||
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Initialize permutation table with shuffled values
|
||||||
|
/// @note Called on construction; changing seed or shuffle affects all noise patterns
|
||||||
void initializePermutation() {
|
void initializePermutation() {
|
||||||
permutation.clear();
|
permutation.clear();
|
||||||
std::vector<int> permutationt;
|
std::vector<int> permutationt;
|
||||||
@@ -61,70 +113,180 @@ private:
|
|||||||
permutation.insert(permutation.end(), permutationt.begin(), permutationt.end());
|
permutation.insert(permutation.end(), permutationt.begin(), permutationt.end());
|
||||||
permutation.insert(permutation.end(), permutationt.begin(), permutationt.end());
|
permutation.insert(permutation.end(), permutationt.begin(), permutationt.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Normalize noise value from [-1,1] to [0,1]
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Normalized noise value in [0,1] range
|
||||||
|
/// @note Useful for texture generation; changes affect output range
|
||||||
|
float normalizedNoise(const Vector2f& point) {
|
||||||
|
return (permute(point) + 1.0f) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Normalize 3D noise value from [-1,1] to [0,1]
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Normalized noise value in [0,1] range
|
||||||
|
float normalizedNoise(const Vector3f& point) {
|
||||||
|
return (permute(point) + 1.0f) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Map value from one range to another
|
||||||
|
/// @param value Input value
|
||||||
|
/// @param inMin Original range minimum
|
||||||
|
/// @param inMax Original range maximum
|
||||||
|
/// @param outMin Target range minimum
|
||||||
|
/// @param outMax Target range maximum
|
||||||
|
/// @return Value mapped to new range
|
||||||
|
/// @note Useful for post-processing; changes affect output scaling
|
||||||
|
float mapRange(float value, float inMin, float inMax, float outMin, float outMax) {
|
||||||
|
return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Blend two noise values
|
||||||
|
/// @param noise1 First noise value
|
||||||
|
/// @param noise2 Second noise value
|
||||||
|
/// @param blendFactor Blending factor [0,1]
|
||||||
|
/// @return Blended noise value
|
||||||
|
/// @note Changes affect multi-layer noise combinations
|
||||||
|
float blendNoises(float noise1, float noise2, float blendFactor) {
|
||||||
|
return lerp(blendFactor, noise1, noise2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Add two noise values with clamping
|
||||||
|
/// @param noise1 First noise value
|
||||||
|
/// @param noise2 Second noise value
|
||||||
|
/// @return Sum clamped to [-1,1]
|
||||||
|
/// @note Clamping prevents overflow; changes affect combined noise magnitude
|
||||||
|
float addNoises(float noise1, float noise2) {
|
||||||
|
return std::clamp(noise1 + noise2, -1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Multiply two noise values
|
||||||
|
/// @param noise1 First noise value
|
||||||
|
/// @param noise2 Second noise value
|
||||||
|
/// @return Product of noise values
|
||||||
|
/// @note Creates modulation effects; changes affect combined noise character
|
||||||
|
float multiplyNoises(float noise1, float noise2) {
|
||||||
|
return noise1 * noise2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Hash function for 2D coordinates
|
||||||
|
/// @param x X coordinate integer
|
||||||
|
/// @param y Y coordinate integer
|
||||||
|
/// @return Hash value in [-1,1] range
|
||||||
|
/// @note Core of value noise; changes affect random distribution
|
||||||
|
float hash(int x, int y) {
|
||||||
|
return (permutation[(x + permutation[y & 255]) & 255] / 255.0f) * 2.0f - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Hash function for 3D coordinates
|
||||||
|
/// @param x X coordinate integer
|
||||||
|
/// @param y Y coordinate integer
|
||||||
|
/// @param z Z coordinate integer
|
||||||
|
/// @return Hash value in [-1,1] range
|
||||||
|
/// @note 3D version of hash function
|
||||||
|
float hash(int x, int y, int z) {
|
||||||
|
return (permutation[(z + permutation[(y + permutation[x & 255]) & 255]) & 255] / 255.0f) * 2.0f - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pseudo-random vector for Worley noise (2D)
|
||||||
|
Vector2f hashVector(const Vector2f& gridPoint) {
|
||||||
|
int x = (int)gridPoint.x() & 255;
|
||||||
|
int y = (int)gridPoint.y() & 255;
|
||||||
|
// Generate pseudo-random float [0,1] for x and y offsets
|
||||||
|
float hx = permutation[(x + permutation[y]) & 255] / 255.0f;
|
||||||
|
float hy = permutation[(y + permutation[(x + 1) & 255]) & 255] / 255.0f;
|
||||||
|
return Vector2f(hx, hy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pseudo-random vector for Worley noise (3D)
|
||||||
|
Vector3f hashVector(const Vector3f& gridPoint) {
|
||||||
|
int x = (int)gridPoint.x() & 255;
|
||||||
|
int y = (int)gridPoint.y() & 255;
|
||||||
|
int z = (int)gridPoint.z() & 255;
|
||||||
|
|
||||||
|
int h_xy = permutation[(x + permutation[y]) & 255];
|
||||||
|
float hx = permutation[(h_xy + z) & 255] / 255.0f;
|
||||||
|
float hy = permutation[(h_xy + permutation[(z + 1) & 255]) & 255] / 255.0f;
|
||||||
|
float hz = permutation[(permutation[(x+1)&255] + permutation[(y+1)&255] + z) & 255] / 255.0f;
|
||||||
|
|
||||||
|
return Vector3f(hx, hy, hz);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// @brief Default constructor with random seed
|
||||||
|
/// @note Uses random_device for seed; different runs produce different noise
|
||||||
PNoise2() : rng(std::random_device{}()) {
|
PNoise2() : rng(std::random_device{}()) {
|
||||||
initializePermutation();
|
initializePermutation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Constructor with specified seed
|
||||||
|
/// @param seed Random seed value
|
||||||
|
/// @note Same seed produces identical noise patterns across runs
|
||||||
PNoise2(unsigned int seed) : rng(seed) {
|
PNoise2(unsigned int seed) : rng(seed) {
|
||||||
initializePermutation();
|
initializePermutation();
|
||||||
}
|
}
|
||||||
|
|
||||||
float permute(Vec2 point) {
|
/// @brief Generate 2D Perlin noise at given point
|
||||||
TIME_FUNCTION;
|
/// @param point 2D coordinate
|
||||||
float x = point.x;
|
/// @return Noise value in [-1,1] range
|
||||||
float y = point.y;
|
/// @note Core 2D noise function; changes affect all 2D noise outputs
|
||||||
|
float permute(const Vector2f& point) {
|
||||||
|
// TIME_FUNCTION;
|
||||||
|
float x = point.x();
|
||||||
|
float y = point.y();
|
||||||
int X = (int)floor(x);
|
int X = (int)floor(x);
|
||||||
int xmod = X & 255;
|
int xmod = X & 255;
|
||||||
int Y = (int)floor(point.y);
|
int Y = (int)floor(y);
|
||||||
int ymod = Y & 255;
|
int ymod = Y & 255;
|
||||||
float xf = point.x - X;
|
float xf = x - X;
|
||||||
float yf = point.y - Y;
|
float yf = y - Y;
|
||||||
|
|
||||||
Vec2 BL = Vec2(xf-0, yf-0);
|
Vector2f BL(xf-0, yf-0);
|
||||||
Vec2 BR = Vec2(xf-1, yf-0);
|
Vector2f BR(xf-1, yf-0);
|
||||||
Vec2 TL = Vec2(xf-0, yf-1);
|
Vector2f TL(xf-0, yf-1);
|
||||||
Vec2 TR = Vec2(xf-1, yf-1);
|
Vector2f TR(xf-1, yf-1);
|
||||||
|
|
||||||
int vBL = permutation[permutation[xmod+0]+ymod+0];
|
int vBL = permutation[permutation[xmod+0]+ymod+0];
|
||||||
int vBR = permutation[permutation[xmod+1]+ymod+0];
|
int vBR = permutation[permutation[xmod+1]+ymod+0];
|
||||||
int vTL = permutation[permutation[xmod+0]+ymod+1];
|
int vTL = permutation[permutation[xmod+0]+ymod+1];
|
||||||
int vTR = permutation[permutation[xmod+1]+ymod+1];
|
int vTR = permutation[permutation[xmod+1]+ymod+1];
|
||||||
|
|
||||||
float dBL = BL.dot(GetConstantVector(vBL));
|
|
||||||
float dBR = BR.dot(GetConstantVector(vBR));
|
|
||||||
float dTL = TL.dot(GetConstantVector(vTL));
|
|
||||||
float dTR = TR.dot(GetConstantVector(vTR));
|
|
||||||
|
|
||||||
float u = fade(xf);
|
float u = fade(xf);
|
||||||
float v = fade(yf);
|
float v = fade(yf);
|
||||||
|
|
||||||
float x1 = lerp(u, grad(vBL, xf, yf), grad(vBR, xf - 1, yf));
|
float x1 = lerp(u, grad(vBL, xf, yf), grad(vBR, xf - 1, yf));
|
||||||
float x2 = lerp(u, grad(vTL, xf, yf - 1), grad(vTR, xf - 1, yf - 1));
|
float x2 = lerp(u, grad(vTL, xf, yf - 1), grad(vTR, xf - 1, yf - 1));
|
||||||
float retval = lerp(v, x1, x2);
|
float retval = lerp(v, x1, x2);
|
||||||
//std::cout << "returning: " << retval << std::endl;
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
/// @brief Generate 3D Perlin noise at given point
|
||||||
float permute(Vec3<T> point) {
|
/// @param point 3D coordinate
|
||||||
TIME_FUNCTION;
|
/// @return Noise value in [-1,1] range
|
||||||
int X = (int)floor(point.x) & 255;
|
float permute(const Vector3f& point) {
|
||||||
int Y = (int)floor(point.y) & 255;
|
// TIME_FUNCTION;
|
||||||
int Z = (int)floor(point.z) & 255;
|
float x = point.x();
|
||||||
float xf = point.x - X;
|
float y = point.y();
|
||||||
float yf = point.y - Y;
|
float z = point.z();
|
||||||
float zf = point.z - Z;
|
|
||||||
|
|
||||||
Vec3ui8 FBL = Vec3ui8(xf-0, yf-0, zf-0);
|
int X = (int)floor(x) & 255;
|
||||||
Vec3ui8 FBR = Vec3ui8(xf-1, yf-0, zf-0);
|
int Y = (int)floor(y) & 255;
|
||||||
Vec3ui8 FTL = Vec3ui8(xf-0, yf-1, zf-0);
|
int Z = (int)floor(z) & 255;
|
||||||
Vec3ui8 FTR = Vec3ui8(xf-1, yf-1, zf-0);
|
float xf = x - X;
|
||||||
|
float yf = y - Y;
|
||||||
|
float zf = z - Z;
|
||||||
|
|
||||||
Vec3ui8 RBL = Vec3ui8(xf-0, yf-0, zf-1);
|
// Distance vectors from corners
|
||||||
Vec3ui8 RBR = Vec3ui8(xf-1, yf-0, zf-1);
|
Vector3f FBL(xf-0, yf-0, zf-0);
|
||||||
Vec3ui8 RTL = Vec3ui8(xf-0, yf-1, zf-1);
|
Vector3f FBR(xf-1, yf-0, zf-0);
|
||||||
Vec3ui8 RTR = Vec3ui8(xf-1, yf-1, zf-1);
|
Vector3f FTL(xf-0, yf-1, zf-0);
|
||||||
|
Vector3f FTR(xf-1, yf-1, zf-0);
|
||||||
|
|
||||||
|
Vector3f RBL(xf-0, yf-0, zf-1);
|
||||||
|
Vector3f RBR(xf-1, yf-0, zf-1);
|
||||||
|
Vector3f RTL(xf-0, yf-1, zf-1);
|
||||||
|
Vector3f RTR(xf-1, yf-1, zf-1);
|
||||||
|
|
||||||
int vFBL = permutation[permutation[permutation[Z+0]+X+0]+Y+0];
|
int vFBL = permutation[permutation[permutation[Z+0]+X+0]+Y+0];
|
||||||
int vFBR = permutation[permutation[permutation[Z+0]+X+1]+Y+0];
|
int vFBR = permutation[permutation[permutation[Z+0]+X+1]+Y+0];
|
||||||
@@ -136,16 +298,6 @@ public:
|
|||||||
int vRTL = permutation[permutation[permutation[Z+1]+X+0]+Y+1];
|
int vRTL = permutation[permutation[permutation[Z+1]+X+0]+Y+1];
|
||||||
int vRTR = permutation[permutation[permutation[Z+1]+X+1]+Y+1];
|
int vRTR = permutation[permutation[permutation[Z+1]+X+1]+Y+1];
|
||||||
|
|
||||||
float dFBL = FBL.dot(GetConstantVector3(vFBL));
|
|
||||||
float dFBR = FBR.dot(GetConstantVector3(vFBR));
|
|
||||||
float dFTL = FTL.dot(GetConstantVector3(vFTL));
|
|
||||||
float dFTR = FTR.dot(GetConstantVector3(vFTR));
|
|
||||||
|
|
||||||
float dRBL = RBL.dot(GetConstantVector3(vRBL));
|
|
||||||
float dRBR = RBR.dot(GetConstantVector3(vRBR));
|
|
||||||
float dRTL = RTL.dot(GetConstantVector3(vRTL));
|
|
||||||
float dRTR = RTR.dot(GetConstantVector3(vRTR));
|
|
||||||
|
|
||||||
float u = fade(xf);
|
float u = fade(xf);
|
||||||
float v = fade(yf);
|
float v = fade(yf);
|
||||||
float w = fade(zf);
|
float w = fade(zf);
|
||||||
@@ -160,10 +312,451 @@ public:
|
|||||||
|
|
||||||
float retval = lerp(w, y1, y2);
|
float retval = lerp(w, y1, y2);
|
||||||
|
|
||||||
//std::cout << "returning: " << retval << std::endl;
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 2D value noise (simpler alternative to Perlin)
|
||||||
|
/// @param point 2D coordinate
|
||||||
|
/// @return Noise value in [-1,1] range
|
||||||
|
/// @note Different character than Perlin; changes affect value-based textures
|
||||||
|
float valueNoise(const Vector2f& point) {
|
||||||
|
int xi = (int)std::floor(point.x());
|
||||||
|
int yi = (int)std::floor(point.y());
|
||||||
|
|
||||||
|
float tx = point.x() - xi;
|
||||||
|
float ty = point.y() - yi;
|
||||||
|
|
||||||
|
int rx0 = xi & 255;
|
||||||
|
int rx1 = (xi + 1) & 255;
|
||||||
|
int ry0 = yi & 255;
|
||||||
|
int ry1 = (yi + 1) & 255;
|
||||||
|
|
||||||
|
// Random values at corners
|
||||||
|
float c00 = hash(rx0, ry0);
|
||||||
|
float c10 = hash(rx1, ry0);
|
||||||
|
float c01 = hash(rx0, ry1);
|
||||||
|
float c11 = hash(rx1, ry1);
|
||||||
|
|
||||||
|
// Interpolation
|
||||||
|
float sx = fade(tx);
|
||||||
|
float sy = fade(ty);
|
||||||
|
|
||||||
|
float nx0 = lerp(sy, c00, c10);
|
||||||
|
float nx1 = lerp(sx, c01, c11);
|
||||||
|
|
||||||
|
return lerp(nx0, nx1, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 3D value noise
|
||||||
|
/// @param point 3D coordinate
|
||||||
|
/// @return Noise value in [-1,1] range
|
||||||
|
float valueNoise(const Vector3f& point) {
|
||||||
|
int xi = (int)std::floor(point.x());
|
||||||
|
int yi = (int)std::floor(point.y());
|
||||||
|
int zi = (int)std::floor(point.z());
|
||||||
|
|
||||||
|
float tx = point.x() - xi;
|
||||||
|
float ty = point.y() - yi;
|
||||||
|
float tz = point.z() - zi;
|
||||||
|
|
||||||
|
int rx0 = xi & 255;
|
||||||
|
int rx1 = (xi + 1) & 255;
|
||||||
|
int ry0 = yi & 255;
|
||||||
|
int ry1 = (yi + 1) & 255;
|
||||||
|
int rz0 = zi & 255;
|
||||||
|
int rz1 = (zi + 1) & 255;
|
||||||
|
|
||||||
|
// Random values at corners
|
||||||
|
float c000 = hash(rx0, ry0, rz0);
|
||||||
|
float c100 = hash(rx1, ry0, rz0);
|
||||||
|
float c010 = hash(rx0, ry1, rz0);
|
||||||
|
float c110 = hash(rx1, ry1, rz0);
|
||||||
|
float c001 = hash(rx0, ry0, rz1);
|
||||||
|
float c101 = hash(rx1, ry0, rz1);
|
||||||
|
float c011 = hash(rx0, ry1, rz1);
|
||||||
|
float c111 = hash(rx1, ry1, rz1);
|
||||||
|
|
||||||
|
// Interpolation
|
||||||
|
float sx = fade(tx);
|
||||||
|
float sy = fade(ty);
|
||||||
|
float sz = fade(tz);
|
||||||
|
|
||||||
|
float nx00 = lerp(sx, c000, c100);
|
||||||
|
float nx10 = lerp(sx, c010, c110);
|
||||||
|
float nx01 = lerp(sx, c001, c101);
|
||||||
|
float nx11 = lerp(sx, c011, c111);
|
||||||
|
|
||||||
|
float ny0 = lerp(sy, nx00, nx10);
|
||||||
|
float ny1 = lerp(sy, nx01, nx11);
|
||||||
|
|
||||||
|
return lerp(sz, ny0, ny1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate RGBA color from 3D noise with offset channels
|
||||||
|
/// @param point 3D coordinate
|
||||||
|
/// @return Vector4f containing RGBA noise values
|
||||||
|
/// @note Each channel uses different offset; changes affect color patterns
|
||||||
|
Vector4f permuteColor(const Vector3f& point) {
|
||||||
|
// TIME_FUNCTION;
|
||||||
|
float noiseR = permute(point);
|
||||||
|
float noiseG = permute(Vector3f(point + Vector3f(100.0f, 100.0f, 100.0f)));
|
||||||
|
float noiseB = permute(Vector3f(point + Vector3f(200.0f, 200.0f, 200.0f)));
|
||||||
|
float noiseA = permute(Vector3f(point + Vector3f(300.0f, 300.0f, 300.0f)));
|
||||||
|
|
||||||
|
float rNormalized = (noiseR + 1.0f) * 0.5f;
|
||||||
|
float gNormalized = (noiseG + 1.0f) * 0.5f;
|
||||||
|
float bNormalized = (noiseB + 1.0f) * 0.5f;
|
||||||
|
float aNormalized = (noiseA + 1.0f) * 0.5f;
|
||||||
|
|
||||||
|
rNormalized = std::clamp(rNormalized, 0.0f, 1.0f);
|
||||||
|
gNormalized = std::clamp(gNormalized, 0.0f, 1.0f);
|
||||||
|
bNormalized = std::clamp(bNormalized, 0.0f, 1.0f);
|
||||||
|
aNormalized = std::clamp(aNormalized, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
return Vector4f(rNormalized, gNormalized, bNormalized, aNormalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate fractal (octave) noise for natural-looking patterns
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @param persistence Amplitude multiplier per octave
|
||||||
|
/// @param lacunarity Frequency multiplier per octave
|
||||||
|
/// @return Combined noise value
|
||||||
|
/// @note Parameters control noise character: octaves=detail, persistence=roughness, lacunarity=frequency change
|
||||||
|
float fractalNoise(const Vector2f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) {
|
||||||
|
float frequency = 1.f;
|
||||||
|
float amplitude = 1.f;
|
||||||
|
float maxV = 0.f;
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
Vector2f scaledPoint = point * frequency;
|
||||||
|
maxV += permute(scaledPoint) * amplitude;
|
||||||
|
amplitude *= persistence;
|
||||||
|
frequency *= lacunarity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 3D fractal (octave) noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @param persistence Amplitude multiplier per octave
|
||||||
|
/// @param lacunarity Frequency multiplier per octave
|
||||||
|
/// @return Combined noise value
|
||||||
|
float fractalNoise(const Vector3f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) {
|
||||||
|
float frequency = 1.f;
|
||||||
|
float amplitude = 1.f;
|
||||||
|
float maxV = 0.f;
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
Vector3f scaledPoint = point * frequency;
|
||||||
|
maxV += permute(scaledPoint) * amplitude;
|
||||||
|
amplitude *= persistence;
|
||||||
|
frequency *= lacunarity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate turbulence noise (absolute value of octaves)
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @return Turbulence noise value
|
||||||
|
/// @note Creates swirling, turbulent patterns; changes affect visual complexity
|
||||||
|
float turbulence(const Vector2f& point, int octaves) {
|
||||||
|
float value = 0.0f;
|
||||||
|
Vector2f tempPoint = point;
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
value += std::abs(permute(tempPoint)) / (1 << i);
|
||||||
|
tempPoint *= 2.f;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 3D turbulence noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @return Turbulence noise value
|
||||||
|
float turbulence(const Vector3f& point, int octaves) {
|
||||||
|
float value = 0.0f;
|
||||||
|
Vector3f tempPoint = point;
|
||||||
|
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
value += std::abs(permute(tempPoint)) / (1 << i);
|
||||||
|
tempPoint *= 2.f;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate ridged (ridged multifractal) noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @param offset Weighting offset for ridge formation
|
||||||
|
/// @return Ridged noise value
|
||||||
|
/// @note Creates sharp ridge-like patterns; offset controls ridge prominence
|
||||||
|
float ridgedNoise(const Vector2f& point, int octaves, float offset = 1.0f) {
|
||||||
|
float result = 0.f;
|
||||||
|
float weight = 1.f;
|
||||||
|
Vector2f p = point;
|
||||||
|
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
float signal = 1.f - std::abs(permute(p));
|
||||||
|
signal *= signal;
|
||||||
|
signal *= weight;
|
||||||
|
weight = signal * offset;
|
||||||
|
result += signal;
|
||||||
|
p *= 2.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 3D ridged noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @param offset Weighting offset for ridge formation
|
||||||
|
/// @return Ridged noise value
|
||||||
|
float ridgedNoise(const Vector3f& point, int octaves, float offset = 1.0f) {
|
||||||
|
float result = 0.f;
|
||||||
|
float weight = 1.f;
|
||||||
|
Vector3f p = point;
|
||||||
|
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
float signal = 1.f - std::abs(permute(p));
|
||||||
|
signal *= signal;
|
||||||
|
signal *= weight;
|
||||||
|
weight = signal * offset;
|
||||||
|
result += signal;
|
||||||
|
p *= 2.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate billow (cloud-like) noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @return Billow noise value
|
||||||
|
/// @note Creates soft, billowy patterns like clouds
|
||||||
|
float billowNoise(const Vector2f& point, int octaves) {
|
||||||
|
float value = 0.0f;
|
||||||
|
float amplitude = 1.0f;
|
||||||
|
float frequency = 1.0f;
|
||||||
|
Vector2f scaledPoint = point * frequency;
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
value += std::abs(permute(scaledPoint)) * amplitude;
|
||||||
|
amplitude *= 0.5f;
|
||||||
|
frequency *= 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generate 3D billow noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param octaves Number of noise layers
|
||||||
|
/// @return Billow noise value
|
||||||
|
float billowNoise(const Vector3f& point, int octaves) {
|
||||||
|
float value = 0.0f;
|
||||||
|
float amplitude = 1.0f;
|
||||||
|
float frequency = 1.0f;
|
||||||
|
Vector3f scaledPoint = point * frequency;
|
||||||
|
for (int i = 0; i < octaves; i++) {
|
||||||
|
value += std::abs(permute(scaledPoint)) * amplitude;
|
||||||
|
amplitude *= 0.5f;
|
||||||
|
frequency *= 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pure White Noise
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Random value [-1, 1] based solely on integer coordinate hashing
|
||||||
|
float whiteNoise(const Vector2f& point) {
|
||||||
|
return hash((int)floor(point.x()), (int)floor(point.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pure White Noise 3D
|
||||||
|
float whiteNoise(const Vector3f& point) {
|
||||||
|
return hash((int)floor(point.x()), (int)floor(point.y()), (int)floor(point.z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Worley (Cellular) Noise 2D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Distance to the nearest feature point [0, 1+]
|
||||||
|
/// @note Used for stone, water caustics, biological cells
|
||||||
|
float worleyNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float minDist = 1.0f;
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
|
||||||
|
float dist = diff.norm();
|
||||||
|
if (dist < minDist) minDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Worley Noise 3D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Distance to nearest feature point
|
||||||
|
float worleyNoise(const Vector3f& point) {
|
||||||
|
Vector3f p = Vector3f(floor(point.x()), floor(point.y()), floor(point.z()));
|
||||||
|
Vector3f f = Vector3f(point.x() - p.x(), point.y() - p.y(), point.z() - p.z());
|
||||||
|
|
||||||
|
float minDist = 1.0f;
|
||||||
|
|
||||||
|
for (int z = -1; z <= 1; z++) {
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector3f neighbor(x, y, z);
|
||||||
|
Vector3f pointInCell = hashVector(Vector3f(p + neighbor));
|
||||||
|
Vector3f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.norm();
|
||||||
|
if (dist < minDist) minDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Voronoi Noise 2D (Cell ID)
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Random hash value [-1, 1] unique to the closest cell
|
||||||
|
float voronoiNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float minDist = 100.0f;
|
||||||
|
Vector2f cellID = p;
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.squaredNorm();
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
cellID = p + neighbor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash((int)cellID.x(), (int)cellID.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief "Crystals" Noise (Variant of Worley)
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return F2 - F1 (Distance to 2nd closest - Distance to closest)
|
||||||
|
/// @note Creates cell-like borders, cracks, or crystal facets
|
||||||
|
float crystalNoise(const Vector2f& point) {
|
||||||
|
Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
|
||||||
|
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
|
||||||
|
|
||||||
|
float d1 = 10.0f;
|
||||||
|
float d2 = 10.0f;
|
||||||
|
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
Vector2f neighbor(x, y);
|
||||||
|
Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
|
||||||
|
Vector2f diff = neighbor + pointInCell - f;
|
||||||
|
float dist = diff.norm();
|
||||||
|
|
||||||
|
if (dist < d1) {
|
||||||
|
d2 = d1;
|
||||||
|
d1 = dist;
|
||||||
|
} else if (dist < d2) {
|
||||||
|
d2 = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d2 - d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Domain Warping
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @param strength Magnitude of the warp
|
||||||
|
/// @return Warped Perlin noise value
|
||||||
|
/// @note Calculates noise(p + noise(p)) for marble/fluid effects
|
||||||
|
float domainWarp(const Vector2f& point, float strength = 1.0f) {
|
||||||
|
Vector2f q(
|
||||||
|
permute(point),
|
||||||
|
permute(Vector2f(point + Vector2f(5.2f, 1.3f)))
|
||||||
|
);
|
||||||
|
return permute(Vector2f(point + q * strength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief 3D Domain Warping
|
||||||
|
float domainWarp(const Vector3f& point, float strength = 1.0f) {
|
||||||
|
Vector3f q(
|
||||||
|
permute(point),
|
||||||
|
permute(Vector3f(point + Vector3f(5.2f, 1.3f, 2.8f))),
|
||||||
|
permute(Vector3f(point + Vector3f(1.1f, 8.4f, 5.5f)))
|
||||||
|
);
|
||||||
|
return permute(Vector3f(point + q * strength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Curl Noise 2D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Divergence-free vector field (useful for particle simulation)
|
||||||
|
/// @note Calculated via finite difference curl of a potential field
|
||||||
|
Vector2f curlNoise(const Vector2f& point) {
|
||||||
|
float n1 = permute(Vector2f(point + Vector2f(0, EPSILON)));
|
||||||
|
float n2 = permute(Vector2f(point + Vector2f(0, -EPSILON)));
|
||||||
|
float n3 = permute(Vector2f(point + Vector2f(EPSILON, 0)));
|
||||||
|
float n4 = permute(Vector2f(point + Vector2f(-EPSILON, 0)));
|
||||||
|
|
||||||
|
float dx = (n3 - n4) / (2.0f * EPSILON);
|
||||||
|
float dy = (n1 - n2) / (2.0f * EPSILON);
|
||||||
|
return Vector2f(dy, -dx).normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Curl Noise 3D
|
||||||
|
/// @param point Input coordinate
|
||||||
|
/// @return Divergence-free vector field
|
||||||
|
/// @note Uses 3 offsets of Perlin noise as Vector Potential
|
||||||
|
Vector3f curlNoise(const Vector3f& point) {
|
||||||
|
Vector3f dx(EPSILON, 0.0f, 0.0f);
|
||||||
|
Vector3f dy(0.0f, EPSILON, 0.0f);
|
||||||
|
Vector3f dz(0.0f, 0.0f, EPSILON);
|
||||||
|
|
||||||
|
auto potential = [&](const Vector3f& p) -> Vector3f {
|
||||||
|
return Vector3f(
|
||||||
|
permute(p),
|
||||||
|
permute(Vector3f(p + Vector3f(123.4f, 129.1f, 827.0f))),
|
||||||
|
permute(Vector3f(p + Vector3f(492.5f, 991.2f, 351.4f)))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector3f p_dx_p = potential(point + dx);
|
||||||
|
Vector3f p_dx_m = potential(point - dx);
|
||||||
|
Vector3f p_dy_p = potential(point + dy);
|
||||||
|
Vector3f p_dy_m = potential(point - dy);
|
||||||
|
Vector3f p_dz_p = potential(point + dz);
|
||||||
|
Vector3f p_dz_m = potential(point - dz);
|
||||||
|
|
||||||
|
// Finite difference
|
||||||
|
float dFz_dy = (p_dy_p.z() - p_dy_m.z()) / (2.0f * EPSILON);
|
||||||
|
float dFy_dz = (p_dz_p.y() - p_dz_m.y()) / (2.0f * EPSILON);
|
||||||
|
float dFx_dz = (p_dz_p.x() - p_dz_m.x()) / (2.0f * EPSILON);
|
||||||
|
float dFz_dx = (p_dx_p.z() - p_dx_m.z()) / (2.0f * EPSILON);
|
||||||
|
float dFy_dx = (p_dx_p.y() - p_dx_m.y()) / (2.0f * EPSILON);
|
||||||
|
float dFx_dy = (p_dy_p.x() - p_dy_m.x()) / (2.0f * EPSILON);
|
||||||
|
|
||||||
|
return Vector3f(
|
||||||
|
dFz_dy - dFy_dz,
|
||||||
|
dFx_dz - dFz_dx,
|
||||||
|
dFy_dx - dFx_dy
|
||||||
|
).normalized();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
693
util/noise2.hpp
693
util/noise2.hpp
@@ -1,693 +0,0 @@
|
|||||||
#ifndef NOISE2_HPP
|
|
||||||
#define NOISE2_HPP
|
|
||||||
|
|
||||||
#include "grid2.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
#include <random>
|
|
||||||
#include <functional>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
struct Grad { float x, y; };
|
|
||||||
|
|
||||||
class Noise2 {
|
|
||||||
public:
|
|
||||||
enum NoiseType {
|
|
||||||
PERLIN,
|
|
||||||
SIMPLEX,
|
|
||||||
VALUE,
|
|
||||||
WORLEY,
|
|
||||||
GABOR,
|
|
||||||
POISSON_DISK,
|
|
||||||
FRACTAL,
|
|
||||||
WAVELET,
|
|
||||||
GAUSSIAN,
|
|
||||||
CELLULAR
|
|
||||||
};
|
|
||||||
|
|
||||||
enum GradientType {
|
|
||||||
HASH_BASED,
|
|
||||||
SIN_BASED,
|
|
||||||
DOT_BASED,
|
|
||||||
PRECOMPUTED
|
|
||||||
};
|
|
||||||
|
|
||||||
Noise2(uint32_t seed = 0, NoiseType type = PERLIN, GradientType gradType = PRECOMPUTED) :
|
|
||||||
rng(seed), dist(0.0f, 1.0f), currentType(type), gradType(gradType),
|
|
||||||
currentSeed(seed), gaborFrequency(4.0f), gaborBandwidth(0.5f)
|
|
||||||
{
|
|
||||||
initializePermutationTable(seed);
|
|
||||||
initializeFeaturePoints(64, seed); // Default 64 feature points
|
|
||||||
initializeWaveletCoefficients(32, seed); // 32x32 wavelet coefficients
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set random seed and reinitialize dependent structures
|
|
||||||
void setSeed(uint32_t seed) {
|
|
||||||
currentSeed = seed;
|
|
||||||
rng.seed(seed);
|
|
||||||
initializePermutationTable(seed);
|
|
||||||
initializeFeaturePoints(featurePoints.size(), seed);
|
|
||||||
initializeWaveletCoefficients(static_cast<int>(std::sqrt(waveletCoefficients.size())), seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set noise type
|
|
||||||
void setNoiseType(NoiseType type) {
|
|
||||||
currentType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set gradient type
|
|
||||||
void setGradientType(GradientType type) {
|
|
||||||
gradType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main noise function that routes to the selected algorithm
|
|
||||||
float noise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
|
|
||||||
switch (currentType) {
|
|
||||||
case PERLIN:
|
|
||||||
return perlinNoise(x, y, octaves, persistence, lacunarity);
|
|
||||||
case SIMPLEX:
|
|
||||||
return simplexNoise(x, y, octaves, persistence, lacunarity);
|
|
||||||
case VALUE:
|
|
||||||
return valueNoise(x, y, octaves, persistence, lacunarity);
|
|
||||||
case WORLEY:
|
|
||||||
return worleyNoise(x, y);
|
|
||||||
case GABOR:
|
|
||||||
return gaborNoise(x, y);
|
|
||||||
case POISSON_DISK:
|
|
||||||
return poissonDiskNoise(x, y);
|
|
||||||
case FRACTAL:
|
|
||||||
return fractalNoise(x, y, octaves, persistence, lacunarity);
|
|
||||||
case WAVELET:
|
|
||||||
return waveletNoise(x, y);
|
|
||||||
case GAUSSIAN:
|
|
||||||
return gaussianNoise(x, y);
|
|
||||||
case CELLULAR:
|
|
||||||
return cellularNoise(x, y);
|
|
||||||
default:
|
|
||||||
return perlinNoise(x, y, octaves, persistence, lacunarity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate simple value noise
|
|
||||||
float valueNoise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
|
|
||||||
float total = 0.0f;
|
|
||||||
float frequency = 1.0f;
|
|
||||||
float amplitude = 1.0f;
|
|
||||||
float maxValue = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < octaves; i++) {
|
|
||||||
total += rawNoise(x * frequency, y * frequency) * amplitude;
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= persistence;
|
|
||||||
frequency *= lacunarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return total / maxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate Perlin-like noise
|
|
||||||
float perlinNoise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
|
|
||||||
float total = 0.0f;
|
|
||||||
float frequency = 1.0f;
|
|
||||||
float amplitude = 1.0f;
|
|
||||||
float maxValue = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < octaves; i++) {
|
|
||||||
total += improvedNoise(x * frequency, y * frequency) * amplitude;
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= persistence;
|
|
||||||
frequency *= lacunarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (total / maxValue + 1.0f) * 0.5f; // Normalize to [0,1]
|
|
||||||
}
|
|
||||||
|
|
||||||
float simplexNoise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
|
|
||||||
float total = 0.0f;
|
|
||||||
float frequency = 1.0f;
|
|
||||||
float amplitude = 1.0f;
|
|
||||||
float maxValue = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < octaves; i++) {
|
|
||||||
total += rawSimplexNoise(x * frequency, y * frequency) * amplitude;
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= persistence;
|
|
||||||
frequency *= lacunarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (total / maxValue + 1.0f) * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Worley (cellular) noise
|
|
||||||
float worleyNoise(float x, float y) {
|
|
||||||
if (featurePoints.empty()) return 0.0f;
|
|
||||||
|
|
||||||
// Find the closest and second closest feature points
|
|
||||||
float minDist1 = std::numeric_limits<float>::max();
|
|
||||||
float minDist2 = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
for (const auto& point : featurePoints) {
|
|
||||||
float dx = x - point.x;
|
|
||||||
float dy = y - point.y;
|
|
||||||
float dist = dx * dx + dy * dy; // Squared distance for performance
|
|
||||||
|
|
||||||
if (dist < minDist1) {
|
|
||||||
minDist2 = minDist1;
|
|
||||||
minDist1 = dist;
|
|
||||||
} else if (dist < minDist2) {
|
|
||||||
minDist2 = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return distance to closest feature point (normalized)
|
|
||||||
return std::sqrt(minDist1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cellular noise variation
|
|
||||||
float cellularNoise(float x, float y) {
|
|
||||||
if (featurePoints.empty()) return 0.0f;
|
|
||||||
|
|
||||||
float minDist1 = std::numeric_limits<float>::max();
|
|
||||||
float minDist2 = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
for (const auto& point : featurePoints) {
|
|
||||||
float dx = x - point.x;
|
|
||||||
float dy = y - point.y;
|
|
||||||
float dist = dx * dx + dy * dy;
|
|
||||||
|
|
||||||
if (dist < minDist1) {
|
|
||||||
minDist2 = minDist1;
|
|
||||||
minDist1 = dist;
|
|
||||||
} else if (dist < minDist2) {
|
|
||||||
minDist2 = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cellular pattern: second closest minus closest
|
|
||||||
return std::sqrt(minDist2) - std::sqrt(minDist1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gabor noise
|
|
||||||
float gaborNoise(float x, float y) {
|
|
||||||
// Simplified Gabor noise - in practice this would be more complex
|
|
||||||
float gaussian = std::exp(-(x*x + y*y) / (2.0f * gaborBandwidth * gaborBandwidth));
|
|
||||||
float cosine = std::cos(2.0f * M_PI * gaborFrequency * (x + y));
|
|
||||||
|
|
||||||
return gaussian * cosine;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poisson disk noise
|
|
||||||
float poissonDiskNoise(float x, float y) {
|
|
||||||
// Sample Poisson disk distribution
|
|
||||||
// This is a simplified version - full implementation would use more sophisticated sampling
|
|
||||||
float minDist = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
for (const auto& point : featurePoints) {
|
|
||||||
float dx = x - point.x;
|
|
||||||
float dy = y - point.y;
|
|
||||||
float dist = std::sqrt(dx * dx + dy * dy);
|
|
||||||
minDist = std::min(minDist, dist);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f - std::min(minDist * 10.0f, 1.0f); // Invert and scale
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fractal noise (fractional Brownian motion)
|
|
||||||
float fractalNoise(float x, float y, int octaves = 8, float persistence = 0.5f, float lacunarity = 2.0f) {
|
|
||||||
float total = 0.0f;
|
|
||||||
float frequency = 1.0f;
|
|
||||||
float amplitude = 1.0f;
|
|
||||||
float maxValue = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < octaves; i++) {
|
|
||||||
total += improvedNoise(x * frequency, y * frequency) * amplitude;
|
|
||||||
maxValue += amplitude;
|
|
||||||
amplitude *= persistence;
|
|
||||||
frequency *= lacunarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fractal noise often has wider range, so we don't normalize as strictly
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wavelet noise
|
|
||||||
float waveletNoise(float x, float y) {
|
|
||||||
// Simplified wavelet noise using precomputed coefficients
|
|
||||||
int ix = static_cast<int>(std::floor(x * 4)) % 32;
|
|
||||||
int iy = static_cast<int>(std::floor(y * 4)) % 32;
|
|
||||||
|
|
||||||
if (ix < 0) ix += 32;
|
|
||||||
if (iy < 0) iy += 32;
|
|
||||||
|
|
||||||
return waveletCoefficients[iy * 32 + ix];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gaussian noise
|
|
||||||
float gaussianNoise(float x, float y) {
|
|
||||||
// Use coordinates to seed RNG for deterministic results
|
|
||||||
rng.seed(static_cast<uint32_t>(x * 1000 + y * 1000 + currentSeed));
|
|
||||||
|
|
||||||
// Box-Muller transform for Gaussian distribution
|
|
||||||
float u1 = dist(rng);
|
|
||||||
float u2 = dist(rng);
|
|
||||||
float z0 = std::sqrt(-2.0f * std::log(u1)) * std::cos(2.0f * M_PI * u2);
|
|
||||||
|
|
||||||
// Normalize to [0,1] range
|
|
||||||
return (z0 + 3.0f) / 6.0f; // Assuming 3 sigma covers most of the distribution
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a grayscale noise grid using current noise type
|
|
||||||
Grid2 generateGrayNoise(int width, int height,
|
|
||||||
float scale = 1.0f,
|
|
||||||
int octaves = 1,
|
|
||||||
float persistence = 0.5f,
|
|
||||||
uint32_t seed = 0,
|
|
||||||
const Vec2& offset = Vec2(0, 0)) {
|
|
||||||
if (seed != 0) setSeed(seed);
|
|
||||||
|
|
||||||
Grid2 grid(width * height);
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
float nx = (x + offset.x) / width * scale;
|
|
||||||
float ny = (y + offset.y) / height * scale;
|
|
||||||
|
|
||||||
float noiseValue = noise(nx, ny, octaves, persistence);
|
|
||||||
|
|
||||||
// Convert to position and grayscale color
|
|
||||||
Vec2 position(x, y);
|
|
||||||
Vec4 color(noiseValue, noiseValue, noiseValue, 1.0f);
|
|
||||||
|
|
||||||
grid.positions[y * width + x] = position;
|
|
||||||
grid.colors[y * width + x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
float pascalTri(const float& a, const float& b) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
int result = 1;
|
|
||||||
for (int i = 0; i < b; ++i){
|
|
||||||
result *= (a - 1) / (i + 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float genSmooth(int N, float x) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
x = clamp(x, 0, 1);
|
|
||||||
float result = 0;
|
|
||||||
for (int n = 0; n <= N; ++n){
|
|
||||||
result += pascalTri(-N - 1, n) * pascalTri(2 * N + 1, N-1) * pow(x, N + n + 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float inverse_smoothstep(float x) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
return 0.5 - sin(asin(1.0 - 2.0 * x) / 3.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate multi-layered RGBA noise
|
|
||||||
Grid2 generateRGBANoise(int width, int height,
|
|
||||||
const Vec4& scale = Vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
|
||||||
const Vec4& octaves = Vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
|
||||||
const Vec4& persistence = Vec4(0.5f, 0.5f, 0.5f, 0.5f),
|
|
||||||
uint32_t seed = 0,
|
|
||||||
const Vec2& offset = Vec2(0, 0)) {
|
|
||||||
if (seed != 0) setSeed(seed);
|
|
||||||
|
|
||||||
Grid2 grid(width * height);
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
float nx = (x + offset.x) / width;
|
|
||||||
float ny = (y + offset.y) / height;
|
|
||||||
|
|
||||||
// Generate separate noise for each channel using current noise type
|
|
||||||
float r = noise(nx * scale.x, ny * scale.x,
|
|
||||||
static_cast<int>(octaves.x), persistence.x);
|
|
||||||
float g = noise(nx * scale.y, ny * scale.y,
|
|
||||||
static_cast<int>(octaves.y), persistence.y);
|
|
||||||
float b = noise(nx * scale.z, ny * scale.z,
|
|
||||||
static_cast<int>(octaves.z), persistence.z);
|
|
||||||
float a = noise(nx * scale.w, ny * scale.w,
|
|
||||||
static_cast<int>(octaves.w), persistence.w);
|
|
||||||
|
|
||||||
Vec2 position(x, y);
|
|
||||||
Vec4 color(r, g, b, a);
|
|
||||||
|
|
||||||
grid.positions[y * width + x] = position;
|
|
||||||
grid.colors[y * width + x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate terrain-like noise (useful for heightmaps)
|
|
||||||
Grid2 generateTerrainNoise(int width, int height, float scale = 1.0f, int octaves = 4,
|
|
||||||
float persistence = 0.5f, uint32_t seed = 0, const Vec2& offset = Vec2(0, 0)) {
|
|
||||||
if (seed != 0) setSeed(seed);
|
|
||||||
|
|
||||||
Grid2 grid(width * height);
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
float nx = (x + offset.x) / width * scale;
|
|
||||||
float ny = (y + offset.y) / height * scale;
|
|
||||||
|
|
||||||
// Use multiple octaves for more natural terrain
|
|
||||||
float heightValue = noise(nx, ny, octaves, persistence);
|
|
||||||
|
|
||||||
// Apply some curve to make it more terrain-like
|
|
||||||
heightValue = std::pow(heightValue, 1.5f);
|
|
||||||
|
|
||||||
Vec2 position(x, y);
|
|
||||||
Vec4 color(heightValue, heightValue, heightValue, 1.0f);
|
|
||||||
|
|
||||||
grid.positions[y * width + x] = position;
|
|
||||||
grid.colors[y * width + x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate cloud-like noise
|
|
||||||
Grid2 generateCloudNoise(int width, int height,
|
|
||||||
float scale = 2.0f,
|
|
||||||
int octaves = 3,
|
|
||||||
float persistence = 0.7f,
|
|
||||||
uint32_t seed = 0,
|
|
||||||
const Vec2& offset = Vec2(0, 0)) {
|
|
||||||
auto grid = generateGrayNoise(width, height, scale, octaves, persistence, seed, offset);
|
|
||||||
|
|
||||||
// Apply soft threshold for cloud effect
|
|
||||||
for (auto& color : grid.colors) {
|
|
||||||
float value = color.x; // Assuming grayscale in red channel
|
|
||||||
// Soft threshold: values below 0.3 become 0, above 0.7 become 1, smooth in between
|
|
||||||
if (value < 0.3f) value = 0.0f;
|
|
||||||
else if (value > 0.7f) value = 1.0f;
|
|
||||||
else value = (value - 0.3f) / 0.4f; // Linear interpolation
|
|
||||||
|
|
||||||
color = Vec4(value, value, value, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate specific noise type directly
|
|
||||||
Grid2 generateSpecificNoise(NoiseType type, int width, int height,
|
|
||||||
float scale = 1.0f, int octaves = 1,
|
|
||||||
float persistence = 0.5f, uint32_t seed = 0) {
|
|
||||||
NoiseType oldType = currentType;
|
|
||||||
currentType = type;
|
|
||||||
|
|
||||||
auto grid = generateGrayNoise(width, height, scale, octaves, persistence, seed);
|
|
||||||
|
|
||||||
currentType = oldType;
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mt19937 rng;
|
|
||||||
std::uniform_real_distribution<float> dist;
|
|
||||||
|
|
||||||
// Precomputed gradient directions for 8 directions
|
|
||||||
static constexpr std::array<Grad, 8> grads = {
|
|
||||||
Grad{1.0f, 0.0f},
|
|
||||||
Grad{0.707f, 0.707f},
|
|
||||||
Grad{0.0f, 1.0f},
|
|
||||||
Grad{-0.707f, 0.707f},
|
|
||||||
Grad{-1.0f, 0.0f},
|
|
||||||
Grad{-0.707f, -0.707f},
|
|
||||||
Grad{0.0f, -1.0f},
|
|
||||||
Grad{0.707f, -0.707f}
|
|
||||||
};
|
|
||||||
|
|
||||||
NoiseType currentType;
|
|
||||||
GradientType gradType;
|
|
||||||
uint32_t currentSeed;
|
|
||||||
|
|
||||||
// Permutation table for Simplex noise
|
|
||||||
std::array<int, 512> perm;
|
|
||||||
|
|
||||||
// For Worley noise
|
|
||||||
std::vector<Vec2> featurePoints;
|
|
||||||
|
|
||||||
// For Gabor noise
|
|
||||||
float gaborFrequency;
|
|
||||||
float gaborBandwidth;
|
|
||||||
|
|
||||||
// For wavelet noise
|
|
||||||
std::vector<float> waveletCoefficients;
|
|
||||||
|
|
||||||
// Initialize permutation table for Simplex noise
|
|
||||||
void initializePermutationTable(uint32_t seed) {
|
|
||||||
std::mt19937 localRng(seed);
|
|
||||||
std::uniform_int_distribution<int> intDist(0, 255);
|
|
||||||
|
|
||||||
// Create initial permutation
|
|
||||||
std::array<int, 256> p;
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
p[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle using Fisher-Yates
|
|
||||||
for (int i = 255; i > 0; i--) {
|
|
||||||
int j = intDist(localRng) % (i + 1);
|
|
||||||
std::swap(p[i], p[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicate for overflow
|
|
||||||
for (int i = 0; i < 512; i++) {
|
|
||||||
perm[i] = p[i & 255];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize feature points for Worley/Poisson noise
|
|
||||||
void initializeFeaturePoints(int numPoints, uint32_t seed) {
|
|
||||||
std::mt19937 localRng(seed);
|
|
||||||
std::uniform_real_distribution<float> localDist(0.0f, 1.0f);
|
|
||||||
|
|
||||||
featurePoints.clear();
|
|
||||||
featurePoints.reserve(numPoints);
|
|
||||||
|
|
||||||
for (int i = 0; i < numPoints; i++) {
|
|
||||||
featurePoints.emplace_back(localDist(localRng), localDist(localRng));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize wavelet coefficients
|
|
||||||
void initializeWaveletCoefficients(int size, uint32_t seed) {
|
|
||||||
std::mt19937 localRng(seed);
|
|
||||||
std::uniform_real_distribution<float> localDist(-1.0f, 1.0f);
|
|
||||||
|
|
||||||
waveletCoefficients.resize(size * size);
|
|
||||||
for (int i = 0; i < size * size; i++) {
|
|
||||||
waveletCoefficients[i] = (localDist(localRng) + 1.0f) * 0.5f; // Normalize to [0,1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw Simplex noise implementation
|
|
||||||
float rawSimplexNoise(float x, float y) {
|
|
||||||
// Skewing factors for 2D
|
|
||||||
const float F2 = 0.5f * (std::sqrt(3.0f) - 1.0f);
|
|
||||||
const float G2 = (3.0f - std::sqrt(3.0f)) / 6.0f;
|
|
||||||
|
|
||||||
// Skew the input space
|
|
||||||
float s = (x + y) * F2;
|
|
||||||
int i = fastFloor(x + s);
|
|
||||||
int j = fastFloor(y + s);
|
|
||||||
|
|
||||||
float t = (i + j) * G2;
|
|
||||||
float X0 = i - t;
|
|
||||||
float Y0 = j - t;
|
|
||||||
float x0 = x - X0;
|
|
||||||
float y0 = y - Y0;
|
|
||||||
|
|
||||||
// Determine which simplex we're in
|
|
||||||
int i1, j1;
|
|
||||||
if (x0 > y0) {
|
|
||||||
i1 = 1; j1 = 0;
|
|
||||||
} else {
|
|
||||||
i1 = 0; j1 = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate other corners
|
|
||||||
float x1 = x0 - i1 + G2;
|
|
||||||
float y1 = y0 - j1 + G2;
|
|
||||||
float x2 = x0 - 1.0f + 2.0f * G2;
|
|
||||||
float y2 = y0 - 1.0f + 2.0f * G2;
|
|
||||||
|
|
||||||
// Calculate contributions from each corner
|
|
||||||
float n0, n1, n2;
|
|
||||||
float t0 = 0.5f - x0*x0 - y0*y0;
|
|
||||||
if (t0 < 0) n0 = 0.0f;
|
|
||||||
else {
|
|
||||||
t0 *= t0;
|
|
||||||
n0 = t0 * t0 * grad(perm[i + perm[j]], x0, y0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float t1 = 0.5f - x1*x1 - y1*y1;
|
|
||||||
if (t1 < 0) n1 = 0.0f;
|
|
||||||
else {
|
|
||||||
t1 *= t1;
|
|
||||||
n1 = t1 * t1 * grad(perm[i + i1 + perm[j + j1]], x1, y1);
|
|
||||||
}
|
|
||||||
|
|
||||||
float t2 = 0.5f - x2*x2 - y2*y2;
|
|
||||||
if (t2 < 0) n2 = 0.0f;
|
|
||||||
else {
|
|
||||||
t2 *= t2;
|
|
||||||
n2 = t2 * t2 * grad(perm[i + 1 + perm[j + 1]], x2, y2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 70.0f * (n0 + n1 + n2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast floor function
|
|
||||||
int fastFloor(float x) {
|
|
||||||
int xi = static_cast<int>(x);
|
|
||||||
return x < xi ? xi - 1 : xi;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gradient function for Simplex noise
|
|
||||||
float grad(int hash, float x, float y) {
|
|
||||||
int h = hash & 7;
|
|
||||||
float u = h < 4 ? x : y;
|
|
||||||
float v = h < 4 ? y : x;
|
|
||||||
return ((h & 1) ? -u : u) + ((h & 2) ? -2.0f * v : 2.0f * v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw noise function (simple hash-based)
|
|
||||||
float rawNoise(float x, float y) {
|
|
||||||
// Simple hash function for deterministic noise
|
|
||||||
int xi = static_cast<int>(std::floor(x));
|
|
||||||
int yi = static_cast<int>(std::floor(y));
|
|
||||||
|
|
||||||
// Use the RNG to generate consistent noise based on grid position
|
|
||||||
rng.seed(xi * 1619 + yi * 31337 + currentSeed);
|
|
||||||
return dist(rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Improved noise function (Perlin-like) using selected gradient type
|
|
||||||
float improvedNoise(float x, float y) {
|
|
||||||
// Integer part
|
|
||||||
int xi = static_cast<int>(std::floor(x));
|
|
||||||
int yi = static_cast<int>(std::floor(y));
|
|
||||||
|
|
||||||
// Fractional part
|
|
||||||
float xf = x - xi;
|
|
||||||
float yf = y - yi;
|
|
||||||
|
|
||||||
// Smooth interpolation
|
|
||||||
float u = fade(xf);
|
|
||||||
float v = fade(yf);
|
|
||||||
|
|
||||||
// Gradient noise from corners using selected gradient calculation
|
|
||||||
float n00 = gradNoise(xi, yi, xf, yf);
|
|
||||||
float n01 = gradNoise(xi, yi + 1, xf, yf - 1);
|
|
||||||
float n10 = gradNoise(xi + 1, yi, xf - 1, yf);
|
|
||||||
float n11 = gradNoise(xi + 1, yi + 1, xf - 1, yf - 1);
|
|
||||||
|
|
||||||
// Bilinear interpolation
|
|
||||||
float x1 = lerp(n00, n10, u);
|
|
||||||
float x2 = lerp(n01, n11, u);
|
|
||||||
return lerp(x1, x2, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gradient noise function using selected gradient type
|
|
||||||
float gradNoise(int xi, int yi, float xf, float yf) {
|
|
||||||
switch (gradType) {
|
|
||||||
case HASH_BASED:
|
|
||||||
return hashGradNoise(xi, yi, xf, yf);
|
|
||||||
case SIN_BASED:
|
|
||||||
return sinGradNoise(xi, yi, xf, yf);
|
|
||||||
case DOT_BASED:
|
|
||||||
return dotGradNoise(xi, yi, xf, yf);
|
|
||||||
case PRECOMPUTED:
|
|
||||||
default:
|
|
||||||
return precomputedGradNoise(xi, yi, xf, yf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast gradient noise function using precomputed gradient directions
|
|
||||||
float precomputedGradNoise(int xi, int yi, float xf, float yf) {
|
|
||||||
// Generate deterministic hash from integer coordinates
|
|
||||||
int hash = (xi * 1619 + yi * 31337 + currentSeed);
|
|
||||||
|
|
||||||
// Use hash to select from 8 precomputed gradient directions
|
|
||||||
int gradIndex = hash & 7; // 8 directions (0-7)
|
|
||||||
|
|
||||||
// Dot product between distance vector and gradient
|
|
||||||
return xf * grads[gradIndex].x + yf * grads[gradIndex].y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash-based gradient noise
|
|
||||||
float hashGradNoise(int xi, int yi, float xf, float yf) {
|
|
||||||
// Generate hash from coordinates
|
|
||||||
uint32_t hash = (xi * 1619 + yi * 31337 + currentSeed);
|
|
||||||
|
|
||||||
// Use hash to generate gradient angle
|
|
||||||
hash = (hash << 13) ^ hash;
|
|
||||||
hash = (hash * (hash * hash * 15731 + 789221) + 1376312589);
|
|
||||||
float angle = (hash & 0xFFFF) / 65535.0f * 2.0f * M_PI;
|
|
||||||
|
|
||||||
// Gradient vector
|
|
||||||
float gx = std::cos(angle);
|
|
||||||
float gy = std::sin(angle);
|
|
||||||
|
|
||||||
// Dot product
|
|
||||||
return xf * gx + yf * gy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sine-based gradient noise
|
|
||||||
float sinGradNoise(int xi, int yi, float xf, float yf) {
|
|
||||||
// Use sine of coordinates to generate gradient
|
|
||||||
float angle = std::sin(xi * 12.9898f + yi * 78.233f + currentSeed) * 43758.5453f;
|
|
||||||
angle = angle - std::floor(angle); // Fractional part
|
|
||||||
angle *= 2.0f * M_PI;
|
|
||||||
|
|
||||||
float gx = std::cos(angle);
|
|
||||||
float gy = std::sin(angle);
|
|
||||||
|
|
||||||
return xf * gx + yf * gy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dot product based gradient noise
|
|
||||||
float dotGradNoise(int xi, int yi, float xf, float yf) {
|
|
||||||
// Simple dot product with random vector based on coordinates
|
|
||||||
float random = std::sin(xi * 127.1f + yi * 311.7f) * 43758.5453123f;
|
|
||||||
random = random - std::floor(random);
|
|
||||||
|
|
||||||
Vec2 grad(std::cos(random * 2.0f * M_PI), std::sin(random * 2.0f * M_PI));
|
|
||||||
Vec2 dist(xf, yf);
|
|
||||||
|
|
||||||
return grad.dot(dist);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fade function for smooth interpolation
|
|
||||||
float fade(float t) {
|
|
||||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linear interpolation
|
|
||||||
float lerp(float a, float b, float t) {
|
|
||||||
return a + t * (b - a);
|
|
||||||
}
|
|
||||||
|
|
||||||
float clamp(float x, float lowerlimit = 0.0f, float upperlimit = 1.0f) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
if (x < lowerlimit) return lowerlimit;
|
|
||||||
if (x > upperlimit) return upperlimit;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "../vectorlogic/vec3.hpp"
|
//#include "../vectorlogic/vec3.hpp"
|
||||||
#include "frame.hpp"
|
#include "frame.hpp"
|
||||||
|
|
||||||
class BMPWriter {
|
class BMPWriter {
|
||||||
@@ -51,40 +51,40 @@ private:
|
|||||||
public:
|
public:
|
||||||
// Save a 2D vector of Vec3ui8 (RGB) colors as BMP
|
// Save a 2D vector of Vec3ui8 (RGB) colors as BMP
|
||||||
// Vec3ui8 components: x = red, y = green, z = blue (values in range [0,1])
|
// Vec3ui8 components: x = red, y = green, z = blue (values in range [0,1])
|
||||||
static bool saveBMP(const std::string& filename, const std::vector<std::vector<Vec3ui8>>& pixels) {
|
// static bool saveBMP(const std::string& filename, const std::vector<std::vector<Vec3ui8>>& pixels) {
|
||||||
if (pixels.empty() || pixels[0].empty()) {
|
// if (pixels.empty() || pixels[0].empty()) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
int height = static_cast<int>(pixels.size());
|
// int height = static_cast<int>(pixels.size());
|
||||||
int width = static_cast<int>(pixels[0].size());
|
// int width = static_cast<int>(pixels[0].size());
|
||||||
|
|
||||||
// Validate that all rows have the same width
|
// // Validate that all rows have the same width
|
||||||
for (const auto& row : pixels) {
|
// for (const auto& row : pixels) {
|
||||||
if (row.size() != width) {
|
// if (row.size() != width) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return saveBMP(filename, pixels, width, height);
|
// return saveBMP(filename, pixels, width, height);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Alternative interface with width/height and flat vector (row-major order)
|
// // Alternative interface with width/height and flat vector (row-major order)
|
||||||
static bool saveBMP(const std::string& filename, const std::vector<Vec3ui8>& pixels, int width, int height) {
|
// static bool saveBMP(const std::string& filename, const std::vector<Vec3ui8>& pixels, int width, int height) {
|
||||||
if (pixels.size() != width * height) {
|
// if (pixels.size() != width * height) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Convert to 2D vector format
|
// // Convert to 2D vector format
|
||||||
std::vector<std::vector<Vec3ui8>> pixels2D(height, std::vector<Vec3ui8>(width));
|
// std::vector<std::vector<Vec3ui8>> pixels2D(height, std::vector<Vec3ui8>(width));
|
||||||
for (int y = 0; y < height; ++y) {
|
// for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
// for (int x = 0; x < width; ++x) {
|
||||||
pixels2D[y][x] = pixels[y * width + x];
|
// pixels2D[y][x] = pixels[y * width + x];
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return saveBMP(filename, pixels2D, width, height);
|
// return saveBMP(filename, pixels2D, width, height);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Save from 1D vector of uint8_t pixels (BGR order: pixels[i]=b, pixels[i+1]=g, pixels[i+2]=r)
|
// Save from 1D vector of uint8_t pixels (BGR order: pixels[i]=b, pixels[i+1]=g, pixels[i+2]=r)
|
||||||
static bool saveBMP(const std::string& filename, const std::vector<uint8_t>& pixels, int width, int height) {
|
static bool saveBMP(const std::string& filename, const std::vector<uint8_t>& pixels, int width, int height) {
|
||||||
@@ -133,9 +133,9 @@ public:
|
|||||||
|
|
||||||
// Input is already BGR: pixels[i]=b, pixels[i+1]=g, pixels[i+2]=r
|
// Input is already BGR: pixels[i]=b, pixels[i+1]=g, pixels[i+2]=r
|
||||||
// So we can copy directly
|
// So we can copy directly
|
||||||
row[dstOffset] = srcRow[srcOffset]; // B
|
row[dstOffset + 0] = srcRow[srcOffset + 2]; // B
|
||||||
row[dstOffset + 1] = srcRow[srcOffset + 1]; // G
|
row[dstOffset + 1] = srcRow[srcOffset + 1]; // G
|
||||||
row[dstOffset + 2] = srcRow[srcOffset + 2]; // R
|
row[dstOffset + 2] = srcRow[srcOffset + 0]; // R
|
||||||
}
|
}
|
||||||
file.write(reinterpret_cast<const char*>(row.data()), rowSize);
|
file.write(reinterpret_cast<const char*>(row.data()), rowSize);
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool saveBMP(const std::string& filename, frame& frame) {
|
static bool saveBMP(const std::string& filename, const frame& frame) {
|
||||||
if (frame.colorFormat == frame::colormap::RGB) {
|
if (frame.colorFormat == frame::colormap::RGB) {
|
||||||
return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight());
|
return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight());
|
||||||
} else if (frame.colorFormat == frame::colormap::RGBA) {
|
} else if (frame.colorFormat == frame::colormap::RGBA) {
|
||||||
@@ -157,53 +157,53 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool saveBMP(const std::string& filename, const std::vector<std::vector<Vec3ui8>>& pixels, int width, int height) {
|
// static bool saveBMP(const std::string& filename, const std::vector<std::vector<Vec3ui8>>& pixels, int width, int height) {
|
||||||
// Create directory if needed
|
// // Create directory if needed
|
||||||
if (!createDirectoryIfNeeded(filename)) {
|
// if (!createDirectoryIfNeeded(filename)) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
BMPHeader header;
|
// BMPHeader header;
|
||||||
BMPInfoHeader infoHeader;
|
// BMPInfoHeader infoHeader;
|
||||||
|
|
||||||
int rowSize = (width * 3 + 3) & ~3; // 24-bit, padded to 4 bytes
|
// int rowSize = (width * 3 + 3) & ~3; // 24-bit, padded to 4 bytes
|
||||||
int imageSize = rowSize * height;
|
// int imageSize = rowSize * height;
|
||||||
|
|
||||||
header.fileSize = sizeof(BMPHeader) + sizeof(BMPInfoHeader) + imageSize;
|
// header.fileSize = sizeof(BMPHeader) + sizeof(BMPInfoHeader) + imageSize;
|
||||||
infoHeader.width = width;
|
// infoHeader.width = width;
|
||||||
infoHeader.height = height;
|
// infoHeader.height = height;
|
||||||
infoHeader.imageSize = imageSize;
|
// infoHeader.imageSize = imageSize;
|
||||||
|
|
||||||
std::ofstream file(filename, std::ios::binary);
|
// std::ofstream file(filename, std::ios::binary);
|
||||||
if (!file) {
|
// if (!file) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
file.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
// file.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||||
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
|
// file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
|
||||||
|
|
||||||
// Write pixel data (BMP stores pixels bottom-to-top)
|
// // Write pixel data (BMP stores pixels bottom-to-top)
|
||||||
std::vector<uint8_t> row(rowSize, 0);
|
// std::vector<uint8_t> row(rowSize, 0);
|
||||||
for (int y = height - 1; y >= 0; --y) {
|
// for (int y = height - 1; y >= 0; --y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
// for (int x = 0; x < width; ++x) {
|
||||||
const Vec3ui8& color = pixels[y][x];
|
// const Vec3ui8& color = pixels[y][x];
|
||||||
|
|
||||||
// Convert from [0,1] float to [0,255] uint8_t
|
// // Convert from [0,1] float to [0,255] uint8_t
|
||||||
uint8_t r = static_cast<uint8_t>(std::clamp(color.x * 255.0f, 0.0f, 255.0f));
|
// uint8_t r = static_cast<uint8_t>(std::clamp(color.x * 255.0f, 0.0f, 255.0f));
|
||||||
uint8_t g = static_cast<uint8_t>(std::clamp(color.y * 255.0f, 0.0f, 255.0f));
|
// uint8_t g = static_cast<uint8_t>(std::clamp(color.y * 255.0f, 0.0f, 255.0f));
|
||||||
uint8_t b = static_cast<uint8_t>(std::clamp(color.z * 255.0f, 0.0f, 255.0f));
|
// uint8_t b = static_cast<uint8_t>(std::clamp(color.z * 255.0f, 0.0f, 255.0f));
|
||||||
|
|
||||||
// BMP is BGR order
|
// // BMP is BGR order
|
||||||
int pixelOffset = x * 3;
|
// int pixelOffset = x * 3;
|
||||||
row[pixelOffset] = b;
|
// row[pixelOffset] = b;
|
||||||
row[pixelOffset + 1] = g;
|
// row[pixelOffset + 1] = g;
|
||||||
row[pixelOffset + 2] = r;
|
// row[pixelOffset + 2] = r;
|
||||||
}
|
// }
|
||||||
file.write(reinterpret_cast<const char*>(row.data()), rowSize);
|
// file.write(reinterpret_cast<const char*>(row.data()), rowSize);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
static std::vector<uint8_t> convertRGBAtoRGB(const std::vector<uint8_t>& rgba) {
|
static std::vector<uint8_t> convertRGBAtoRGB(const std::vector<uint8_t>& rgba) {
|
||||||
std::vector<uint8_t> rgb;
|
std::vector<uint8_t> rgb;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
#include "../timing_decorator.hpp"
|
#include "../timing_decorator.hpp"
|
||||||
|
|
||||||
class frame {
|
class frame {
|
||||||
@@ -26,7 +28,6 @@ private:
|
|||||||
size_t sourceSize = 0;
|
size_t sourceSize = 0;
|
||||||
size_t width = 0;
|
size_t width = 0;
|
||||||
size_t height = 0;
|
size_t height = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class colormap {
|
enum class colormap {
|
||||||
RGB,
|
RGB,
|
||||||
@@ -45,43 +46,287 @@ public:
|
|||||||
RAW
|
RAW
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class interpolation {
|
||||||
|
NEAREST,
|
||||||
|
BILINEAR,
|
||||||
|
AREA,
|
||||||
|
LANCZOS4
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
size_t getChannels(colormap fmt) const {
|
||||||
|
switch (fmt) {
|
||||||
|
case colormap::RGBA: return 4;
|
||||||
|
case colormap::BGR: return 3;
|
||||||
|
case colormap::BGRA: return 4;
|
||||||
|
case colormap::B: return 1;
|
||||||
|
case colormap::RGB: default: return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetState(size_t newSize) {
|
||||||
|
cformat = compresstype::RAW;
|
||||||
|
_compressedData.clear();
|
||||||
|
_compressedData.shrink_to_fit();
|
||||||
|
overheadmap.clear();
|
||||||
|
sourceSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rgbToGrayscale(float r, float g, float b) const {
|
||||||
|
return 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||||
|
}
|
||||||
|
public:
|
||||||
colormap colorFormat = colormap::RGB;
|
colormap colorFormat = colormap::RGB;
|
||||||
compresstype cformat = compresstype::RAW;
|
compresstype cformat = compresstype::RAW;
|
||||||
|
|
||||||
const size_t& getWidth() {
|
const size_t& getWidth() const {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t& getHeight() {
|
const size_t& getHeight() const {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
frame() {};
|
frame() {};
|
||||||
frame(size_t w, size_t h, colormap format = colormap::RGB)
|
frame(size_t w, size_t h, colormap format = colormap::RGB)
|
||||||
: width(w), height(h), colorFormat(format), cformat(compresstype::RAW) {
|
: width(w), height(h), colorFormat(format), cformat(compresstype::RAW) {
|
||||||
size_t channels = 3;
|
_data.resize(width * height * getChannels(format));
|
||||||
switch (format) {
|
|
||||||
case colormap::RGBA: channels = 4; break;
|
|
||||||
case colormap::BGR: channels = 3; break;
|
|
||||||
case colormap::BGRA: channels = 4; break;
|
|
||||||
case colormap::B: channels = 1; break;
|
|
||||||
default: channels = 3; break;
|
|
||||||
}
|
|
||||||
_data.resize(width * height * channels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setData(const std::vector<uint8_t>& data) {
|
void setData(const std::vector<uint8_t>& data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
cformat = compresstype::RAW;
|
resetState(data.size());
|
||||||
_compressedData.clear();
|
}
|
||||||
_compressedData.shrink_to_fit();
|
|
||||||
overheadmap.clear();
|
void setData(const std::vector<uint8_t>& inputData, colormap inputFormat) {
|
||||||
sourceSize = data.size();
|
if (inputFormat == colorFormat) {
|
||||||
|
setData(inputData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t srcChannels = getChannels(inputFormat);
|
||||||
|
size_t dstChannels = getChannels(colorFormat);
|
||||||
|
size_t numPixels = width * height;
|
||||||
|
|
||||||
|
if (inputData.size() != numPixels * srcChannels) {
|
||||||
|
throw std::runtime_error("Input data size does not match frame dimensions for the specified format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> newData;
|
||||||
|
newData.reserve(numPixels * dstChannels);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numPixels; ++i) {
|
||||||
|
size_t px = i * srcChannels;
|
||||||
|
uint8_t r = 0, g = 0, b = 0, a = 255;
|
||||||
|
|
||||||
|
switch (inputFormat) {
|
||||||
|
case colormap::RGB: {
|
||||||
|
r = inputData[px];
|
||||||
|
g = inputData[px+1];
|
||||||
|
b = inputData[px+2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case colormap::RGBA:
|
||||||
|
r = inputData[px];
|
||||||
|
g = inputData[px+1];
|
||||||
|
b = inputData[px+2];
|
||||||
|
a = inputData[px+3];
|
||||||
|
break;
|
||||||
|
case colormap::BGR:
|
||||||
|
b = inputData[px];
|
||||||
|
g = inputData[px+1];
|
||||||
|
r = inputData[px+2];
|
||||||
|
break;
|
||||||
|
case colormap::BGRA:
|
||||||
|
b = inputData[px];
|
||||||
|
g = inputData[px+1];
|
||||||
|
r = inputData[px+2];
|
||||||
|
a = inputData[px+3];
|
||||||
|
break;
|
||||||
|
case colormap::B:
|
||||||
|
r = g = b = inputData[px];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (colorFormat) {
|
||||||
|
case colormap::RGB:
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(b);
|
||||||
|
break;
|
||||||
|
case colormap::RGBA:
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(a);
|
||||||
|
break;
|
||||||
|
case colormap::BGR:
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(r);
|
||||||
|
break;
|
||||||
|
case colormap::BGRA:
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(a);
|
||||||
|
break;
|
||||||
|
case colormap::B:
|
||||||
|
newData.push_back(rgbToGrayscale(r, g, b));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = std::move(newData);
|
||||||
|
resetState(_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setData(const std::vector<float>& inputData) {
|
||||||
|
size_t channels = getChannels(colorFormat);
|
||||||
|
|
||||||
|
if (inputData.size() != width * height * channels) {
|
||||||
|
throw std::runtime_error("Input float data size does not match frame dimensions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> newData;
|
||||||
|
newData.reserve(inputData.size());
|
||||||
|
|
||||||
|
for (float val : inputData) {
|
||||||
|
// Clamp between 0.0 and 1.0, scale to 255
|
||||||
|
float v = std::max(0.0f, std::min(1.0f, val));
|
||||||
|
newData.push_back(static_cast<uint8_t>(v * 255.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = std::move(newData);
|
||||||
|
resetState(_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setData(const std::vector<float>& inputData, colormap inputFormat) {
|
||||||
|
size_t srcChannels = getChannels(inputFormat);
|
||||||
|
size_t dstChannels = getChannels(colorFormat);
|
||||||
|
size_t numPixels = width * height;
|
||||||
|
|
||||||
|
if (inputData.size() != numPixels * srcChannels) {
|
||||||
|
throw std::runtime_error("Input float data size does not match frame dimensions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> newData;
|
||||||
|
newData.reserve(numPixels * dstChannels);
|
||||||
|
|
||||||
|
auto floatToByte = [](float f) -> uint8_t {
|
||||||
|
return static_cast<uint8_t>(std::max(0.0f, std::min(1.0f, f)) * 255.0f);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numPixels; ++i) {
|
||||||
|
size_t px = i * srcChannels;
|
||||||
|
uint8_t r = 0, g = 0, b = 0, a = 255;
|
||||||
|
|
||||||
|
// Extract and convert floats to bytes
|
||||||
|
switch (inputFormat) {
|
||||||
|
case colormap::RGB:
|
||||||
|
r = floatToByte(inputData[px]);
|
||||||
|
g = floatToByte(inputData[px+1]);
|
||||||
|
b = floatToByte(inputData[px+2]);
|
||||||
|
break;
|
||||||
|
case colormap::RGBA:
|
||||||
|
r = floatToByte(inputData[px]);
|
||||||
|
g = floatToByte(inputData[px+1]);
|
||||||
|
b = floatToByte(inputData[px+2]);
|
||||||
|
a = floatToByte(inputData[px+3]);
|
||||||
|
break;
|
||||||
|
case colormap::BGR:
|
||||||
|
b = floatToByte(inputData[px]);
|
||||||
|
g = floatToByte(inputData[px+1]);
|
||||||
|
r = floatToByte(inputData[px+2]);
|
||||||
|
break;
|
||||||
|
case colormap::BGRA:
|
||||||
|
b = floatToByte(inputData[px]);
|
||||||
|
g = floatToByte(inputData[px+1]);
|
||||||
|
r = floatToByte(inputData[px+2]);
|
||||||
|
a = floatToByte(inputData[px+3]);
|
||||||
|
break;
|
||||||
|
case colormap::B:
|
||||||
|
r = g = b = floatToByte(inputData[px]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (colorFormat) {
|
||||||
|
case colormap::RGB:
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(b);
|
||||||
|
break;
|
||||||
|
case colormap::RGBA:
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(a);
|
||||||
|
break;
|
||||||
|
case colormap::BGR:
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(r);
|
||||||
|
break;
|
||||||
|
case colormap::BGRA:
|
||||||
|
newData.push_back(b);
|
||||||
|
newData.push_back(g);
|
||||||
|
newData.push_back(r);
|
||||||
|
newData.push_back(a);
|
||||||
|
break;
|
||||||
|
case colormap::B:
|
||||||
|
newData.push_back(rgbToGrayscale(r, g, b));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = std::move(newData);
|
||||||
|
resetState(_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<uint8_t>& getData() const {
|
const std::vector<uint8_t>& getData() const {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> getPixel(size_t x, size_t y) const {
|
||||||
|
if (cformat != compresstype::RAW) {
|
||||||
|
throw std::runtime_error("Cannot get pixel data from a compressed frame. Decompress first.");
|
||||||
|
}
|
||||||
|
if (x >= width || y >= height) {
|
||||||
|
throw std::out_of_range("Pixel coordinates out of bounds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t channels = getChannels(colorFormat);
|
||||||
|
size_t index = (y * width + x) * channels;
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixel;
|
||||||
|
pixel.reserve(channels);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < channels; ++i) {
|
||||||
|
pixel.push_back(_data[index + i]);
|
||||||
|
}
|
||||||
|
return pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPixel(size_t x, size_t y, const std::vector<uint8_t>& values) {
|
||||||
|
if (cformat != compresstype::RAW) {
|
||||||
|
throw std::runtime_error("Cannot set pixel data on a compressed frame. Decompress first.");
|
||||||
|
}
|
||||||
|
if (x >= width || y >= height) {
|
||||||
|
throw std::out_of_range("Pixel coordinates out of bounds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t channels = getChannels(colorFormat);
|
||||||
|
if (values.size() != channels) {
|
||||||
|
throw std::invalid_argument("Input value count does not match frame channel count.");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = (y * width + x) * channels;
|
||||||
|
for (size_t i = 0; i < channels; ++i) {
|
||||||
|
_data[index + i] = values[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since data changed, previous compression stats are invalid
|
||||||
|
resetState(_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
// Run-Length Encoding (RLE) compression
|
// Run-Length Encoding (RLE) compression
|
||||||
frame& compressFrameRLE() {
|
frame& compressFrameRLE() {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
@@ -243,13 +488,27 @@ public:
|
|||||||
void printCompressionInfo() const {
|
void printCompressionInfo() const {
|
||||||
std::cout << "Compression Type: ";
|
std::cout << "Compression Type: ";
|
||||||
switch (cformat) {
|
switch (cformat) {
|
||||||
case compresstype::RLE: std::cout << "RLE"; break;
|
case compresstype::RLE:
|
||||||
case compresstype::DIFF: std::cout << "DIFF"; break;
|
std::cout << "RLE";
|
||||||
case compresstype::DIFFRLE: std::cout << "DIFF + RLE"; break;
|
break;
|
||||||
case compresstype::LZ78: std::cout << "LZ78 (kinda)"; break;
|
case compresstype::DIFF:
|
||||||
case compresstype::HUFFMAN: std::cout << "HUFFMAN"; break;
|
std::cout << "DIFF";
|
||||||
case compresstype::RAW: std::cout << "RAW (uncompressed)"; break;
|
break;
|
||||||
default: std::cout << "UNKNOWN"; break;
|
case compresstype::DIFFRLE:
|
||||||
|
std::cout << "DIFF + RLE";
|
||||||
|
break;
|
||||||
|
case compresstype::LZ78:
|
||||||
|
std::cout << "LZ78 (kinda)";
|
||||||
|
break;
|
||||||
|
case compresstype::HUFFMAN:
|
||||||
|
std::cout << "HUFFMAN";
|
||||||
|
break;
|
||||||
|
case compresstype::RAW:
|
||||||
|
std::cout << "RAW (uncompressed)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cout << "UNKNOWN";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
@@ -310,6 +569,35 @@ public:
|
|||||||
_data.shrink_to_fit();
|
_data.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resize(size_t newWidth, size_t newHeight, interpolation method = interpolation::NEAREST) {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
if (cformat != compresstype::RAW) {
|
||||||
|
throw std::runtime_error("Cannot resize a compressed frame. Decompress first.");
|
||||||
|
}
|
||||||
|
if (newWidth == 0 || newHeight == 0) {
|
||||||
|
throw std::invalid_argument("Target dimensions must be non-zero.");
|
||||||
|
}
|
||||||
|
if (width == newWidth && height == newHeight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t channels = getChannels(colorFormat);
|
||||||
|
std::vector<uint8_t> newData;
|
||||||
|
newData.resize(newWidth * newHeight * channels);
|
||||||
|
|
||||||
|
if (method == interpolation::NEAREST) {
|
||||||
|
resizeNearest(newData, newWidth, newHeight, channels);
|
||||||
|
} else if (method == interpolation::BILINEAR) {
|
||||||
|
resizeBilinear(newData, newWidth, newHeight, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
_data = std::move(newData);
|
||||||
|
width = newWidth;
|
||||||
|
height = newHeight;
|
||||||
|
|
||||||
|
resetState(_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::vector<uint8_t>> sortvecs(std::vector<std::vector<uint8_t>> source) {
|
std::vector<std::vector<uint8_t>> sortvecs(std::vector<std::vector<uint8_t>> source) {
|
||||||
std::sort(source.begin(), source.end(), [](const std::vector<uint8_t> & a, const std::vector<uint8_t> & b) {return a.size() > b.size();});
|
std::sort(source.begin(), source.end(), [](const std::vector<uint8_t> & a, const std::vector<uint8_t> & b) {return a.size() > b.size();});
|
||||||
@@ -501,21 +789,98 @@ private:
|
|||||||
throw std::logic_error("Function not yet implemented");
|
throw std::logic_error("Function not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resizeNearest(std::vector<uint8_t>& dst, size_t newW, size_t newH, size_t channels) {
|
||||||
|
const double x_ratio = (double)width / newW;
|
||||||
|
const double y_ratio = (double)height / newH;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for (size_t y = 0; y < newH; ++y) {
|
||||||
|
size_t srcY = static_cast<size_t>(std::floor(y * y_ratio));
|
||||||
|
if (srcY >= height) srcY = height - 1;
|
||||||
|
|
||||||
|
size_t destOffsetBase = y * newW * channels;
|
||||||
|
size_t srcRowOffset = srcY * width * channels;
|
||||||
|
|
||||||
|
for (size_t x = 0; x < newW; ++x) {
|
||||||
|
size_t srcX = static_cast<size_t>(std::floor(x * x_ratio));
|
||||||
|
if (srcX >= width) srcX = width - 1;
|
||||||
|
|
||||||
|
size_t srcIndex = srcRowOffset + (srcX * channels);
|
||||||
|
size_t destIndex = destOffsetBase + (x * channels);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
|
dst[destIndex + c] = _data[srcIndex + c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizeBilinear(std::vector<uint8_t>& dst, size_t newW, size_t newH, size_t channels) {
|
||||||
|
const float x_ratio = (width > 1) ? static_cast<float>(width - 1) / (newW - 1) : 0;
|
||||||
|
const float y_ratio = (height > 1) ? static_cast<float>(height - 1) / (newH - 1) : 0;
|
||||||
|
|
||||||
|
for (size_t y = 0; y < newH; ++y) {
|
||||||
|
float srcY_f = y * y_ratio;
|
||||||
|
size_t y_l = static_cast<size_t>(srcY_f);
|
||||||
|
size_t y_h = (y_l + 1 < height) ? y_l + 1 : y_l;
|
||||||
|
float y_weight = srcY_f - y_l;
|
||||||
|
float y_inv = 1.0f - y_weight;
|
||||||
|
|
||||||
|
size_t destOffsetBase = y * newW * channels;
|
||||||
|
|
||||||
|
for (size_t x = 0; x < newW; ++x) {
|
||||||
|
float srcX_f = x * x_ratio;
|
||||||
|
size_t x_l = static_cast<size_t>(srcX_f);
|
||||||
|
size_t x_h = (x_l + 1 < width) ? x_l + 1 : x_l;
|
||||||
|
float x_weight = srcX_f - x_l;
|
||||||
|
float x_inv = 1.0f - x_weight;
|
||||||
|
|
||||||
|
size_t idx_TL = (y_l * width + x_l) * channels;
|
||||||
|
size_t idx_TR = (y_l * width + x_h) * channels;
|
||||||
|
size_t idx_BL = (y_h * width + x_l) * channels;
|
||||||
|
size_t idx_BR = (y_h * width + x_h) * channels;
|
||||||
|
size_t destIndex = destOffsetBase + (x * channels);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
|
float val_TL = _data[idx_TL + c];
|
||||||
|
float val_TR = _data[idx_TR + c];
|
||||||
|
float val_BL = _data[idx_BL + c];
|
||||||
|
float val_BR = _data[idx_BR + c];
|
||||||
|
|
||||||
|
float top = val_TL * x_inv + val_TR * x_weight;
|
||||||
|
float bottom = val_BL * x_inv + val_BR * x_weight;
|
||||||
|
float result = top * y_inv + bottom * y_weight;
|
||||||
|
dst[destIndex + c] = static_cast<uint8_t>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, frame& f) {
|
||||||
std::ostream& operator<<(std::ostream& os, frame& f) {
|
|
||||||
os << "Frame[" << f.getWidth() << "x" << f.getHeight() << "] ";
|
os << "Frame[" << f.getWidth() << "x" << f.getHeight() << "] ";
|
||||||
|
|
||||||
// Color format
|
// Color format
|
||||||
os << "Format: ";
|
os << "Format: ";
|
||||||
switch (f.colorFormat) {
|
switch (f.colorFormat) {
|
||||||
case frame::colormap::RGB: os << "RGB"; break;
|
case frame::colormap::RGB:
|
||||||
case frame::colormap::RGBA: os << "RGBA"; break;
|
os << "RGB";
|
||||||
case frame::colormap::BGR: os << "BGR"; break;
|
break;
|
||||||
case frame::colormap::BGRA: os << "BGRA"; break;
|
case frame::colormap::RGBA:
|
||||||
case frame::colormap::B: os << "Grayscale"; break;
|
os << "RGBA";
|
||||||
default: os << "Unknown"; break;
|
break;
|
||||||
|
case frame::colormap::BGR:
|
||||||
|
os << "BGR";
|
||||||
|
break;
|
||||||
|
case frame::colormap::BGRA:
|
||||||
|
os << "BGRA";
|
||||||
|
break;
|
||||||
|
case frame::colormap::B:
|
||||||
|
os << "Grayscale";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
os << "Unknown";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compression info
|
// Compression info
|
||||||
|
|||||||
@@ -63,8 +63,11 @@ public:
|
|||||||
return crossProduct.length() / direction.length();
|
return crossProduct.length() / direction.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform ray by a 4x4 matrix (for perspective/affine transformations)
|
// Ray3 transform(const Mat4<T>& matrix) const {
|
||||||
Ray3 transform(const class Mat4& matrix) const;
|
// Vec3<T> transformedOrigin = matrix.transformPoint(origin);
|
||||||
|
// Vec3<T> transformedDirection = matrix.transformDirection(direction);
|
||||||
|
// return Ray3<T>(transformedOrigin, transformedDirection.normalized());
|
||||||
|
// }
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
return "Ray3(origin: " + origin.toString() + ", direction: " + direction.toString() + ")";
|
return "Ray3(origin: " + origin.toString() + ", direction: " + direction.toString() + ")";
|
||||||
@@ -72,5 +75,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
using Ray3f = Ray3<float>;
|
using Ray3f = Ray3<float>;
|
||||||
|
using Ray3T = Ray3<size_t>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
383
util/sim/elementcontent.hpp
Normal file
383
util/sim/elementcontent.hpp
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#ifndef ELEMENTS
|
||||||
|
#define ELEMENTS
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
struct BaseElementProps {
|
||||||
|
float density; // kg/m^3
|
||||||
|
float meltingPoint; // Kelvin
|
||||||
|
float boilingPoint; // Kelvin
|
||||||
|
float specificHeat; // J/(kg*K)
|
||||||
|
float electronegativity; // Pauling scale
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<BaseElementProps, 118> ELEMENT_DB = {{
|
||||||
|
// 1: Hydrogen
|
||||||
|
{0.08988f, 14.01f, 20.28f, 14304.0f, 2.20f},
|
||||||
|
// 2: Helium
|
||||||
|
{0.1785f, 0.95f, 4.22f, 5193.0f, 0.0f}, // No electronegativity, using 0
|
||||||
|
// 3: Lithium
|
||||||
|
{534.0f, 453.69f, 1560.0f, 3582.0f, 0.98f},
|
||||||
|
// 4: Beryllium
|
||||||
|
{1850.0f, 1560.0f, 2742.0f, 1825.0f, 1.57f},
|
||||||
|
// 5: Boron
|
||||||
|
{2340.0f, 2349.0f, 4200.0f, 1026.0f, 2.04f},
|
||||||
|
// 6: Carbon
|
||||||
|
{2267.0f, 4000.0f, 4300.0f, 709.0f, 2.55f},
|
||||||
|
// 7: Nitrogen
|
||||||
|
{1.2506f, 63.15f, 77.36f, 1040.0f, 3.04f},
|
||||||
|
// 8: Oxygen
|
||||||
|
{1.429f, 54.36f, 90.2f, 918.0f, 3.44f},
|
||||||
|
// 9: Fluorine
|
||||||
|
{1.696f, 53.53f, 85.03f, 824.0f, 3.98f},
|
||||||
|
// 10: Neon
|
||||||
|
{0.9002f, 24.56f, 27.07f, 1030.0f, 0.0f}, // No electronegativity
|
||||||
|
// 11: Sodium
|
||||||
|
{968.0f, 370.87f, 1156.0f, 1228.0f, 0.93f},
|
||||||
|
// 12: Magnesium
|
||||||
|
{1738.0f, 923.0f, 1363.0f, 1023.0f, 1.31f},
|
||||||
|
// 13: Aluminium
|
||||||
|
{2700.0f, 933.47f, 2792.0f, 897.0f, 1.61f},
|
||||||
|
// 14: Silicon
|
||||||
|
{2329.0f, 1687.0f, 3538.0f, 705.0f, 1.9f},
|
||||||
|
// 15: Phosphorus
|
||||||
|
{1823.0f, 317.3f, 550.0f, 769.0f, 2.19f},
|
||||||
|
// 16: Sulfur
|
||||||
|
{2070.0f, 388.36f, 717.87f, 710.0f, 2.58f},
|
||||||
|
// 17: Chlorine
|
||||||
|
{3.2f, 171.6f, 239.11f, 479.0f, 3.16f},
|
||||||
|
// 18: Argon
|
||||||
|
{1.784f, 83.8f, 87.3f, 520.0f, 0.0f}, // No electronegativity
|
||||||
|
// 19: Potassium
|
||||||
|
{890.0f, 336.53f, 1032.0f, 757.0f, 0.82f},
|
||||||
|
// 20: Calcium
|
||||||
|
{1550.0f, 1115.0f, 1757.0f, 647.0f, 1.0f},
|
||||||
|
// 21: Scandium
|
||||||
|
{2985.0f, 1814.0f, 3109.0f, 568.0f, 1.36f},
|
||||||
|
// 22: Titanium
|
||||||
|
{4506.0f, 1941.0f, 3560.0f, 523.0f, 1.54f},
|
||||||
|
// 23: Vanadium
|
||||||
|
{6110.0f, 2183.0f, 3680.0f, 489.0f, 1.63f},
|
||||||
|
// 24: Chromium
|
||||||
|
{7150.0f, 2180.0f, 2944.0f, 449.0f, 1.66f},
|
||||||
|
// 25: Manganese
|
||||||
|
{7210.0f, 1519.0f, 2334.0f, 479.0f, 1.55f},
|
||||||
|
// 26: Iron
|
||||||
|
{7874.0f, 1811.0f, 3134.0f, 449.0f, 1.83f},
|
||||||
|
// 27: Cobalt
|
||||||
|
{8900.0f, 1768.0f, 3200.0f, 421.0f, 1.88f},
|
||||||
|
// 28: Nickel
|
||||||
|
{8908.0f, 1728.0f, 3186.0f, 444.0f, 1.91f},
|
||||||
|
// 29: Copper
|
||||||
|
{8960.0f, 1357.77f, 2835.0f, 385.0f, 1.9f},
|
||||||
|
// 30: Zinc
|
||||||
|
{7140.0f, 692.88f, 1180.0f, 388.0f, 1.65f},
|
||||||
|
// 31: Gallium
|
||||||
|
{5910.0f, 302.9146f, 2673.0f, 371.0f, 1.81f},
|
||||||
|
// 32: Germanium
|
||||||
|
{5323.0f, 1211.4f, 3106.0f, 320.0f, 2.01f},
|
||||||
|
// 33: Arsenic
|
||||||
|
{5727.0f, 1090.0f, 887.0f, 329.0f, 2.18f}, // Sublimes at 887K
|
||||||
|
// 34: Selenium
|
||||||
|
{4810.0f, 453.0f, 958.0f, 321.0f, 2.55f},
|
||||||
|
// 35: Bromine
|
||||||
|
{3102.8f, 265.8f, 332.0f, 474.0f, 2.96f},
|
||||||
|
// 36: Krypton
|
||||||
|
{3.749f, 115.79f, 119.93f, 248.0f, 3.0f},
|
||||||
|
// 37: Rubidium
|
||||||
|
{1532.0f, 312.46f, 961.0f, 363.0f, 0.82f},
|
||||||
|
// 38: Strontium
|
||||||
|
{2640.0f, 1050.0f, 1655.0f, 301.0f, 0.95f},
|
||||||
|
// 39: Yttrium
|
||||||
|
{4472.0f, 1799.0f, 3609.0f, 298.0f, 1.22f},
|
||||||
|
// 40: Zirconium
|
||||||
|
{6520.0f, 2128.0f, 4682.0f, 278.0f, 1.33f},
|
||||||
|
// 41: Niobium
|
||||||
|
{8570.0f, 2750.0f, 5017.0f, 265.0f, 1.6f},
|
||||||
|
// 42: Molybdenum
|
||||||
|
{10280.0f, 2896.0f, 4912.0f, 251.0f, 2.16f},
|
||||||
|
// 43: Technetium
|
||||||
|
{11000.0f, 2430.0f, 4538.0f, 0.0f, 1.9f}, // No specific heat data
|
||||||
|
// 44: Ruthenium
|
||||||
|
{12450.0f, 2607.0f, 4423.0f, 238.0f, 2.2f},
|
||||||
|
// 45: Rhodium
|
||||||
|
{12410.0f, 2237.0f, 3968.0f, 243.0f, 2.28f},
|
||||||
|
// 46: Palladium
|
||||||
|
{12023.0f, 1828.05f, 3236.0f, 244.0f, 2.2f},
|
||||||
|
// 47: Silver
|
||||||
|
{10490.0f, 1234.93f, 2435.0f, 235.0f, 1.93f},
|
||||||
|
// 48: Cadmium
|
||||||
|
{8650.0f, 594.22f, 1040.0f, 232.0f, 1.69f},
|
||||||
|
// 49: Indium
|
||||||
|
{7310.0f, 429.75f, 2345.0f, 233.0f, 1.78f},
|
||||||
|
// 50: Tin
|
||||||
|
{7265.0f, 505.08f, 2875.0f, 228.0f, 1.96f},
|
||||||
|
// 51: Antimony
|
||||||
|
{6697.0f, 903.78f, 1860.0f, 207.0f, 2.05f},
|
||||||
|
// 52: Tellurium
|
||||||
|
{6240.0f, 722.66f, 1261.0f, 202.0f, 2.1f},
|
||||||
|
// 53: Iodine
|
||||||
|
{4933.0f, 386.85f, 457.4f, 214.0f, 2.66f},
|
||||||
|
// 54: Xenon
|
||||||
|
{5.894f, 161.4f, 165.03f, 158.0f, 2.6f},
|
||||||
|
// 55: Caesium
|
||||||
|
{1930.0f, 301.59f, 944.0f, 242.0f, 0.79f},
|
||||||
|
// 56: Barium
|
||||||
|
{3510.0f, 1000.0f, 2170.0f, 204.0f, 0.89f},
|
||||||
|
// 57: Lanthanum
|
||||||
|
{6162.0f, 1193.0f, 3737.0f, 195.0f, 1.1f},
|
||||||
|
// 58: Cerium
|
||||||
|
{6770.0f, 1068.0f, 3716.0f, 192.0f, 1.12f},
|
||||||
|
// 59: Praseodymium
|
||||||
|
{6770.0f, 1208.0f, 3793.0f, 193.0f, 1.13f},
|
||||||
|
// 60: Neodymium
|
||||||
|
{7010.0f, 1297.0f, 3347.0f, 190.0f, 1.14f},
|
||||||
|
// 61: Promethium
|
||||||
|
{7260.0f, 1315.0f, 3273.0f, 0.0f, 1.13f}, // No specific heat data
|
||||||
|
// 62: Samarium
|
||||||
|
{7520.0f, 1345.0f, 2067.0f, 197.0f, 1.17f},
|
||||||
|
// 63: Europium
|
||||||
|
{5244.0f, 1099.0f, 1802.0f, 182.0f, 1.2f},
|
||||||
|
// 64: Gadolinium
|
||||||
|
{7900.0f, 1585.0f, 3546.0f, 236.0f, 1.2f},
|
||||||
|
// 65: Terbium
|
||||||
|
{8230.0f, 1629.0f, 3503.0f, 182.0f, 1.2f},
|
||||||
|
// 66: Dysprosium
|
||||||
|
{8540.0f, 1680.0f, 2840.0f, 170.0f, 1.22f},
|
||||||
|
// 67: Holmium
|
||||||
|
{8790.0f, 1734.0f, 2993.0f, 165.0f, 1.23f},
|
||||||
|
// 68: Erbium
|
||||||
|
{9066.0f, 1802.0f, 3141.0f, 168.0f, 1.24f},
|
||||||
|
// 69: Thulium
|
||||||
|
{9320.0f, 1818.0f, 2223.0f, 160.0f, 1.25f},
|
||||||
|
// 70: Ytterbium
|
||||||
|
{6900.0f, 1097.0f, 1469.0f, 155.0f, 1.1f},
|
||||||
|
// 71: Lutetium
|
||||||
|
{9841.0f, 1925.0f, 3675.0f, 154.0f, 1.27f},
|
||||||
|
// 72: Hafnium
|
||||||
|
{13310.0f, 2506.0f, 4876.0f, 144.0f, 1.3f},
|
||||||
|
// 73: Tantalum
|
||||||
|
{16690.0f, 3290.0f, 5731.0f, 140.0f, 1.5f},
|
||||||
|
// 74: Tungsten
|
||||||
|
{19250.0f, 3695.0f, 6203.0f, 132.0f, 2.36f},
|
||||||
|
// 75: Rhenium
|
||||||
|
{21020.0f, 3459.0f, 5869.0f, 137.0f, 1.9f},
|
||||||
|
// 76: Osmium
|
||||||
|
{22590.0f, 3306.0f, 5285.0f, 130.0f, 2.2f},
|
||||||
|
// 77: Iridium
|
||||||
|
{22560.0f, 2719.0f, 4701.0f, 131.0f, 2.2f},
|
||||||
|
// 78: Platinum
|
||||||
|
{21450.0f, 2041.4f, 4098.0f, 133.0f, 2.28f},
|
||||||
|
// 79: Gold
|
||||||
|
{19300.0f, 1337.33f, 3129.0f, 129.0f, 2.54f},
|
||||||
|
// 80: Mercury
|
||||||
|
{13534.0f, 234.43f, 629.88f, 140.0f, 2.0f},
|
||||||
|
// 81: Thallium
|
||||||
|
{11850.0f, 577.0f, 1746.0f, 129.0f, 1.62f},
|
||||||
|
// 82: Lead
|
||||||
|
{11340.0f, 600.61f, 2022.0f, 129.0f, 2.33f}, // Using 4+ value
|
||||||
|
// 83: Bismuth
|
||||||
|
{9780.0f, 544.7f, 1837.0f, 122.0f, 2.02f},
|
||||||
|
// 84: Polonium
|
||||||
|
{9196.0f, 527.0f, 1235.0f, 0.0f, 2.0f}, // No specific heat data
|
||||||
|
// 85: Astatine
|
||||||
|
{8930.0f, 575.0f, 610.0f, 0.0f, 2.2f}, // Approx density, no specific heat
|
||||||
|
// 86: Radon
|
||||||
|
{9.73f, 202.0f, 211.3f, 94.0f, 2.2f},
|
||||||
|
// 87: Francium
|
||||||
|
{2480.0f, 281.0f, 890.0f, 0.0f, 0.79f}, // Approx values
|
||||||
|
// 88: Radium
|
||||||
|
{5500.0f, 973.0f, 2010.0f, 94.0f, 0.9f},
|
||||||
|
// 89: Actinium
|
||||||
|
{10000.0f, 1323.0f, 3471.0f, 120.0f, 1.1f},
|
||||||
|
// 90: Thorium
|
||||||
|
{11700.0f, 2115.0f, 5061.0f, 113.0f, 1.3f},
|
||||||
|
// 91: Protactinium
|
||||||
|
{15370.0f, 1841.0f, 4300.0f, 0.0f, 1.5f}, // No specific heat data
|
||||||
|
// 92: Uranium
|
||||||
|
{19100.0f, 1405.3f, 4404.0f, 116.0f, 1.38f},
|
||||||
|
// 93: Neptunium
|
||||||
|
{20450.0f, 917.0f, 4273.0f, 0.0f, 1.36f}, // No specific heat data
|
||||||
|
// 94: Plutonium
|
||||||
|
{19850.0f, 912.5f, 3501.0f, 0.0f, 1.28f}, // No specific heat data
|
||||||
|
// 95: Americium
|
||||||
|
{12000.0f, 1449.0f, 2880.0f, 0.0f, 1.13f}, // No specific heat data
|
||||||
|
// 96: Curium
|
||||||
|
{13510.0f, 1613.0f, 3383.0f, 0.0f, 1.28f}, // No specific heat data
|
||||||
|
// 97: Berkelium
|
||||||
|
{14780.0f, 1259.0f, 2900.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 98: Californium
|
||||||
|
{15100.0f, 1173.0f, 1743.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 99: Einsteinium
|
||||||
|
{8840.0f, 1133.0f, 1269.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 100: Fermium
|
||||||
|
{9700.0f, 1125.0f, 1800.0f, 0.0f, 1.3f}, // Estimated values
|
||||||
|
// 101: Mendelevium
|
||||||
|
{10300.0f, 1100.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 102: Nobelium
|
||||||
|
{9900.0f, 1100.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 103: Lawrencium
|
||||||
|
{14400.0f, 1900.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 104: Rutherfordium
|
||||||
|
{17000.0f, 2400.0f, 5800.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 105: Dubnium
|
||||||
|
{21600.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 106: Seaborgium
|
||||||
|
{23500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 107: Bohrium
|
||||||
|
{26500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 108: Hassium
|
||||||
|
{28000.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 109: Meitnerium
|
||||||
|
{27500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 110: Darmstadtium
|
||||||
|
{26500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 111: Roentgenium
|
||||||
|
{23000.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 112: Copernicium
|
||||||
|
{14000.0f, 283.0f, 340.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 113: Nihonium
|
||||||
|
{16000.0f, 700.0f, 1400.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 114: Flerovium
|
||||||
|
{11400.0f, 284.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 115: Moscovium
|
||||||
|
{13500.0f, 700.0f, 1400.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 116: Livermorium
|
||||||
|
{12900.0f, 700.0f, 1100.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 117: Tennessine
|
||||||
|
{7200.0f, 700.0f, 883.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 118: Oganesson
|
||||||
|
{7000.0f, 325.0f, 450.0f, 0.0f, 0.0f} // Estimated
|
||||||
|
}};
|
||||||
|
|
||||||
|
struct PointProperties {
|
||||||
|
float weight = 0.0f; // Total mass
|
||||||
|
float density = 0.0f; // Mass / Volume
|
||||||
|
float meltingPoint = 0.0f;
|
||||||
|
float boilingPoint = 0.0f;
|
||||||
|
float specificHeat = 0.0f;
|
||||||
|
float electronegativity = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elementContent {
|
||||||
|
float hydrogen = 0.0f;
|
||||||
|
float helium = 0.0f;
|
||||||
|
float lithium = 0.0f;
|
||||||
|
float beryllium = 0.0f;
|
||||||
|
float boron = 0.0f;
|
||||||
|
float carbon = 0.0f;
|
||||||
|
float nitrogen = 0.0f;
|
||||||
|
float oxygen = 0.0f;
|
||||||
|
float fluorine = 0.0f;
|
||||||
|
float neon = 0.0f;
|
||||||
|
float sodium = 0.0f;
|
||||||
|
float magnesium = 0.0f;
|
||||||
|
float aluminum = 0.0f;
|
||||||
|
float silicon = 0.0f;
|
||||||
|
float phosporus = 0.0f;
|
||||||
|
float sulfur = 0.0f;
|
||||||
|
float chlorine = 0.0f;
|
||||||
|
float argon = 0.0f;
|
||||||
|
float potassium = 0.0f;
|
||||||
|
float calcium = 0.0f;
|
||||||
|
float scandium = 0.0f;
|
||||||
|
float titanium = 0.0f;
|
||||||
|
float vanadium = 0.0f;
|
||||||
|
float chromium = 0.0f;
|
||||||
|
float manganese = 0.0f;
|
||||||
|
float iron = 0.0f;
|
||||||
|
float cobalt = 0.0f;
|
||||||
|
float nickel = 0.0f;
|
||||||
|
float copper = 0.0f;
|
||||||
|
float zinc = 0.0f;
|
||||||
|
float gallium = 0.0f;
|
||||||
|
float germanium = 0.0f;
|
||||||
|
float arsenic = 0.0f;
|
||||||
|
float selenium = 0.0f;
|
||||||
|
float bromine = 0.0f;
|
||||||
|
float krypton = 0.0f;
|
||||||
|
float rubidium = 0.0f;
|
||||||
|
float strontium = 0.0f;
|
||||||
|
float yttrium = 0.0f;
|
||||||
|
float zirconium = 0.0f;
|
||||||
|
float niobium = 0.0f;
|
||||||
|
float molybdenum = 0.0f;
|
||||||
|
float technetium = 0.0f;
|
||||||
|
float ruthenium = 0.0f;
|
||||||
|
float rhodium = 0.0f;
|
||||||
|
float palladium = 0.0f;
|
||||||
|
float silver = 0.0f;
|
||||||
|
float cadmium = 0.0f;
|
||||||
|
float indium = 0.0f;
|
||||||
|
float tin = 0.0f;
|
||||||
|
float antimony = 0.0f;
|
||||||
|
float tellurium = 0.0f;
|
||||||
|
float iodine = 0.0f;
|
||||||
|
float xenon = 0.0f;
|
||||||
|
float caesium = 0.0f;
|
||||||
|
float barium = 0.0f;
|
||||||
|
float lanthanum = 0.0f;
|
||||||
|
float cerium = 0.0f;
|
||||||
|
float praseodymium = 0.0f;
|
||||||
|
float neodymium = 0.0f;
|
||||||
|
float promethium = 0.0f;
|
||||||
|
float samarium = 0.0f;
|
||||||
|
float europium = 0.0f;
|
||||||
|
float gadolinium = 0.0f;
|
||||||
|
float terbium = 0.0f;
|
||||||
|
float dysprosium = 0.0f;
|
||||||
|
float holmium = 0.0f;
|
||||||
|
float erbium = 0.0f;
|
||||||
|
float thulium = 0.0f;
|
||||||
|
float ytterbium = 0.0f;
|
||||||
|
float lutetium = 0.0f;
|
||||||
|
float hafnium = 0.0f;
|
||||||
|
float tantalum = 0.0f;
|
||||||
|
float tungsten = 0.0f;
|
||||||
|
float rhenium = 0.0f;
|
||||||
|
float osmium = 0.0f;
|
||||||
|
float iridium = 0.0f;
|
||||||
|
float platinum = 0.0f;
|
||||||
|
float gold = 0.0f;
|
||||||
|
float mercury = 0.0f;
|
||||||
|
float thallium = 0.0f;
|
||||||
|
float lead = 0.0f;
|
||||||
|
float bismuth = 0.0f;
|
||||||
|
float polonium = 0.0f;
|
||||||
|
float astatine = 0.0f;
|
||||||
|
float radon = 0.0f;
|
||||||
|
float francium = 0.0f;
|
||||||
|
float radium = 0.0f;
|
||||||
|
float actinium = 0.0f;
|
||||||
|
float thorium = 0.0f;
|
||||||
|
float protactinium = 0.0f;
|
||||||
|
float uranium = 0.0f;
|
||||||
|
float neptunium = 0.0f;
|
||||||
|
float plutonium = 0.0f;
|
||||||
|
float americium = 0.0f;
|
||||||
|
float curium = 0.0f;
|
||||||
|
float berkelium = 0.0f;
|
||||||
|
float californium = 0.0f;
|
||||||
|
float einsteinium = 0.0f;
|
||||||
|
float fermium = 0.0f;
|
||||||
|
float mendelevium = 0.0f;
|
||||||
|
float nobelium = 0.0f;
|
||||||
|
float lawrencium = 0.0f;
|
||||||
|
float rutherfordium = 0.0f;
|
||||||
|
float dubnium = 0.0f;
|
||||||
|
float seaborgium = 0.0f;
|
||||||
|
float bohrium = 0.0f;
|
||||||
|
float hassium = 0.0f;
|
||||||
|
float meitnerium = 0.0f;
|
||||||
|
float darmstadtium = 0.0f;
|
||||||
|
float roentgenium = 0.0f;
|
||||||
|
float cpernicium = 0.0f;
|
||||||
|
float nihnium = 0.0f;
|
||||||
|
float flerovium = 0.0f;
|
||||||
|
float moscovium = 0.0f;
|
||||||
|
float livermorium = 0.0f;
|
||||||
|
float tennessine = 0.0f;
|
||||||
|
float oganesson = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
364
util/sim/fluidsim.hpp
Normal file
364
util/sim/fluidsim.hpp
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#ifndef FLUIDSIM_HPP
|
||||||
|
#define FLUIDSIM_HPP
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
|
#include "../util/output/frame.hpp"
|
||||||
|
|
||||||
|
struct fluidParticle {
|
||||||
|
Eigen::Matrix<float, 3, 1> velocity;
|
||||||
|
Eigen::Matrix<float, 3, 1> acceleration;
|
||||||
|
Eigen::Matrix<float, 3, 1> forceAccumulator;
|
||||||
|
float density = 0.0f;
|
||||||
|
float pressure = 0.0f;
|
||||||
|
Eigen::Matrix<float, 3, 1> pressureForce;
|
||||||
|
float viscosity = 0.5f;
|
||||||
|
Eigen::Matrix<float, 3, 1> viscosityForce;
|
||||||
|
float restitution = 5.0f;
|
||||||
|
float mass;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gridConfig {
|
||||||
|
float gridSizeCube = 8192;
|
||||||
|
float SMOOTHING_RADIUS = 1024.0f;
|
||||||
|
float REST_DENSITY = 0.00005f;
|
||||||
|
float TIMESTEP = 0.016f;
|
||||||
|
float G_ATTRACTION = 50.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
Eigen::Matrix<float, 3, 1> posGen() {
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::mt19937 gen(rd());
|
||||||
|
static std::normal_distribution<float> dist(0.0f, 1024.0f);
|
||||||
|
|
||||||
|
return Eigen::Matrix<float, 3, 1>(dist(gen),dist(gen),dist(gen));
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Matrix<float, 3, 1> velGen() {
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::mt19937 gen(rd());
|
||||||
|
static std::normal_distribution<float> dist(0.0f, 1.0f);
|
||||||
|
|
||||||
|
return Eigen::Matrix<float, 3, 1>(dist(gen),dist(gen),dist(gen));
|
||||||
|
}
|
||||||
|
|
||||||
|
float W_poly6(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.squaredNorm();
|
||||||
|
|
||||||
|
if (r > h || r < 0) return 0;
|
||||||
|
float factor = 315 / (64 * M_PI * pow(h, 9));
|
||||||
|
float m = pow(h*h-r, 3);
|
||||||
|
return factor * m;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f gradW_poly6(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.squaredNorm();
|
||||||
|
float h2 = h * h;
|
||||||
|
|
||||||
|
if (r > h2 || r < 0) {
|
||||||
|
return Eigen::Vector3f::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
float m = -6 * pow(h*h-r, 2);
|
||||||
|
float factor = -945.0f / (32.0f * M_PI * std::pow(h, 9));
|
||||||
|
|
||||||
|
return factor * m * rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lapW_poly6(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.squaredNorm();
|
||||||
|
float h2 = h * h;
|
||||||
|
|
||||||
|
if (r > h || r < 0) return 0;
|
||||||
|
|
||||||
|
float m = h2 - r;
|
||||||
|
float term2 = 3.0f * h2 - 7.0f * r;
|
||||||
|
float factor = -945.0f / (32.0f * M_PI * std::pow(h, 9));
|
||||||
|
|
||||||
|
return factor * m * term2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float W_spiky(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.norm();
|
||||||
|
if (r > h || r < 0) return 0;
|
||||||
|
|
||||||
|
float coeff = pow(r-h, 3);
|
||||||
|
float factor = 15 / (M_PI * pow(h, 6));
|
||||||
|
|
||||||
|
return factor * coeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f gradW_spiky(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.norm();
|
||||||
|
|
||||||
|
if (r > h || r < 0) {
|
||||||
|
return Eigen::Vector3f::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
float diff = h - r;
|
||||||
|
float coeff = -45.0f / (M_PI * std::pow(h, 6));
|
||||||
|
|
||||||
|
Eigen::Vector3f direction = rv / r;
|
||||||
|
|
||||||
|
return coeff * std::pow(diff, 2) * direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
float W_visc(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.norm();
|
||||||
|
|
||||||
|
if (r > h || r < 0) return 0;
|
||||||
|
|
||||||
|
float r2 = r * r;
|
||||||
|
float r3 = r2 * r;
|
||||||
|
float h3 = h * h * h;
|
||||||
|
|
||||||
|
float coeff = 15.0f / (2.0f * M_PI * h3);
|
||||||
|
float term = (-0.5f * r3 / h3) + (r2 / (h * h)) + (h / (2.0f * r)) - 1.0f;
|
||||||
|
|
||||||
|
return coeff * term;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lapW_visc(Eigen::Vector3f rv, float h) {
|
||||||
|
float r = rv.norm();
|
||||||
|
|
||||||
|
if (r > h || r < 0) return 0;
|
||||||
|
|
||||||
|
float diff = h - r;
|
||||||
|
float coeff = 45.0f / (M_PI * std::pow(h, 6));
|
||||||
|
|
||||||
|
return coeff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f buildGradient(float value, const std::map<float, Eigen::Vector3f>& gradientKeys) {
|
||||||
|
auto exactMatch = gradientKeys.find(value);
|
||||||
|
if (exactMatch != gradientKeys.end()) {
|
||||||
|
return exactMatch->second;
|
||||||
|
}
|
||||||
|
auto lower = gradientKeys.lower_bound(value);
|
||||||
|
if (lower == gradientKeys.begin()) {
|
||||||
|
return gradientKeys.begin()->second;
|
||||||
|
}
|
||||||
|
if (lower == gradientKeys.end()) {
|
||||||
|
return gradientKeys.rbegin()->second;
|
||||||
|
}
|
||||||
|
auto upper = lower;
|
||||||
|
lower = std::prev(lower);
|
||||||
|
float key1 = lower->first;
|
||||||
|
float key2 = upper->first;
|
||||||
|
const Eigen::Vector3f& color1 = lower->second;
|
||||||
|
const Eigen::Vector3f& color2 = upper->second;
|
||||||
|
float t = (value - key1) / (key2 - key1);
|
||||||
|
t = std::clamp(t, 0.0f, 1.0f);
|
||||||
|
return color1 + t * (color2 - color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
class fluidSim {
|
||||||
|
private:
|
||||||
|
std::unordered_map<size_t, Eigen::Matrix<float, 3, 1>> idposMap;
|
||||||
|
float newMass = 1000;
|
||||||
|
int nextObjectId = 0;
|
||||||
|
std::map<float, Eigen::Vector3f> gradientmap;
|
||||||
|
public:
|
||||||
|
gridConfig config;
|
||||||
|
float closeThresh;
|
||||||
|
Octree<fluidParticle> grid;
|
||||||
|
|
||||||
|
fluidSim() : grid({-config.gridSizeCube, -config.gridSizeCube, -config.gridSizeCube}, {config.gridSizeCube, config.gridSizeCube, config.gridSizeCube}) {
|
||||||
|
closeThresh = 0.01f * config.SMOOTHING_RADIUS;
|
||||||
|
//grid.setBackgroundColor({0.1f, 0.1f, 0.2f});
|
||||||
|
gradientmap.emplace(0.0, Eigen::Vector3f(1, 0, 0));
|
||||||
|
gradientmap.emplace(0.5, Eigen::Vector3f(0, 1, 0));
|
||||||
|
gradientmap.emplace(1.0, Eigen::Vector3f(0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
grid.clear();
|
||||||
|
idposMap.clear();
|
||||||
|
nextObjectId = 0;
|
||||||
|
newMass = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawnParticles(fluidParticle toSpawn, int count, bool resize = true) {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
toSpawn.mass = newMass;
|
||||||
|
Eigen::Vector3f color = buildGradient(toSpawn.mass / 1000, gradientmap);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Eigen::Matrix<float, 3, 1> pos = posGen();
|
||||||
|
int id = nextObjectId++;
|
||||||
|
|
||||||
|
grid.set(toSpawn, pos, true, color, 10, true, id, (toSpawn.mass > 100) ? true : false, 1);
|
||||||
|
idposMap.emplace(id, pos);
|
||||||
|
}
|
||||||
|
if (resize){
|
||||||
|
newMass *= 0.999f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float sphKernel(Eigen::Vector3f rv) {
|
||||||
|
float r = rv.norm();
|
||||||
|
if (r < closeThresh) return W_spiky(rv, config.G_ATTRACTION);
|
||||||
|
else return W_poly6(rv,config.G_ATTRACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void computeDensities() {
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
float densSum = 0;
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) continue;
|
||||||
|
std::vector<std::shared_ptr<Octree<fluidParticle>::NodeData>> neighbors = grid.findInRadius(point.second, config.SMOOTHING_RADIUS);
|
||||||
|
for (auto& neighbor : neighbors) {
|
||||||
|
Eigen::Vector3f rv = node->position - neighbor->position;
|
||||||
|
float w = sphKernel(rv);
|
||||||
|
densSum += neighbor->data.mass * w;
|
||||||
|
}
|
||||||
|
node->data.density = densSum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyPressure() {
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
node->data.pressure = node->data.restitution * (node->data.density - config.REST_DENSITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Eigen::Vector3f pressureForce = Eigen::Vector3f::Zero();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Octree<fluidParticle>::NodeData>> neighbors = grid.findInRadius(point.second, config.SMOOTHING_RADIUS);
|
||||||
|
for (auto& neighbor : neighbors) {
|
||||||
|
if (node == neighbor) continue;
|
||||||
|
Eigen::Vector3f rv = node->position - neighbor->position;
|
||||||
|
Eigen::Vector3f gradW = gradW_spiky(rv, config.SMOOTHING_RADIUS);
|
||||||
|
float scalarP = (node->data.pressure + neighbor->data.pressure) / (2.0f * neighbor->data.density);
|
||||||
|
|
||||||
|
pressureForce -= neighbor->data.mass * scalarP * gradW;
|
||||||
|
}
|
||||||
|
node->data.pressureForce = pressureForce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyViscosity() {
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) continue;
|
||||||
|
Eigen::Vector3f viscosityForce = Eigen::Vector3f::Zero();
|
||||||
|
if (node->data.velocity == Eigen::Vector3f::Zero()) node->data.velocity = velGen();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Octree<fluidParticle>::NodeData>> neighbors = grid.findInRadius(point.second, config.SMOOTHING_RADIUS);
|
||||||
|
for (auto& neighbor : neighbors) {
|
||||||
|
Eigen::Vector3f rv = node->position - neighbor->position;
|
||||||
|
Eigen::Vector3f velDiff = neighbor->data.velocity - node->data.velocity;
|
||||||
|
float lapW = lapW_visc(rv, config.SMOOTHING_RADIUS);
|
||||||
|
viscosityForce += node->data.viscosity * neighbor->data.mass * (velDiff / neighbor->data.density) * lapW;
|
||||||
|
}
|
||||||
|
node->data.viscosityForce = viscosityForce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f applyMutualGravity(std::shared_ptr<Octree<fluidParticle>::NodeData>& node) {
|
||||||
|
Eigen::Vector3f gravityForce = Eigen::Vector3f::Zero();
|
||||||
|
std::vector<std::shared_ptr<Octree<fluidParticle>::NodeData>> neighbors = grid.findInRadius(node->position, config.SMOOTHING_RADIUS);
|
||||||
|
for (auto& neighbor : neighbors) {
|
||||||
|
if (node == neighbor) continue;
|
||||||
|
Eigen::Vector3f dir = neighbor->position - node->position;
|
||||||
|
float dist = dir.norm();
|
||||||
|
if (dist > EPSILON) {
|
||||||
|
dir.normalize();
|
||||||
|
float forceMag = (config.G_ATTRACTION * node->data.mass * neighbor->data.mass) / (dist * dist);
|
||||||
|
gravityForce += dir * forceMag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gravityForce;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f applyCenterGravity(std::shared_ptr<Octree<fluidParticle>::NodeData>& node) {
|
||||||
|
Eigen::Vector3f center = Eigen::Vector3f::Zero();
|
||||||
|
Eigen::Vector3f direction = center - node->position;
|
||||||
|
float distSq = direction.squaredNorm();
|
||||||
|
float dist = std::sqrt(distSq);
|
||||||
|
if (dist < 50.0f) {
|
||||||
|
dist = 50.0f;
|
||||||
|
distSq = 2500.0f;
|
||||||
|
}
|
||||||
|
direction /= dist;
|
||||||
|
float centerMass = 5000.0f;
|
||||||
|
float forceMag = (config.G_ATTRACTION * node->data.mass * centerMass) / distSq;
|
||||||
|
return direction * forceMag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyForce() {
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) continue;
|
||||||
|
Eigen::Vector3f internalForces = node->data.pressureForce + node->data.viscosityForce;
|
||||||
|
Eigen::Vector3f acceleration = (internalForces / node->data.mass);
|
||||||
|
Eigen::Vector3f gravity = applyMutualGravity(node);
|
||||||
|
gravity += applyCenterGravity(node);
|
||||||
|
node->data.forceAccumulator = (acceleration + gravity) / node->data.mass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void replaceLost() {
|
||||||
|
std::vector<size_t> idsToRemove;
|
||||||
|
int gridHalfSize = config.gridSizeCube / 2;
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
if (std::abs(point.second[0]) > gridHalfSize ||
|
||||||
|
std::abs(point.second[1]) > gridHalfSize ||
|
||||||
|
std::abs(point.second[2]) > gridHalfSize) {
|
||||||
|
idsToRemove.push_back(point.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t id : idsToRemove) {
|
||||||
|
grid.remove(idposMap[id]);
|
||||||
|
idposMap.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!idsToRemove.empty()) {
|
||||||
|
fluidParticle newParticles;
|
||||||
|
spawnParticles(newParticles, idsToRemove.size(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyPhysics() {
|
||||||
|
computeDensities();
|
||||||
|
applyPressure();
|
||||||
|
applyViscosity();
|
||||||
|
applyForce();
|
||||||
|
|
||||||
|
for (auto& point : idposMap) {
|
||||||
|
auto node = grid.find(point.second);
|
||||||
|
if (!node) continue;
|
||||||
|
Eigen::Matrix<float, 3, 1> acceleration = node->data.forceAccumulator;
|
||||||
|
node->data.velocity += acceleration * config.TIMESTEP;
|
||||||
|
Eigen::Matrix<float, 3, 1> newPos = point.second + (node->data.velocity);
|
||||||
|
Eigen::Vector3f oldPos = point.second;
|
||||||
|
if (grid.move(oldPos, newPos)) {
|
||||||
|
idposMap[point.first] = newPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replaceLost();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getParticleCount() const { return idposMap.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
1026
util/sim/planet.hpp
Normal file
1026
util/sim/planet.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
|||||||
#ifndef temp_hpp
|
|
||||||
#define temp_hpp
|
|
||||||
|
|
||||||
#include "../vectorlogic/vec2.hpp"
|
|
||||||
#include "../timing_decorator.hpp"
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class Temp {
|
|
||||||
private:
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static Vec2 findClosestPoint(const Vec2& position, std::unordered_map<Vec2, Temp> others) {
|
|
||||||
if (others.empty()) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto closest = others.begin();
|
|
||||||
float minDistance = position.distance(closest->first);
|
|
||||||
|
|
||||||
for (auto it = std::next(others.begin()); it != others.end(); ++it) {
|
|
||||||
float distance = position.distance(it->first);
|
|
||||||
if (distance < minDistance) {
|
|
||||||
minDistance = distance;
|
|
||||||
closest = it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return closest->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
float temp;
|
|
||||||
float conductivity = 0.5;
|
|
||||||
float specific_heat = 900.0;
|
|
||||||
float diffusivity = 2000.0;
|
|
||||||
|
|
||||||
Temp() : temp(0.0) {};
|
|
||||||
Temp(float temp) : temp(temp) {};
|
|
||||||
|
|
||||||
Temp(const Vec2& testPos, const std::unordered_map<Vec2, Temp>& others) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
float power = 2.0;
|
|
||||||
float num = 0.0;
|
|
||||||
float den = 0.0;
|
|
||||||
|
|
||||||
for (const auto& [point, tempObj] : others) {
|
|
||||||
float dist = testPos.distance(point);
|
|
||||||
float weight = 1.0 / std::pow(dist, power);
|
|
||||||
num += weight * tempObj.temp;
|
|
||||||
den += weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (den < 1e-10 && den > -1e-10) {
|
|
||||||
den = 1e-10;
|
|
||||||
}
|
|
||||||
this->temp = num / den;
|
|
||||||
}
|
|
||||||
|
|
||||||
static float calTempIDW(const Vec2& testPos, const std::unordered_map<Vec2, Temp>& others) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
float power = 2.0;
|
|
||||||
float num = 0.0;
|
|
||||||
float den = 0.0;
|
|
||||||
for (const auto& [point, temp] : others) {
|
|
||||||
float dist = testPos.distance(point);
|
|
||||||
|
|
||||||
float weight = 1.0 / std::pow(dist, power);
|
|
||||||
num += weight * temp.temp;
|
|
||||||
den += weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (den < 1e-10 && den > -1e-10) {
|
|
||||||
den = 1e-10;
|
|
||||||
}
|
|
||||||
return num / den;
|
|
||||||
}
|
|
||||||
|
|
||||||
void calLapl(const Vec2& testPos, const std::unordered_map<Vec2, Temp>& others, float deltaTime) {
|
|
||||||
//TIME_FUNCTION;
|
|
||||||
float dt = deltaTime;
|
|
||||||
float sumWeights = 0.0f;
|
|
||||||
float sumTempWeights = 0.0f;
|
|
||||||
float searchRadius = 25.0f;
|
|
||||||
|
|
||||||
for (const auto& [point, tempObj] : others) {
|
|
||||||
float dist = testPos.distance(point);
|
|
||||||
|
|
||||||
if (dist < 0.001f || dist > searchRadius) continue;
|
|
||||||
|
|
||||||
float weight = 1.0f / (dist * dist);
|
|
||||||
|
|
||||||
sumTempWeights += weight * tempObj.temp;
|
|
||||||
sumWeights += weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sumWeights < 1e-10f) return;
|
|
||||||
|
|
||||||
float equilibriumTemp = sumTempWeights / sumWeights;
|
|
||||||
|
|
||||||
float rate = this->diffusivity * 0.01f;
|
|
||||||
|
|
||||||
float lerpFactor = 1.0f - std::exp(-rate * dt);
|
|
||||||
|
|
||||||
this->temp += (equilibriumTemp - this->temp) * lerpFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
#ifndef WATER_HPP
|
|
||||||
#define WATER_HPP
|
|
||||||
|
|
||||||
#include "../vectorlogic/vec2.hpp"
|
|
||||||
#include "../vectorlogic/vec3.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
// Water constants (SI units: Kelvin, Pascals, Meters)
|
|
||||||
struct WaterConstants {
|
|
||||||
// Thermodynamic properties at STP (Standard Temperature and Pressure)
|
|
||||||
static constexpr float STANDARD_TEMPERATURE = 293.15f;
|
|
||||||
static constexpr float STANDARD_PRESSURE = 101325.0f;
|
|
||||||
static constexpr float FREEZING_POINT = 273.15f;
|
|
||||||
static constexpr float BOILING_POINT = 373.15f;
|
|
||||||
|
|
||||||
// Reference densities (kg/m³)
|
|
||||||
static constexpr float DENSITY_STP = 998.0f;
|
|
||||||
static constexpr float DENSITY_0C = 999.8f;
|
|
||||||
static constexpr float DENSITY_4C = 1000.0f;
|
|
||||||
|
|
||||||
// Viscosity reference values (Pa·s)
|
|
||||||
static constexpr float VISCOSITY_0C = 0.001792f;
|
|
||||||
static constexpr float VISCOSITY_20C = 0.001002f;
|
|
||||||
static constexpr float VISCOSITY_100C = 0.000282f;
|
|
||||||
|
|
||||||
// Thermal properties
|
|
||||||
static constexpr float SPECIFIC_HEAT_CAPACITY = 4182.0f;
|
|
||||||
static constexpr float THERMAL_CONDUCTIVITY = 0.598f;
|
|
||||||
static constexpr float LATENT_HEAT_VAPORIZATION = 2257000.0f;
|
|
||||||
static constexpr float LATENT_HEAT_FUSION = 334000.0f;
|
|
||||||
|
|
||||||
// Other physical constants
|
|
||||||
static constexpr float SURFACE_TENSION = 0.0728f;
|
|
||||||
static constexpr float SPEED_OF_SOUND = 1482.0f;
|
|
||||||
static constexpr float BULK_MODULUS = 2.15e9f;
|
|
||||||
};
|
|
||||||
|
|
||||||
class WaterThermodynamics {
|
|
||||||
public:
|
|
||||||
// Calculate density based on temperature (empirical relationship for 0-100°C)
|
|
||||||
static float calculateDensity(float temperature_K) {
|
|
||||||
// Empirical formula for pure water density vs temperature
|
|
||||||
float T = temperature_K - 273.15f; // Convert to Celsius for empirical formulas
|
|
||||||
|
|
||||||
if (T <= 0.0f) return WaterConstants::DENSITY_0C;
|
|
||||||
if (T >= 100.0f) return 958.4f; // Density at 100°C
|
|
||||||
|
|
||||||
// Polynomial approximation for 0-100°C range
|
|
||||||
return 1000.0f * (1.0f - (T + 288.9414f) * (T - 3.9863f) * (T - 3.9863f) /
|
|
||||||
(508929.2f * (T + 68.12963f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate dynamic viscosity based on temperature (using Vogel-Fulcher-Tammann equation)
|
|
||||||
static float calculateViscosity(float temperature_K) {
|
|
||||||
float T = temperature_K;
|
|
||||||
// Vogel-Fulcher-Tammann equation parameters for water
|
|
||||||
constexpr float A = -3.7188f;
|
|
||||||
constexpr float B = 578.919f;
|
|
||||||
constexpr float C = -137.546f;
|
|
||||||
|
|
||||||
return 0.001f * std::exp(A + B / (T - C)); // Returns in Pa·s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate viscosity using simpler Arrhenius-type equation (good for 0-100°C)
|
|
||||||
static float calculateViscositySimple(float temperature_K) {
|
|
||||||
float T = temperature_K - 273.15f; // Celsius
|
|
||||||
|
|
||||||
if (T <= 0.0f) return WaterConstants::VISCOSITY_0C;
|
|
||||||
if (T >= 100.0f) return WaterConstants::VISCOSITY_100C;
|
|
||||||
|
|
||||||
// Simple exponential decay model for 0-100°C range
|
|
||||||
return 0.001792f * std::exp(-0.024f * T);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate thermal conductivity (W/(m·K))
|
|
||||||
static float calculateThermalConductivity(float temperature_K) {
|
|
||||||
float T = temperature_K - 273.15f; // Celsius
|
|
||||||
// Linear approximation for 0-100°C
|
|
||||||
return 0.561f + 0.002f * T - 0.00001f * T * T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate surface tension (N/m)
|
|
||||||
static float calculateSurfaceTension(float temperature_K) {
|
|
||||||
float T = temperature_K - 273.15f; // Celsius
|
|
||||||
// Linear decrease with temperature
|
|
||||||
return 0.07564f - 0.000141f * T - 0.00000025f * T * T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate speed of sound in water (m/s)
|
|
||||||
static float calculateSpeedOfSound(float temperature_K, float pressure_Pa = WaterConstants::STANDARD_PRESSURE) {
|
|
||||||
float T = temperature_K - 273.15f; // Celsius
|
|
||||||
// Empirical formula for pure water
|
|
||||||
return 1402.5f + 5.0f * T - 0.055f * T * T + 0.0003f * T * T * T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate bulk modulus (compressibility) in Pa
|
|
||||||
static float calculateBulkModulus(float temperature_K, float pressure_Pa = WaterConstants::STANDARD_PRESSURE) {
|
|
||||||
float T = temperature_K - 273.15f; // Celsius
|
|
||||||
// Approximation - decreases slightly with temperature
|
|
||||||
return WaterConstants::BULK_MODULUS * (1.0f - 0.0001f * T);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if water should change phase
|
|
||||||
static bool isFrozen(float temperature_K, float pressure_Pa = WaterConstants::STANDARD_PRESSURE) {
|
|
||||||
return temperature_K <= WaterConstants::FREEZING_POINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isBoiling(float temperature_K, float pressure_Pa = WaterConstants::STANDARD_PRESSURE) {
|
|
||||||
// Simple boiling point calculation (neglecting pressure effects for simplicity)
|
|
||||||
return temperature_K >= WaterConstants::BOILING_POINT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WaterParticle {
|
|
||||||
Vec3 velocity;
|
|
||||||
Vec3 acceleration;
|
|
||||||
Vec3 force;
|
|
||||||
|
|
||||||
float temperature;
|
|
||||||
float pressure;
|
|
||||||
float density;
|
|
||||||
float mass;
|
|
||||||
float viscosity;
|
|
||||||
|
|
||||||
float volume;
|
|
||||||
float energy;
|
|
||||||
|
|
||||||
WaterParticle(float percent = 1.0f, float temp_K = WaterConstants::STANDARD_TEMPERATURE)
|
|
||||||
: velocity(0, 0, 0), acceleration(0, 0, 0), force(0, 0, 0),
|
|
||||||
temperature(temp_K), pressure(WaterConstants::STANDARD_PRESSURE),
|
|
||||||
volume(1.0f * percent) {
|
|
||||||
|
|
||||||
updateThermodynamicProperties();
|
|
||||||
|
|
||||||
// Mass is density × volume
|
|
||||||
mass = density * volume;
|
|
||||||
energy = mass * WaterConstants::SPECIFIC_HEAT_CAPACITY * temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update all temperature-dependent properties
|
|
||||||
void updateThermodynamicProperties() {
|
|
||||||
density = WaterThermodynamics::calculateDensity(temperature);
|
|
||||||
viscosity = WaterThermodynamics::calculateViscosity(temperature);
|
|
||||||
|
|
||||||
// If we have a fixed mass, adjust volume for density changes
|
|
||||||
if (mass > 0.0f) {
|
|
||||||
volume = mass / density;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add thermal energy and update temperature
|
|
||||||
void addThermalEnergy(float energy_joules) {
|
|
||||||
energy += energy_joules;
|
|
||||||
temperature = energy / (mass * WaterConstants::SPECIFIC_HEAT_CAPACITY);
|
|
||||||
updateThermodynamicProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set temperature directly
|
|
||||||
void setTemperature(float temp_K) {
|
|
||||||
temperature = temp_K;
|
|
||||||
energy = mass * WaterConstants::SPECIFIC_HEAT_CAPACITY * temperature;
|
|
||||||
updateThermodynamicProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check phase state
|
|
||||||
bool isFrozen() const { return WaterThermodynamics::isFrozen(temperature, pressure); }
|
|
||||||
bool isBoiling() const { return WaterThermodynamics::isBoiling(temperature, pressure); }
|
|
||||||
bool isLiquid() const { return !isFrozen() && !isBoiling(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef TIMING_CPP
|
||||||
|
#define TIMING_CPP
|
||||||
|
|
||||||
#include "timing_decorator.hpp"
|
#include "timing_decorator.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -113,3 +116,5 @@ void FunctionTimer::printStats(Mode mode) {
|
|||||||
void FunctionTimer::clearStats() {
|
void FunctionTimer::clearStats() {
|
||||||
stats_.clear();
|
stats_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,11 +5,16 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
class Vec2 {
|
class Vec2 {
|
||||||
public:
|
public:
|
||||||
float x, y;
|
T x, y;
|
||||||
|
|
||||||
Vec2() : x(0), y(0) {}
|
Vec2() : x(0), y(0) {}
|
||||||
Vec2(float x, float y) : x(x), y(y) {}
|
Vec2(T x, T y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
explicit Vec2(const Vec2<U>& other) : x(static_cast<T>(other.x)), y(static_cast<T>(other.y)) {}
|
||||||
|
|
||||||
Vec2& move(const Vec2 newpos) {
|
Vec2& move(const Vec2 newpos) {
|
||||||
x = newpos.x;
|
x = newpos.x;
|
||||||
@@ -33,11 +38,13 @@ class Vec2 {
|
|||||||
return Vec2(x / other.x, y / other.y);
|
return Vec2(x / other.x, y / other.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 operator+(float scalar) const {
|
template<typename U>
|
||||||
|
Vec2 operator+(U scalar) const {
|
||||||
return Vec2(x + scalar, y + scalar);
|
return Vec2(x + scalar, y + scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 operator-(float scalar) const {
|
template<typename U>
|
||||||
|
Vec2 operator-(U scalar) const {
|
||||||
return Vec2(x - scalar, y - scalar);
|
return Vec2(x - scalar, y - scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +52,19 @@ class Vec2 {
|
|||||||
return Vec2(-x, -y);
|
return Vec2(-x, -y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 operator*(float scalar) const {
|
template<typename U>
|
||||||
|
Vec2 operator*(U scalar) const {
|
||||||
return Vec2(x * scalar, y * scalar);
|
return Vec2(x * scalar, y * scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 operator/(float scalar) const {
|
template<typename U>
|
||||||
|
Vec2 operator/(U scalar) const {
|
||||||
return Vec2(x / scalar, y / scalar);
|
return Vec2(x / scalar, y / scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2& operator=(float scalar) {
|
template<typename U>
|
||||||
x = y = scalar;
|
Vec2& operator=(U scalar) {
|
||||||
|
x = y = static_cast<T>(scalar);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,57 +92,64 @@ class Vec2 {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2& operator+=(float scalar) {
|
template<typename U>
|
||||||
|
Vec2& operator+=(U scalar) {
|
||||||
x += scalar;
|
x += scalar;
|
||||||
y += scalar;
|
y += scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2& operator-=(float scalar) {
|
template<typename U>
|
||||||
|
Vec2& operator-=(U scalar) {
|
||||||
x -= scalar;
|
x -= scalar;
|
||||||
y -= scalar;
|
y -= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2& operator*=(float scalar) {
|
template<typename U>
|
||||||
|
Vec2& operator*=(U scalar) {
|
||||||
x *= scalar;
|
x *= scalar;
|
||||||
y *= scalar;
|
y *= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2& operator/=(float scalar) {
|
template<typename U>
|
||||||
|
Vec2& operator/=(U scalar) {
|
||||||
x /= scalar;
|
x /= scalar;
|
||||||
y /= scalar;
|
y /= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
float dot(const Vec2& other) const {
|
T dot(const Vec2& other) const {
|
||||||
return x * other.x + y * other.y;
|
return x * other.x + y * other.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
float length() const {
|
template<typename U = float>
|
||||||
return std::sqrt(x * x + y * y);
|
U length() const {
|
||||||
|
return std::sqrt(static_cast<U>(x * x + y * y));
|
||||||
}
|
}
|
||||||
|
|
||||||
float lengthSquared() const {
|
T lengthSquared() const {
|
||||||
return x * x + y * y;
|
return x * x + y * y;
|
||||||
}
|
}
|
||||||
|
|
||||||
float distance(const Vec2& other) const {
|
template<typename U = float>
|
||||||
return (*this - other).length();
|
U distance(const Vec2& other) const {
|
||||||
|
return (*this - other).template length<U>();
|
||||||
}
|
}
|
||||||
|
|
||||||
float distanceSquared(const Vec2& other) const {
|
T distanceSquared(const Vec2& other) const {
|
||||||
Vec2 diff = *this - other;
|
Vec2 diff = *this - other;
|
||||||
return diff.x * diff.x + diff.y * diff.y;
|
return diff.x * diff.x + diff.y * diff.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 normalized() const {
|
template<typename U = float>
|
||||||
float len = length();
|
Vec2<U> normalized() const {
|
||||||
|
auto len = length<U>();
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
return *this / len;
|
return Vec2<U>(static_cast<U>(x) / len, static_cast<U>(y) / len);
|
||||||
}
|
}
|
||||||
return *this;
|
return Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Vec2& other) const {
|
bool operator==(const Vec2& other) const {
|
||||||
@@ -190,87 +207,90 @@ class Vec2 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 clamp(float minVal, float maxVal) const {
|
template<typename U>
|
||||||
|
Vec2 clamp(U minVal, U maxVal) const {
|
||||||
return Vec2(
|
return Vec2(
|
||||||
std::clamp(x, minVal, maxVal),
|
std::clamp(x, static_cast<T>(minVal), static_cast<T>(maxVal)),
|
||||||
std::clamp(y, minVal, maxVal)
|
std::clamp(y, static_cast<T>(minVal), static_cast<T>(maxVal))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isZero(float epsilon = 1e-10f) const {
|
template<typename U = float>
|
||||||
return std::abs(x) < epsilon && std::abs(y) < epsilon;
|
bool isZero(U epsilon = static_cast<U>(1e-10)) const {
|
||||||
|
return std::abs(static_cast<U>(x)) < epsilon &&
|
||||||
|
std::abs(static_cast<U>(y)) < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool equals(const Vec2& other, float epsilon = 1e-10f) const {
|
template<typename U = float>
|
||||||
return std::abs(x - other.x) < epsilon &&
|
bool equals(const Vec2& other, U epsilon = static_cast<U>(1e-10)) const {
|
||||||
std::abs(y - other.y) < epsilon;
|
return std::abs(static_cast<U>(x - other.x)) < epsilon &&
|
||||||
}
|
std::abs(static_cast<U>(y - other.y)) < epsilon;
|
||||||
|
|
||||||
friend Vec2 operator+(float scalar, const Vec2& vec) {
|
|
||||||
return Vec2(scalar + vec.x, scalar + vec.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Vec2 operator-(float scalar, const Vec2& vec) {
|
|
||||||
return Vec2(scalar - vec.x, scalar - vec.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Vec2 operator*(float scalar, const Vec2& vec) {
|
|
||||||
return Vec2(scalar * vec.x, scalar * vec.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Vec2 operator/(float scalar, const Vec2& vec) {
|
|
||||||
return Vec2(scalar / vec.x, scalar / vec.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 perpendicular() const {
|
Vec2 perpendicular() const {
|
||||||
return Vec2(-y, x);
|
return Vec2(-y, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 reflect(const Vec2& normal) const {
|
template<typename U = float>
|
||||||
return *this - 2.0f * this->dot(normal) * normal;
|
Vec2<U> reflect(const Vec2<U>& normal) const {
|
||||||
|
auto this_f = Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
|
return this_f - static_cast<U>(2.0) * this_f.dot(normal) * normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 lerp(const Vec2& other, float t) const {
|
template<typename U = float>
|
||||||
t = std::clamp(t, 0.0f, 1.0f);
|
Vec2<U> lerp(const Vec2<U>& other, U t) const {
|
||||||
return *this + (other - *this) * t;
|
t = std::clamp(t, static_cast<U>(0.0), static_cast<U>(1.0));
|
||||||
|
auto this_f = Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
|
return this_f + (other - this_f) * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 slerp(const Vec2& other, float t) const {
|
template<typename U = float>
|
||||||
t = std::clamp(t, 0.0f, 1.0f);
|
Vec2<U> slerp(const Vec2<U>& other, U t) const {
|
||||||
float dot = this->dot(other);
|
t = std::clamp(t, static_cast<U>(0.0), static_cast<U>(1.0));
|
||||||
dot = std::clamp(dot, -1.0f, 1.0f);
|
auto this_f = Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
|
U dot = this_f.dot(other);
|
||||||
|
dot = std::clamp(dot, static_cast<U>(-1.0), static_cast<U>(1.0));
|
||||||
|
|
||||||
float theta = std::acos(dot) * t;
|
U theta = std::acos(dot) * t;
|
||||||
Vec2 relative = other - *this * dot;
|
auto relative = other - this_f * dot;
|
||||||
relative = relative.normalized();
|
relative = relative.normalized();
|
||||||
|
|
||||||
return (*this * std::cos(theta)) + (relative * std::sin(theta));
|
return (this_f * std::cos(theta)) + (relative * std::sin(theta));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 rotate(float angle) const {
|
template<typename U = float>
|
||||||
float cosA = std::cos(angle);
|
Vec2<U> rotate(U angle) const {
|
||||||
float sinA = std::sin(angle);
|
U cosA = std::cos(angle);
|
||||||
return Vec2(x * cosA - y * sinA, x * sinA + y * cosA);
|
U sinA = std::sin(angle);
|
||||||
|
return Vec2<U>(
|
||||||
|
static_cast<U>(x) * cosA - static_cast<U>(y) * sinA,
|
||||||
|
static_cast<U>(x) * sinA + static_cast<U>(y) * cosA
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
float angle() const {
|
template<typename U = float>
|
||||||
return std::atan2(y, x);
|
U angle() const {
|
||||||
|
return std::atan2(static_cast<U>(y), static_cast<U>(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
float angleTo(const Vec2& other) const {
|
template<typename U = float>
|
||||||
return std::acos(this->dot(other) / (this->length() * other.length()));
|
U angleTo(const Vec2<U>& other) const {
|
||||||
|
auto this_f = Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
|
return std::acos(this_f.dot(other) / (this_f.length() * other.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
float directionTo(const Vec2& other) const {
|
template<typename U = float>
|
||||||
Vec2 direction = other - *this;
|
U directionTo(const Vec2<U>& other) const {
|
||||||
|
auto this_f = Vec2<U>(static_cast<U>(x), static_cast<U>(y));
|
||||||
|
auto direction = other - this_f;
|
||||||
return direction.angle();
|
return direction.angle();
|
||||||
}
|
}
|
||||||
|
|
||||||
float& operator[](int index) {
|
T& operator[](int index) {
|
||||||
return (&x)[index];
|
return (&x)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
const float& operator[](int index) const {
|
const T& operator[](int index) const {
|
||||||
return (&x)[index];
|
return (&x)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,23 +305,62 @@ class Vec2 {
|
|||||||
|
|
||||||
struct Hash {
|
struct Hash {
|
||||||
std::size_t operator()(const Vec2& v) const {
|
std::size_t operator()(const Vec2& v) const {
|
||||||
return std::hash<float>()(v.x) ^ (std::hash<float>()(v.y) << 1);
|
return std::hash<T>()(v.x) ^ (std::hash<T>()(v.y) << 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
float aspect() {
|
||||||
|
return static_cast<float>(x) / static_cast<float>(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<float> toFloat() {
|
||||||
|
return Vec2<float>(static_cast<float>(x), static_cast<float>(y));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Vec2& vec) {
|
template<typename T>
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Vec2<T>& vec) {
|
||||||
os << vec.toString();
|
os << vec.toString();
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
auto operator+(U scalar, const Vec2<T>& vec) -> Vec2<decltype(scalar + vec.x)> {
|
||||||
|
using ResultType = decltype(scalar + vec.x);
|
||||||
|
return Vec2<ResultType>(scalar + vec.x, scalar + vec.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
auto operator-(U scalar, const Vec2<T>& vec) -> Vec2<decltype(scalar - vec.x)> {
|
||||||
|
using ResultType = decltype(scalar - vec.x);
|
||||||
|
return Vec2<ResultType>(scalar - vec.x, scalar - vec.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
auto operator*(U scalar, const Vec2<T>& vec) -> Vec2<decltype(scalar * vec.x)> {
|
||||||
|
using ResultType = decltype(scalar * vec.x);
|
||||||
|
return Vec2<ResultType>(scalar * vec.x, scalar * vec.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
auto operator/(U scalar, const Vec2<T>& vec) -> Vec2<decltype(scalar / vec.x)> {
|
||||||
|
using ResultType = decltype(scalar / vec.x);
|
||||||
|
return Vec2<ResultType>(scalar / vec.x, scalar / vec.y);
|
||||||
|
}
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template<>
|
template<typename T>
|
||||||
struct hash<Vec2> {
|
struct hash<Vec2<T>> {
|
||||||
size_t operator()(const Vec2& v) const {
|
size_t operator()(const Vec2<T>& v) const {
|
||||||
return hash<float>()(v.x) ^ (hash<float>()(v.y) << 1);
|
return hash<T>()(v.x) ^ (hash<T>()(v.y) << 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using Vec2f = Vec2<float>;
|
||||||
|
using Vec2d = Vec2<double>;
|
||||||
|
using Vec2i = Vec2<int>;
|
||||||
|
using Vec2u = Vec2<unsigned int>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5,18 +5,30 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdfloat>
|
||||||
|
#include <cstring>
|
||||||
|
#include "vec2.hpp"
|
||||||
|
#include "../basicdefines.hpp"
|
||||||
|
|
||||||
|
#ifdef __SSE__
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Vec3 {
|
class alignas(16) Vec3 {
|
||||||
public:
|
public:
|
||||||
struct{ T x, y, z; };
|
struct{ T x, y, z; };
|
||||||
|
|
||||||
Vec3() : x(0), y(0), z(0) {}
|
Vec3() : x(0), y(0), z(0) {}
|
||||||
Vec3(T x, T y, T z) : x(x), y(y), z(z) {}
|
Vec3(T x, T y, T z) : x(x), y(y), z(z) {}
|
||||||
Vec3(T scalar) : x(scalar), y(scalar), z(scalar) {}
|
Vec3(T scalar) : x(scalar), y(scalar), z(scalar) {}
|
||||||
Vec3(const T acd[3]) : x(acd[0]), y(acd[1]), z(acd[2]) {}
|
Vec3(float acd[3]) : x(acd[0]), y(acd[1]), z(acd[2]) {}
|
||||||
|
template<typename U>
|
||||||
|
Vec3(const Vec3<U>& other) : x(static_cast<T>(other.x)), y(static_cast<T>(other.y)), z(static_cast<T>(other.z)) {}
|
||||||
|
|
||||||
Vec3(const class Vec2& vec2, T z = 0);
|
template<typename U>
|
||||||
|
Vec3(const class Vec2<U>& vec2, U z = 0) : x(static_cast<T>(vec2.x)), y(static_cast<T>(vec2.y)), z(static_cast<T>(z)) {}
|
||||||
|
|
||||||
Vec3& move(const Vec3& newpos) {
|
Vec3& move(const Vec3& newpos) {
|
||||||
x = newpos.x;
|
x = newpos.x;
|
||||||
@@ -26,19 +38,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Arithmetic operations
|
// Arithmetic operations
|
||||||
Vec3 operator+(const Vec3& other) const {
|
template<typename U>
|
||||||
|
Vec3 operator+(const Vec3<U>& other) const {
|
||||||
return Vec3(x + other.x, y + other.y, z + other.z);
|
return Vec3(x + other.x, y + other.y, z + other.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 operator-(const Vec3& other) const {
|
template<typename U>
|
||||||
|
Vec3 operator-(const Vec3<U>& other) const {
|
||||||
return Vec3(x - other.x, y - other.y, z - other.z);
|
return Vec3(x - other.x, y - other.y, z - other.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 operator*(const Vec3& other) const {
|
template<typename U>
|
||||||
|
Vec3 operator*(const Vec3<U>& other) const {
|
||||||
return Vec3(x * other.x, y * other.y, z * other.z);
|
return Vec3(x * other.x, y * other.y, z * other.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 operator/(const Vec3& other) const {
|
template<typename U>
|
||||||
|
Vec3 operator/(const Vec3<U>& other) const {
|
||||||
return Vec3(x / other.x, y / other.y, z / other.z);
|
return Vec3(x / other.x, y / other.y, z / other.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +75,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec3 operator/(T scalar) const {
|
Vec3 operator/(T scalar) const {
|
||||||
return Vec3(x / scalar, y / scalar, z / scalar);
|
T invScalar = T(1) / scalar;
|
||||||
|
return Vec3(x * invScalar, y * invScalar, z * invScalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3& operator=(T scalar) {
|
Vec3& operator=(T scalar) {
|
||||||
@@ -117,9 +134,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec3& operator/=(T scalar) {
|
Vec3& operator/=(T scalar) {
|
||||||
x /= scalar;
|
T invScalar = T(1) / scalar;
|
||||||
y /= scalar;
|
x *= invScalar;
|
||||||
z /= scalar;
|
y *= invScalar;
|
||||||
|
z *= invScalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +145,7 @@ public:
|
|||||||
return x * other.x + y * other.y + z * other.z;
|
return x * other.x + y * other.y + z * other.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3& cross(const Vec3& other) const {
|
Vec3 cross(const Vec3& other) const {
|
||||||
return Vec3(
|
return Vec3(
|
||||||
y * other.z - z * other.y,
|
y * other.z - z * other.y,
|
||||||
z * other.x - x * other.z,
|
z * other.x - x * other.z,
|
||||||
@@ -136,7 +154,31 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
T length() const {
|
T length() const {
|
||||||
return static_cast<T>(std::sqrt(static_cast<double>(x * x + y * y + z * z)));
|
return std::sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast inverse length (Quake III algorithm)
|
||||||
|
T invLength() const {
|
||||||
|
const T lenSq = x * x + y * y + z * z;
|
||||||
|
if (lenSq == 0) return 0;
|
||||||
|
|
||||||
|
// Fast inverse square root approximation
|
||||||
|
const T half = T(0.5) * lenSq;
|
||||||
|
T o = lenSq;
|
||||||
|
|
||||||
|
// Type punning for float/double
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
long i = *(long*)&o;
|
||||||
|
i = 0x5f3759df - (i >> 1);
|
||||||
|
o = *(float*)&i;
|
||||||
|
} else if constexpr (std::is_same_v<T, double>) {
|
||||||
|
long long i = *(long long*)&o;
|
||||||
|
i = 0x5fe6eb50c7b537a9 - (i >> 1);
|
||||||
|
o = *(double*)&i;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = o * (T(1.5) - (half * o * o));
|
||||||
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
T lengthSquared() const {
|
T lengthSquared() const {
|
||||||
@@ -149,13 +191,28 @@ public:
|
|||||||
|
|
||||||
T distanceSquared(const Vec3& other) const {
|
T distanceSquared(const Vec3& other) const {
|
||||||
Vec3 diff = *this - other;
|
Vec3 diff = *this - other;
|
||||||
return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
|
return diff.lengthSquared();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalized with SSE optimization
|
||||||
Vec3 normalized() const {
|
Vec3 normalized() const {
|
||||||
T len = length();
|
const T invLen = invLength();
|
||||||
if (len > 0) {
|
if (invLen > 0) {
|
||||||
return *this / len;
|
#ifdef __SSE__
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
__m128 vec = _mm_set_ps(0.0f, z, y, x);
|
||||||
|
__m128 inv = _mm_set1_ps(invLen);
|
||||||
|
__m128 result = _mm_mul_ps(vec, inv);
|
||||||
|
|
||||||
|
alignas(16) float components[4];
|
||||||
|
_mm_store_ps(components, result);
|
||||||
|
return Vec3(components[0], components[1], components[2]);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// Fallback to scalar operations
|
||||||
|
return Vec3(x * invLen, y * invLen, z * invLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -169,27 +226,77 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const Vec3& other) const {
|
bool operator<(const Vec3& other) const {
|
||||||
return (x < other.x) ||
|
return (lengthSquared() < other.lengthSquared());
|
||||||
(x == other.x && y < other.y) ||
|
}
|
||||||
(x == other.x && y == other.y && z < other.z);
|
|
||||||
|
bool operator<(T scalar) const {
|
||||||
|
return (x < scalar && y < scalar && z < scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<=(const Vec3& other) const {
|
bool operator<=(const Vec3& other) const {
|
||||||
return (x < other.x) ||
|
return (lengthSquared() <= other.lengthSquared());
|
||||||
(x == other.x && y < other.y) ||
|
}
|
||||||
(x == other.x && y == other.y && z <= other.z);
|
|
||||||
|
bool operator<=(T scalar) const {
|
||||||
|
return (x <= scalar && y <= scalar && z <= scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator>(const Vec3& other) const {
|
bool operator>(const Vec3& other) const {
|
||||||
return (x > other.x) ||
|
return (lengthSquared() > other.lengthSquared());
|
||||||
(x == other.x && y > other.y) ||
|
}
|
||||||
(x == other.x && y == other.y && z > other.z);
|
|
||||||
|
bool operator>(T scalar) const {
|
||||||
|
return (x > scalar && y > scalar && z > scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator>=(const Vec3& other) const {
|
bool operator>=(const Vec3& other) const {
|
||||||
return (x > other.x) ||
|
return (lengthSquared() >= other.lengthSquared());
|
||||||
(x == other.x && y > other.y) ||
|
}
|
||||||
(x == other.x && y == other.y && z >= other.z);
|
|
||||||
|
bool operator>=(T scalar) const {
|
||||||
|
return (x >= scalar && y >= scalar && z >= scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllLT(const Vec3& other) const {
|
||||||
|
return x < other.x && y < other.y && z < other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllGT(const Vec3& other) const {
|
||||||
|
return x > other.x && y > other.y && z > other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllLTE(const Vec3& other) const {
|
||||||
|
return x <= other.x && y <= other.y && z <= other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllGTE(const Vec3& other) const {
|
||||||
|
return x >= other.x && y >= other.y && z >= other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyLT(const Vec3& other) const {
|
||||||
|
return x < other.x || y < other.y || z < other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyGT(const Vec3& other) const {
|
||||||
|
return x > other.x || y > other.y || z > other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyLTE(const Vec3& other) const {
|
||||||
|
return x <= other.x || y <= other.y || z <= other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyGTE(const Vec3& other) const {
|
||||||
|
return x >= other.x || y >= other.y || z >= other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompareFunc>
|
||||||
|
Vec3<bool> mask(CompareFunc comp, T value) const {
|
||||||
|
return Vec3<bool>(comp(x, value), comp(y, value), comp(z, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompareFunc>
|
||||||
|
Vec3<bool> mask(CompareFunc comp, const Vec3& other) const {
|
||||||
|
return Vec3<bool>(comp(x, other.x), comp(y, other.y), comp(z, other.z));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 abs() const {
|
Vec3 abs() const {
|
||||||
@@ -200,6 +307,26 @@ public:
|
|||||||
return Vec3(std::floor(x), std::floor(y), std::floor(z));
|
return Vec3(std::floor(x), std::floor(y), std::floor(z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec3<int> floorToI() const {
|
||||||
|
return Vec3<int>(static_cast<int>(std::floor(x)), static_cast<int>(std::floor(y)), static_cast<int>(std::floor(z)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3<uint8_t> floorToI8() const {
|
||||||
|
return Vec3<uint8_t>(static_cast<uint8_t>(std::max(T(0), std::floor(x))), static_cast<uint8_t>(std::max(T(0), std::floor(y))), static_cast<uint8_t>(std::max(T(0), std::floor(z))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3<size_t> floorToT() const {
|
||||||
|
return Vec3<size_t>(static_cast<size_t>(std::max(T(0), std::floor(x))), static_cast<size_t>(std::max(T(0), std::floor(y))), static_cast<size_t>(std::max(T(0), std::floor(z))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3<float> toFloat() const {
|
||||||
|
return Vec3<float>(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3<double> toDouble() const {
|
||||||
|
return Vec3<double>(static_cast<double>(x), static_cast<double>(y), static_cast<double>(z));
|
||||||
|
}
|
||||||
|
|
||||||
Vec3 ceil() const {
|
Vec3 ceil() const {
|
||||||
return Vec3(std::ceil(x), std::ceil(y), std::ceil(z));
|
return Vec3(std::ceil(x), std::ceil(y), std::ceil(z));
|
||||||
}
|
}
|
||||||
@@ -217,23 +344,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec3 clamp(const Vec3& minVal, const Vec3& maxVal) const {
|
Vec3 clamp(const Vec3& minVal, const Vec3& maxVal) const {
|
||||||
return Vec3(
|
return this->max(minVal).min(maxVal);
|
||||||
std::clamp(x, minVal.x, maxVal.x),
|
|
||||||
std::clamp(y, minVal.y, maxVal.y),
|
|
||||||
std::clamp(z, minVal.z, maxVal.z)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 clamp(T minVal, T maxVal) const {
|
Vec3 clamp(T minVal, T maxVal) const {
|
||||||
return Vec3(
|
return this->max(Vec3(minVal)).min(Vec3(maxVal));
|
||||||
std::clamp(x, minVal, maxVal),
|
|
||||||
std::clamp(y, minVal, maxVal),
|
|
||||||
std::clamp(z, minVal, maxVal)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isZero(float epsilon = 1e-10f) const {
|
bool isZero() const {
|
||||||
return std::abs(x) < epsilon && std::abs(y) < epsilon && std::abs(z) < epsilon;
|
return length() < EPSILON;
|
||||||
|
//return std::abs(x) < epsilon && std::abs(y) < epsilon && std::abs(z) < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool equals(const Vec3& other, float epsilon = 1e-10f) const {
|
bool equals(const Vec3& other, float epsilon = 1e-10f) const {
|
||||||
@@ -242,33 +362,28 @@ public:
|
|||||||
std::abs(z - other.z) < epsilon;
|
std::abs(z - other.z) < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template friend operators to allow different scalar types
|
friend Vec3<T> operator+(float scalar, const Vec3<T>& vec) {
|
||||||
template<typename S>
|
return Vec3<T>(static_cast<T>(scalar + vec.x),
|
||||||
friend Vec3<T> operator+(S scalar, const Vec3<T>& vec) {
|
static_cast<T>(scalar + vec.y),
|
||||||
return Vec3<T>(static_cast<T>(scalar) + vec.x,
|
static_cast<T>(scalar + vec.z));
|
||||||
static_cast<T>(scalar) + vec.y,
|
|
||||||
static_cast<T>(scalar) + vec.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename S>
|
friend Vec3<T> operator-(float scalar, const Vec3<T>& vec) {
|
||||||
friend Vec3<T> operator-(S scalar, const Vec3<T>& vec) {
|
return Vec3<T>(static_cast<T>(scalar - vec.x),
|
||||||
return Vec3<T>(static_cast<T>(scalar) - vec.x,
|
static_cast<T>(scalar - vec.y),
|
||||||
static_cast<T>(scalar) - vec.y,
|
static_cast<T>(scalar - vec.z));
|
||||||
static_cast<T>(scalar) - vec.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename S>
|
friend Vec3<T> operator*(float scalar, const Vec3<T>& vec) {
|
||||||
friend Vec3<T> operator*(S scalar, const Vec3<T>& vec) {
|
return Vec3<T>(static_cast<T>(scalar * vec.x),
|
||||||
return Vec3<T>(static_cast<T>(scalar) * vec.x,
|
static_cast<T>(scalar * vec.y),
|
||||||
static_cast<T>(scalar) * vec.y,
|
static_cast<T>(scalar * vec.z));
|
||||||
static_cast<T>(scalar) * vec.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename S>
|
friend Vec3<T> operator/(float scalar, const Vec3<T>& vec) {
|
||||||
friend Vec3<T> operator/(S scalar, const Vec3<T>& vec) {
|
return Vec3<T>(static_cast<T>(scalar / vec.x),
|
||||||
return Vec3<T>(static_cast<T>(scalar) / vec.x,
|
static_cast<T>(scalar / vec.y),
|
||||||
static_cast<T>(scalar) / vec.y,
|
static_cast<T>(scalar / vec.z));
|
||||||
static_cast<T>(scalar) / vec.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 reflect(const Vec3& normal) const {
|
Vec3 reflect(const Vec3& normal) const {
|
||||||
@@ -276,37 +391,49 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec3 lerp(const Vec3& other, T t) const {
|
Vec3 lerp(const Vec3& other, T t) const {
|
||||||
t = std::clamp(t, 0.0f, 1.0f);
|
t = std::clamp(t, T(0), T(1));
|
||||||
return *this + (other - *this) * t;
|
return *this + (other - *this) * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 slerp(const Vec3& other, T t) const {
|
Vec3 fastLerp(const Vec3& other, T t) const {
|
||||||
t = std::clamp(t, 0.0f, 1.0f);
|
return *this + (other - *this) * t;
|
||||||
T dot = this->dot(other);
|
}
|
||||||
dot = std::clamp(dot, -1.0f, 1.0f);
|
|
||||||
|
|
||||||
T theta = std::acos(dot) * t;
|
Vec3 fmaLerp(const Vec3& other, T t) const {
|
||||||
Vec3 relative = other - *this * dot;
|
return Vec3(
|
||||||
|
std::fma(t, other.x - x, x),
|
||||||
|
std::fma(t, other.y - y, y),
|
||||||
|
std::fma(t, other.z - z, z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 slerp(const Vec3& other, T t) const {
|
||||||
|
t = std::clamp(t, T(0), T(1));
|
||||||
|
T dotVal = this->dot(other);
|
||||||
|
dotVal = std::clamp(dotVal, T(-1), T(1));
|
||||||
|
|
||||||
|
T theta = std::acos(dotVal) * t;
|
||||||
|
Vec3 relative = other - *this * dotVal;
|
||||||
relative = relative.normalized();
|
relative = relative.normalized();
|
||||||
|
|
||||||
return (*this * std::cos(theta)) + (relative * std::sin(theta));
|
return (*this * std::cos(theta)) + (relative * std::sin(theta));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 rotateX(float angle) const {
|
Vec3 rotateX(T angle) const {
|
||||||
float cosA = std::cos(angle);
|
T cosA = std::cos(angle);
|
||||||
float sinA = std::sin(angle);
|
T sinA = std::sin(angle);
|
||||||
return Vec3(x, y * cosA - z * sinA, y * sinA + z * cosA);
|
return Vec3(x, y * cosA - z * sinA, y * sinA + z * cosA);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 rotateY(float angle) const {
|
Vec3 rotateY(T angle) const {
|
||||||
float cosA = std::cos(angle);
|
T cosA = std::cos(angle);
|
||||||
float sinA = std::sin(angle);
|
T sinA = std::sin(angle);
|
||||||
return Vec3(x * cosA + z * sinA, y, -x * sinA + z * cosA);
|
return Vec3(x * cosA + z * sinA, y, -x * sinA + z * cosA);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 rotateZ(float angle) const {
|
Vec3 rotateZ(T angle) const {
|
||||||
float cosA = std::cos(angle);
|
T cosA = std::cos(angle);
|
||||||
float sinA = std::sin(angle);
|
T sinA = std::sin(angle);
|
||||||
return Vec3(x * cosA - y * sinA, x * sinA + y * cosA, z);
|
return Vec3(x * cosA - y * sinA, x * sinA + y * cosA, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,27 +476,35 @@ public:
|
|||||||
return (&x)[index];
|
return (&x)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 safeInverse(float epsilon = 1e-10f) const {
|
Vec3 safeInverse() const {
|
||||||
return Vec3(
|
return Vec3(
|
||||||
1 / (std::abs(x) < epsilon ? std::copysign(epsilon, x) : x),
|
1 / (std::abs(x) < EPSILON ? std::copysign(EPSILON, x) : x),
|
||||||
1 / (std::abs(y) < epsilon ? std::copysign(epsilon, y) : y),
|
1 / (std::abs(y) < EPSILON ? std::copysign(EPSILON, y) : y),
|
||||||
1 / (std::abs(z) < epsilon ? std::copysign(epsilon, z) : z)
|
1 / (std::abs(z) < EPSILON ? std::copysign(EPSILON, z) : z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t calculateOctantMask() const {
|
uint8_t calculateOctantMask() const {
|
||||||
uint8_t mask = 0;
|
uint8_t mask = 0;
|
||||||
if (x > 0.0f) mask |= 1;
|
if (x > 0.f) mask |= 1;
|
||||||
if (y > 0.0f) mask |= 2;
|
if (y > 0.f) mask |= 2;
|
||||||
if (z > 0.0f) mask |= 4;
|
if (z > 0.f) mask |= 4;
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
float maxComp() const {
|
uint8_t calculateInvOctantMask() const {
|
||||||
|
uint8_t mask = 0;
|
||||||
|
if (x < 0.f) mask |= 1;
|
||||||
|
if (y < 0.f) mask |= 2;
|
||||||
|
if (z < 0.f) mask |= 4;
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
T maxComp() const {
|
||||||
return std::max({x, y, z});
|
return std::max({x, y, z});
|
||||||
}
|
}
|
||||||
|
|
||||||
float minComp() const {
|
T minComp() const {
|
||||||
return std::min({x, y, z});
|
return std::min({x, y, z});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,12 +517,82 @@ public:
|
|||||||
return std::hash<float>()(v.x) ^ (std::hash<float>()(v.y) << 1) ^ (std::hash<float>()(v.z) << 2);
|
return std::hash<float>()(v.x) ^ (std::hash<float>()(v.y) << 1) ^ (std::hash<float>()(v.z) << 2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Vec2<T> toLatLon() const {
|
||||||
|
T r = length();
|
||||||
|
if (r == T(0)) return Vec2<T>(0, 0);
|
||||||
|
T θ = std::acos(z / r);
|
||||||
|
T lat = static_cast<T>(M_PI/2.0) - θ;
|
||||||
|
|
||||||
|
T lon = std::atan2(y, x);
|
||||||
|
return Vec2<T>(lat, lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<T> toLatLon(const Vec3& center) const {
|
||||||
|
Vec3 relative = *this - center;
|
||||||
|
return relative.toLatLon();
|
||||||
|
}
|
||||||
|
|
||||||
|
T toElevation() const {
|
||||||
|
return length();
|
||||||
|
}
|
||||||
|
|
||||||
|
T toElevation(const Vec3& center) const {
|
||||||
|
return distance(center);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __SSE__
|
||||||
|
// SSE-optimized version for float types
|
||||||
|
template<>
|
||||||
|
inline Vec3<float> Vec3<float>::normalized() const {
|
||||||
|
float lenSq = lengthSquared();
|
||||||
|
if (lenSq > 0.0f) {
|
||||||
|
// Load vector into SSE register
|
||||||
|
__m128 vec = _mm_set_ps(0.0f, z, y, x); // w=0, z, y, x
|
||||||
|
|
||||||
|
// Fast inverse square root using SSE
|
||||||
|
__m128 lenSq128 = _mm_set1_ps(lenSq);
|
||||||
|
|
||||||
|
// Quake III fast inverse sqrt SSE version
|
||||||
|
__m128 half = _mm_mul_ps(lenSq128, _mm_set1_ps(0.5f));
|
||||||
|
__m128 three = _mm_set1_ps(1.5f);
|
||||||
|
|
||||||
|
__m128 y = lenSq128;
|
||||||
|
__m128i i = _mm_castps_si128(y);
|
||||||
|
i = _mm_sub_epi32(_mm_set1_epi32(0x5f3759df),
|
||||||
|
_mm_srai_epi32(i, 1));
|
||||||
|
y = _mm_castsi128_ps(i);
|
||||||
|
|
||||||
|
y = _mm_mul_ps(y, _mm_sub_ps(three, _mm_mul_ps(half, _mm_mul_ps(y, y))));
|
||||||
|
|
||||||
|
// Multiply vector by inverse length
|
||||||
|
__m128 invLen128 = y;
|
||||||
|
__m128 result = _mm_mul_ps(vec, invLen128);
|
||||||
|
|
||||||
|
// Extract results
|
||||||
|
alignas(16) float resultArr[4];
|
||||||
|
_mm_store_ps(resultArr, result);
|
||||||
|
|
||||||
|
return Vec3<float>(resultArr[0], resultArr[1], resultArr[2]);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//use a smaller format first instead of larger format.
|
||||||
|
//#ifdef std::float16_t
|
||||||
|
//using Vec3f = Vec3<std::float16_t>;
|
||||||
|
//#else
|
||||||
using Vec3f = Vec3<float>;
|
using Vec3f = Vec3<float>;
|
||||||
|
//#endif
|
||||||
using Vec3d = Vec3<double>;
|
using Vec3d = Vec3<double>;
|
||||||
using Vec3i = Vec3<int>;
|
using Vec3i = Vec3<int>;
|
||||||
|
using Vec3i32 = Vec3<uint32_t>;
|
||||||
|
using Vec3i8 = Vec3<int8_t>;
|
||||||
using Vec3ui8 = Vec3<uint8_t>;
|
using Vec3ui8 = Vec3<uint8_t>;
|
||||||
|
using Vec3T = Vec3<size_t>;
|
||||||
|
using Vec3b = Vec3<bool>;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Vec3<T>& vec) {
|
inline std::ostream& operator<<(std::ostream& os, const Vec3<T>& vec) {
|
||||||
@@ -404,4 +609,23 @@ namespace std {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vec3<T> max(Vec3<T> a, Vec3<T> b) {
|
||||||
|
return a.max(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vec3<T> min(Vec3<T> a, Vec3<T> b) {
|
||||||
|
return a.min(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vec3<T> mix(const Vec3<T>& a, const Vec3<T>& b, const Vec3<bool>& mask) {
|
||||||
|
return Vec3<T>(
|
||||||
|
mask.x ? b.x : a.x,
|
||||||
|
mask.y ? b.y : a.y,
|
||||||
|
mask.z ? b.z : a.z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
#ifndef VOXEL_HPP
|
|
||||||
#define VOXEL_HPP
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
#include <limits>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <random>
|
|
||||||
#include <functional>
|
|
||||||
#include <tuple>
|
|
||||||
#include "timing_decorator.hpp"
|
|
||||||
#include "vec3.hpp"
|
|
||||||
#include "vec4.hpp"
|
|
||||||
|
|
||||||
class VoxelGrid {
|
|
||||||
private:
|
|
||||||
std::unordered_map<Vec3, size_t> positionToIndex;
|
|
||||||
std::vector<Vec3> positions;
|
|
||||||
std::vector<Vec4> colors;
|
|
||||||
std::vector<int> layers;
|
|
||||||
|
|
||||||
Vec3 gridSize;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Vec3 voxelSize;
|
|
||||||
|
|
||||||
enum LayerType {
|
|
||||||
ATMOSPHERE = 0,
|
|
||||||
CRUST = 1,
|
|
||||||
MANTLE = 2,
|
|
||||||
OUTER_CORE = 3,
|
|
||||||
INNER_CORE = 4,
|
|
||||||
EMPTY = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
VoxelGrid(const Vec3& size, const Vec3& voxelSize = Vec3(1, 1, 1)) : gridSize(size), voxelSize(voxelSize) {}
|
|
||||||
|
|
||||||
void addVoxel(const Vec3& position, const Vec4& color) {
|
|
||||||
Vec3 gridPos = worldToGrid(position);
|
|
||||||
|
|
||||||
auto it = positionToIndex.find(gridPos);
|
|
||||||
if (it == positionToIndex.end()) {
|
|
||||||
size_t index = positions.size();
|
|
||||||
positions.push_back(gridPos);
|
|
||||||
colors.push_back(color);
|
|
||||||
layers.push_back(EMPTY);
|
|
||||||
positionToIndex[gridPos] = index;
|
|
||||||
} else {
|
|
||||||
colors[it->second] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addVoxelWithLayer(const Vec3& position, const Vec4& color, int layer) {
|
|
||||||
Vec3 gridPos = worldToGrid(position);
|
|
||||||
|
|
||||||
auto it = positionToIndex.find(gridPos);
|
|
||||||
if (it == positionToIndex.end()) {
|
|
||||||
size_t index = positions.size();
|
|
||||||
positions.push_back(gridPos);
|
|
||||||
colors.push_back(color);
|
|
||||||
layers.push_back(layer);
|
|
||||||
positionToIndex[gridPos] = index;
|
|
||||||
} else {
|
|
||||||
colors[it->second] = color;
|
|
||||||
layers[it->second] = layer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec4 getVoxel(const Vec3& position) const {
|
|
||||||
Vec3 gridPos = worldToGrid(position);
|
|
||||||
auto it = positionToIndex.find(gridPos);
|
|
||||||
if (it != positionToIndex.end()) {
|
|
||||||
return colors[it->second];
|
|
||||||
}
|
|
||||||
return Vec4(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getVoxelLayer(const Vec3& position) const {
|
|
||||||
Vec3 gridPos = worldToGrid(position);
|
|
||||||
auto it = positionToIndex.find(gridPos);
|
|
||||||
if (it != positionToIndex.end()) {
|
|
||||||
return layers[it->second];
|
|
||||||
}
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOccupied(const Vec3& position) const {
|
|
||||||
Vec3 gridPos = worldToGrid(position);
|
|
||||||
return positionToIndex.find(gridPos) != positionToIndex.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 worldToGrid(const Vec3& worldPos) const {
|
|
||||||
return (worldPos / voxelSize).floor();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 gridToWorld(const Vec3& gridPos) const {
|
|
||||||
return gridPos * voxelSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Vec3>& getOccupiedPositions() const {
|
|
||||||
return positions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Vec4>& getColors() const {
|
|
||||||
return colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<int>& getLayers() const {
|
|
||||||
return layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unordered_map<Vec3, size_t>& getPositionToIndexMap() const {
|
|
||||||
return positionToIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3& getGridSize() const {
|
|
||||||
return gridSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3& getVoxelSize() const {
|
|
||||||
return voxelSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
positions.clear();
|
|
||||||
colors.clear();
|
|
||||||
layers.clear();
|
|
||||||
positionToIndex.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void assignPlanetaryLayers(const Vec3& center = Vec3(0, 0, 0)) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
printf("Assigning planetary layers...\n");
|
|
||||||
|
|
||||||
const float atmospherePercent = 0.05f;
|
|
||||||
const float crustPercent = 0.01f;
|
|
||||||
const float mantlePercent = 0.10f;
|
|
||||||
const float outerCorePercent = 0.42f;
|
|
||||||
const float innerCorePercent = 0.42f;
|
|
||||||
|
|
||||||
float maxDistance = 0.0f;
|
|
||||||
for (const auto& pos : positions) {
|
|
||||||
Vec3 worldPos = gridToWorld(pos);
|
|
||||||
float distance = (worldPos - center).length();
|
|
||||||
maxDistance = std::max(maxDistance, distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Maximum distance from center: %.2f\n", maxDistance);
|
|
||||||
|
|
||||||
const float atmosphereStart = maxDistance * (1.0f - atmospherePercent);
|
|
||||||
const float crustStart = maxDistance * (1.0f - atmospherePercent - crustPercent);
|
|
||||||
const float mantleStart = maxDistance * (1.0f - atmospherePercent - crustPercent - mantlePercent);
|
|
||||||
const float outerCoreStart = maxDistance * (1.0f - atmospherePercent - crustPercent - mantlePercent - outerCorePercent);
|
|
||||||
|
|
||||||
printf("Layer boundaries:\n");
|
|
||||||
printf(" Atmosphere: %.2f to %.2f\n", atmosphereStart, maxDistance);
|
|
||||||
printf(" Crust: %.2f to %.2f\n", crustStart, atmosphereStart);
|
|
||||||
printf(" Mantle: %.2f to %.2f\n", mantleStart, crustStart);
|
|
||||||
printf(" Outer Core: %.2f to %.2f\n", outerCoreStart, mantleStart);
|
|
||||||
printf(" Inner Core: 0.00 to %.2f\n", outerCoreStart);
|
|
||||||
|
|
||||||
int atmosphereCount = 0, crustCount = 0, mantleCount = 0, outerCoreCount = 0, innerCoreCount = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < positions.size(); ++i) {
|
|
||||||
Vec3 worldPos = gridToWorld(positions[i]);
|
|
||||||
float distance = (worldPos - center).length();
|
|
||||||
|
|
||||||
Vec4 layerColor;
|
|
||||||
int layerType;
|
|
||||||
|
|
||||||
if (distance >= atmosphereStart) {
|
|
||||||
// Atmosphere - transparent blue
|
|
||||||
layerColor = Vec4(0.2f, 0.4f, 1.0f, 0.3f); // Semi-transparent blue
|
|
||||||
layerType = ATMOSPHERE;
|
|
||||||
atmosphereCount++;
|
|
||||||
} else if (distance >= crustStart) {
|
|
||||||
// Crust - light brown
|
|
||||||
layerColor = Vec4(0.8f, 0.7f, 0.5f, 1.0f); // Light brown
|
|
||||||
layerType = CRUST;
|
|
||||||
crustCount++;
|
|
||||||
} else if (distance >= mantleStart) {
|
|
||||||
// Mantle - reddish brown
|
|
||||||
layerColor = Vec4(0.7f, 0.3f, 0.2f, 1.0f); // Reddish brown
|
|
||||||
layerType = MANTLE;
|
|
||||||
mantleCount++;
|
|
||||||
} else if (distance >= outerCoreStart) {
|
|
||||||
// Outer Core - orange/yellow
|
|
||||||
layerColor = Vec4(1.0f, 0.6f, 0.2f, 1.0f); // Orange
|
|
||||||
layerType = OUTER_CORE;
|
|
||||||
outerCoreCount++;
|
|
||||||
} else {
|
|
||||||
// Inner Core - bright yellow
|
|
||||||
layerColor = Vec4(1.0f, 0.9f, 0.1f, 1.0f); // Bright yellow
|
|
||||||
layerType = INNER_CORE;
|
|
||||||
innerCoreCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
colors[i] = layerColor;
|
|
||||||
layers[i] = layerType;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Layer distribution:\n");
|
|
||||||
printf(" Atmosphere: %d voxels (%.1f%%)\n", atmosphereCount, (atmosphereCount * 100.0f) / positions.size());
|
|
||||||
printf(" Crust: %d voxels (%.1f%%)\n", crustCount, (crustCount * 100.0f) / positions.size());
|
|
||||||
printf(" Mantle: %d voxels (%.1f%%)\n", mantleCount, (mantleCount * 100.0f) / positions.size());
|
|
||||||
printf(" Outer Core: %d voxels (%.1f%%)\n", outerCoreCount, (outerCoreCount * 100.0f) / positions.size());
|
|
||||||
printf(" Inner Core: %d voxels (%.1f%%)\n", innerCoreCount, (innerCoreCount * 100.0f) / positions.size());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user