145 Commits

Author SHA1 Message Date
yggdrasil75
3ff50cb43d plants 2026-03-09 05:38:27 -04:00
Yggdrasil75
3d96e569c8 I fixed this so removing. 2026-03-04 14:36:31 -05:00
Yggdrasil75
31fb9ffedb asdf 2026-03-04 14:34:40 -05:00
Yggdrasil75
4478bc1845 pushing a star 2026-03-04 14:07:05 -05:00
yggdrasil75
83989e955e fixes 2026-03-04 05:41:51 -05:00
Yggdrasil75
494ee931df pushing this, might need some fixes still 2026-03-03 14:55:36 -05:00
Yggdrasil75
d36d00bc13 some memory changes and an attempt at fillplanet 2026-03-03 13:03:45 -05:00
Yggdrasil75
a0afac9c18 asdf 2026-03-03 06:05:15 -05:00
yggdrasil75
565646e13e pushing this back. 2026-03-02 21:22:40 -05:00
Yggdrasil75
14158fae83 fixed interpolating breaking stuff 2026-03-02 14:01:00 -05:00
Yggdrasil75
4f0dba5eb4 pushing this, lots of fun things to try. 2026-03-02 13:53:13 -05:00
Yggdrasil75
926ffe18cd fixed some rendering 2026-02-27 08:45:26 -05:00
yggdrasil75
b4a7f536bc asdfasdfasdf 2026-02-27 05:47:50 -05:00
yggdrasil75
06d5b383e5 woops 2026-02-26 15:19:52 -05:00
Yggdrasil75
18aa8f06b7 bunch of planet sim changes 2026-02-26 14:03:12 -05:00
Yggdrasil75
fdd5553d20 pushing 2026-02-24 15:01:08 -05:00
Yggdrasil75
e99bbc08af pushing home 2026-02-23 15:00:58 -05:00
Yggdrasil75
c7eb5739f4 some speed test changes 2026-02-23 08:18:07 -05:00
yggdrasil75
2c993995e8 making a proper widget. 2026-02-22 21:07:20 -05:00
yggdrasil75
cb0b8b7643 woops 2026-02-21 11:13:45 -05:00
yggdrasil75
dc36b93e4f some changes for speed and usability with the fast version 2026-02-21 11:05:11 -05:00
Yggdrasil75
2768b6849e some g2 changes. 2026-02-20 13:20:53 -05:00
Yggdrasil75
4f227df1d7 lots of performance changes to g3. reverted to voxel grid from mesh. 2026-02-20 13:20:29 -05:00
Yggdrasil75
14b4d06495 some crud and such 2026-02-16 10:21:18 -05:00
yggdrasil75
d6d638cb01 minor change to resolution slider, added backface culling and normal preprocessing 2026-02-14 10:06:55 -05:00
yggdrasil75
a1e4a852be a 2026-02-14 09:35:33 -05:00
yggdrasil75
af4e6ebe3f direct render tris, and moving some stuff out of main. 2026-02-14 09:32:55 -05:00
Yggdrasil75
6b9362219e meshify is working decently 2026-02-13 13:07:51 -05:00
Yggdrasil75
e402e712b4 pushing some junk home. adding back meshing 2026-02-12 15:00:34 -05:00
Yggdrasil75
1ba5611672 fix a minor bug 2026-02-11 07:01:42 -05:00
Yggdrasil75
d07325932c removed spheres 2026-02-10 13:22:46 -05:00
Yggdrasil75
2db9575bd1 adding materials. 2026-02-10 10:06:43 -05:00
Yggdrasil75
798506c300 some more unwraps. 2026-02-09 14:57:39 -05:00
Yggdrasil75
8a564f51ad some unwraps and proper epsilon usage. 2026-02-09 14:48:35 -05:00
yggdrasil75
5198650926 asdf 2026-02-09 05:43:53 -05:00
yggdrasil75
ca226d8c02 added fluidsim to main 2026-02-07 08:58:54 -05:00
yggdrasil75
02361effc5 need to clean up some fluidsim stuff 2026-02-07 08:30:04 -05:00
Yggdrasil75
d6d4a30798 better simulation 2026-02-06 14:31:04 -05:00
Yggdrasil75
73eaa63dfb so much debugging only to find I was looking in the wrong place. 2026-02-06 10:05:12 -05:00
Yggdrasil75
17e4cdf1a5 stuff being weird. 2026-02-05 13:57:52 -05:00
yggdrasil75
47b264d016 particle sim stuff 2026-02-05 07:42:48 -05:00
yggdrasil75
39c38e4061 added move and more of the particle stuff 2026-02-05 06:52:02 -05:00
Yggdrasil75
341066a0cf pushing 2026-02-04 15:00:05 -05:00
Yggdrasil75
b84c5f141e gonna work on this more later. adding a fluid sim, gonna use it to create the planet by spawning in heavy particles and then slowly lighter and smaller over time until a sphere is formed. 2026-02-03 14:32:37 -05:00
Yggdrasil75
62adc8575b fix value and fractal noise 2026-02-03 13:05:26 -05:00
Yggdrasil75
d51f6ba3da moved noise to its own file 2026-02-03 08:22:20 -05:00
Yggdrasil75
e54c524f26 set up frame to be more useful, minor fixes in grid, added use lod checkbox 2026-02-03 07:48:29 -05:00
yggdrasil75
88ee3732b6 another minor feature 2026-02-03 05:44:48 -05:00
yggdrasil75
9b902cebd7 minor fixes 2026-02-03 05:40:54 -05:00
Yggdrasil75
d719d0a922 more camera stuff 2026-02-02 11:21:58 -05:00
yggdrasil75
3f88fdd14e lets try speeding it up again. 2026-02-02 06:34:36 -05:00
yggdrasil75
58d90e9cd7 fixes and lod 2026-01-30 19:41:55 -05:00
Yggdrasil75
2006fd5f57 asdf 2026-01-30 14:56:26 -05:00
Yggdrasil75
9a87eeb355 layered noise 2026-01-30 14:43:27 -05:00
Yggdrasil75
662da70665 a lot of noise stuff 2026-01-30 13:22:25 -05:00
Yggdrasil75
ce526bebc9 woops. didnt mean to remove that. 2026-01-30 08:17:19 -05:00
Yggdrasil75
33c27fd5b7 remove old junk 2026-01-30 08:10:17 -05:00
Yggdrasil75
9b81d0f5f8 moved stats to window 2026-01-30 07:56:35 -05:00
Yggdrasil75
c8aa34dcda faster fastvoxeltraverse 2026-01-30 07:05:52 -05:00
Yggdrasil75
c6038722e5 some attempts at speed improvements 2026-01-29 14:50:40 -05:00
Yggdrasil75
820cc1f873 added some more fun features for rendering 2026-01-29 12:52:31 -05:00
Yggdrasil75
c282acd725 meh. 2026-01-29 09:15:02 -05:00
Yggdrasil75
670ff42b82 CRUD 2026-01-29 06:50:57 -05:00
yggdrasil75
44068ba32c minimal effort global illumination 2026-01-29 05:39:10 -05:00
yggdrasil75
aaa7b1e24e preparations for further changes 2026-01-28 21:00:16 -05:00
Yggdrasil75
f24fcaa691 minor positioning changes 2026-01-28 14:48:02 -05:00
Yggdrasil75
4febc51784 it works 2026-01-28 14:34:58 -05:00
Yggdrasil75
19462868ea updated pnoise, added path tracing of lights. I got some more to do though. 2026-01-28 09:45:14 -05:00
Yggdrasil75
e5e0c5b838 woops, changed this while testing, forgot to change back 2026-01-28 06:46:37 -05:00
yggdrasil75
d45df35101 fixed overloading a variable 2026-01-27 19:51:06 -05:00
Yggdrasil75
c0d15a0b9f pushing camera eigen update, and other fixes 2026-01-27 16:11:09 -05:00
yggdrasil75
e0764318b4 added eigen, dropping my own for the speed potential. its still kinda broken, but its faster. 2026-01-26 21:06:53 -05:00
yggdrasil75
c3916d146a usually works. need to figure out what causes it to not to sometimes. 2026-01-26 20:02:01 -05:00
Yggdrasil75
ed6b625826 well, its still broken. hopefully this helps. 2026-01-23 15:15:06 -05:00
Yggdrasil75
e7ce32b266 let the compiler optimize. its better at it than I am. 2026-01-23 13:41:57 -05:00
Yggdrasil75
c77d3b25ba fixed an oversight. was too small a morton code. 2026-01-23 13:10:39 -05:00
Yggdrasil75
4f409cedc5 well, fast enough I guess. 2026-01-23 10:13:51 -05:00
yggdrasil75
9c0be89a8b need to fix later 2026-01-23 05:47:28 -05:00
yggdrasil75
65d36cc34c sped up sphere creation, added some chunk stuff. removed serialization that didnt work. 2026-01-22 21:04:13 -05:00
yggdrasil75
a453149f57 some further optimizing and updating mat3 2026-01-22 18:15:53 -05:00
Yggdrasil75
842da7a507 reverted step. added morton encoding. 2026-01-22 14:44:53 -05:00
Yggdrasil75
5d18ff0199 tMax was slower with the precompute. 2026-01-22 10:08:47 -05:00
yggdrasil75
5ef07c6af5 made it I think faster 2026-01-21 20:25:25 -05:00
Yggdrasil75
0aeed604a7 pushing home. will need to correct some things. idea: precalculate regions of steps 2026-01-21 15:01:22 -05:00
Yggdrasil75
dc514cfe31 add fp16 with fp32 fallback 2026-01-21 10:13:39 -05:00
Yggdrasil75
ff3e4711db fast inverse square root is fast. and ignore that typo 2026-01-21 07:23:09 -05:00
yggdrasil75
63a21b103f fixing initial alpha 2026-01-21 05:49:36 -05:00
yggdrasil75
76e0d4e06a kinda works. 2026-01-20 21:10:10 -05:00
yggdrasil75
a4e378bbcd some fixes, also changed from doubles to floats to save memory for sake of cache optimization 2026-01-20 18:17:35 -05:00
Yggdrasil75
c52c6b14b9 correct something in my uv map 2026-01-20 15:01:35 -05:00
Yggdrasil75
a0b9fab5f9 fixed a lot, added some more, made it faster. 2026-01-20 12:45:23 -05:00
Yggdrasil75
58555f96c8 skipping chunks added. 2026-01-20 09:30:23 -05:00
Yggdrasil75
840a3f1517 fixing stuff. 2026-01-20 08:20:47 -05:00
Yggdrasil75
f91998e839 revert of majority of chunk stuff. wasnt working well. 2026-01-20 06:56:17 -05:00
Yggdrasil75
db423030dd pushing this home. 2026-01-19 15:01:15 -05:00
Yggdrasil75
0782189838 fixed segfault. 2026-01-19 12:48:02 -05:00
Yggdrasil75
310b4233be some attempts to resolve chunking issues 2026-01-19 12:33:29 -05:00
Yggdrasil75
835ac45c9a pushing some more features to chunks before implementing. 2026-01-19 09:09:05 -05:00
Yggdrasil75
a914e7a1f7 add chunks 2026-01-19 07:12:20 -05:00
yggdrasil75
1ae8514e01 fixed a color index issue. 2026-01-18 21:11:05 -05:00
yggdrasil75
a4e8581e99 remove mesh, add a limit to voxeltraverse 2026-01-18 20:59:55 -05:00
yggdrasil75
b820c89bd0 pushing some additional features 2026-01-18 20:44:30 -05:00
Yggdrasil75
81af30d4c6 fix rgba issue 2026-01-16 14:57:50 -05:00
Yggdrasil75
0cb71a4799 added sphere and noise documentation 2026-01-16 14:45:55 -05:00
Yggdrasil75
acba629774 noise features, fixed some grid stuff. 2026-01-16 13:37:13 -05:00
Yggdrasil75
4dfe6c9a5e recording mode added 2026-01-15 14:49:00 -05:00
Yggdrasil75
71777dc135 lots of changes, serialization is done better, grid is defined better, etc. 2026-01-14 13:04:13 -05:00
Yggdrasil75
c39a3beeff pushing serialization. need to do it better in a dedicated file later. 2026-01-14 08:11:37 -05:00
Yggdrasil75
175fb19da1 asdf 2026-01-13 14:57:04 -05:00
Yggdrasil75
40c761edb7 done with trying to copy treexy. it wasnt better after I implemented it. going to switch back to my method. 2026-01-13 14:56:59 -05:00
Yggdrasil75
082682a339 pushing some changes. 2026-01-13 14:33:23 -05:00
Yggdrasil75
2870be6cf3 used the treexy version for a function. my attempt failed. 2026-01-13 08:09:24 -05:00
Yggdrasil75
3a183e0f92 bunch of fixes to the grid33 version 2026-01-13 07:51:03 -05:00
Yggdrasil75
5d614e6737 fixed some issues. updated a bunch. pushing now. 2026-01-12 14:38:42 -05:00
Yggdrasil75
b1cffb9a54 pushing some changes to controls 2026-01-09 13:34:02 -05:00
Yggdrasil75
1a73679f4e templated vec2. updated a bunch of stuff. 2026-01-09 08:42:05 -05:00
Yggdrasil75
4a98b10189 might fix the rgba. dont know. 2026-01-08 14:23:28 -05:00
Yggdrasil75
1c9958711d works when not using rgba. 2026-01-08 14:22:56 -05:00
Yggdrasil75
cdd1ef5e8e manual save works. but not while in the frame 2026-01-08 14:19:52 -05:00
Yggdrasil75
044c059c1d asdf 2026-01-08 10:45:43 -05:00
Yggdrasil75
2e5d2b150a gonna push this cause its kinda there. but not really. 2026-01-08 10:08:05 -05:00
Yggdrasil75
a8a528beda pushing this. 2026-01-07 14:23:51 -05:00
Yggdrasil75
1aa81ce65f asdf 2026-01-07 14:06:25 -05:00
Yggdrasil75
65f2464c70 finally fixed bmp bgr/rgb bug. 2026-01-07 13:12:19 -05:00
Yggdrasil75
7c569afd46 better 2026-01-06 14:58:20 -05:00
Yggdrasil75
d9aff085a8 pushing this. 2026-01-06 14:49:09 -05:00
Yggdrasil75
b74ba3cfdc at least dir fix works. 2026-01-05 14:57:33 -05:00
Yggdrasil75
f347637b77 more set features and a color noise 2026-01-05 14:10:27 -05:00
Yggdrasil75
264f5d9496 pushing cause its kinda working. 2026-01-05 12:56:32 -05:00
Yggdrasil75
466fa26dc7 some improvements 2026-01-05 10:58:00 -05:00
Yggdrasil75
57e9834772 pushing some stuff home 2026-01-02 15:01:34 -05:00
yggdrasil75
7bc711a3ad fixed 1 direction 2026-01-02 05:15:08 -05:00
Yggdrasil75
a9caaeb40f I dont get my scalars 2025-12-31 14:13:07 -05:00
Yggdrasil75
c92b5dc203 my grid should work. just need minor tests 2025-12-31 10:49:52 -05:00
Yggdrasil75
aed793b6fe better comparisons? 2025-12-31 09:50:19 -05:00
Yggdrasil75
17975b58a9 cleaning up my comparison operators 2025-12-31 09:12:47 -05:00
Yggdrasil75
02115dcfc0 gonna get this clean one of these days. 2025-12-30 15:00:58 -05:00
Yggdrasil75
05f709c00b fixing minor issues 2025-12-30 08:27:05 -05:00
Yggdrasil75
4cb1bd2530 Merge branch 'master' of https://github.com/yggdrasil75/stupidsimcpp 2025-12-29 14:57:33 -05:00
Yggdrasil75
bc55db2c74 pushing this home. 2025-12-29 14:57:29 -05:00
Yggdrasil75
1a4ad39642 staging 2025-12-29 13:30:54 -05:00
Yggdrasil75
82a10cb2c5 some fun changes. 2025-12-29 13:30:39 -05:00
yggdrasil75
cd60918540 woops 2025-12-26 12:58:30 -05:00
yggdrasil75
ce27939e4e meh2 2025-12-26 12:50:24 -05:00
yggdrasil75
7252c655a2 meh 2025-12-26 12:50:19 -05:00
59 changed files with 12474 additions and 9348 deletions

3
.gitignore vendored
View File

@@ -2,3 +2,6 @@
/bin/ /bin/
/web/output/ /web/output/
imgui.ini imgui.ini
#prototyping in python below
*.py
/venv/

4
.gitmodules vendored
View File

@@ -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

Submodule eigen added at fdfdd4c96b

114
h.cpp Normal file
View 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

Submodule imgui updated: 23bd697f05...814c6a194b

View File

@@ -7,16 +7,34 @@ 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)/g2chromatic2.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
@@ -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
View File

@@ -0,0 +1 @@
Thanks to Treexy, Amanatides, Woo, Occam, Incf, and so on.

170
tests/fluidsim.cpp Normal file
View 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

View File

@@ -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;
}

View File

@@ -1,531 +0,0 @@
#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
#include <cmath>
#include <tuple>
#include <unordered_set>
#include "../util/grid/grid2.hpp"
#include "../util/output/aviwriter.hpp"
#include "../util/output/bmpwriter.hpp"
#include "../util/timing_decorator.cpp"
#include "../imgui/imgui.h"
#include "../imgui/backends/imgui_impl_glfw.h"
#include "../imgui/backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include "../stb/stb_image.h"
#include <thread>
#include <atomic>
#include <future>
#include <mutex>
#include <chrono>
#ifndef M_PI
#define M_PI = 3.1415
#endif
std::mutex m;
std::atomic<bool> isGenerating{false};
std::future<void> generationFuture;
std::mutex previewMutex;
std::atomic<bool> updatePreview{false};
frame currentPreviewFrame;
GLuint textu = 0;
std::string previewText;
struct Shared {
std::mutex mutex;
Grid2 grid;
bool hasNewFrame = false;
int currentFrame = 0;
};
struct AnimationConfig {
int width = 1024;
int height = 1024;
int totalFrames = 480;
float fps = 30.0f;
int numSeeds = 8;
int noisemod = 42;
};
Grid2 setup(AnimationConfig config) {
TIME_FUNCTION;
Grid2 grid;
std::vector<Vec2> pos;
std::vector<Vec4> colors;
std::vector<float> sizes;
for (int y = 0; y < config.height; ++y) {
for (int x = 0; x < config.width; ++x) {
float gradient = (x + y) / float(config.width + config.height - 2);
pos.push_back(Vec2(x,y));
colors.push_back(Vec4(gradient, gradient, gradient, 1.0f));
sizes.push_back(1.0f);
}
}
grid.bulkAddObjects(pos,colors,sizes);
return grid;
}
void Preview(Grid2& grid) {
TIME_FUNCTION;
int width;
int height;
//std::vector<uint8_t> rgbData;
frame rgbData = grid.getGridAsFrame(frame::colormap::RGB);
std::cout << "Frame looks like: " << rgbData << std::endl;
bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData);
if (!success) {
std::cout << "yo! this failed in Preview" << std::endl;
}
}
void livePreview(const Grid2& grid) {
std::lock_guard<std::mutex> lock(previewMutex);
currentPreviewFrame = grid.getGridAsFrame(frame::colormap::RGBA);
glGenTextures(1, &textu);
glBindTexture(GL_TEXTURE_2D, textu);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindTexture(GL_TEXTURE_2D, textu);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
updatePreview = true;
}
std::vector<std::tuple<size_t, Vec2, Vec4>> pickSeeds(Grid2 grid, AnimationConfig config) {
TIME_FUNCTION;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> xDist(0, config.width - 1);
std::uniform_int_distribution<> yDist(0, config.height - 1);
std::uniform_real_distribution<> colorDist(0.2f, 0.8f);
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds;
for (int i = 0; i < config.numSeeds; ++i) {
Vec2 point(xDist(gen), yDist(gen));
Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255);
size_t id = grid.getOrCreatePositionVec(point, 0.0, true);
grid.setColor(id, color);
seeds.push_back(std::make_tuple(id,point, color));
}
return seeds;
}
void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<size_t, Vec2, Vec4>>& seeds) {
TIME_FUNCTION;
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
std::unordered_set<size_t> visitedThisFrame;
for (const auto& seed : seeds) {
visitedThisFrame.insert(std::get<0>(seed));
}
//#pragma omp parallel for
for (const std::tuple<size_t, Vec2, Vec4>& seed : seeds) {
size_t id = std::get<0>(seed);
Vec2 seedPOS = std::get<1>(seed);
Vec4 seedColor = std::get<2>(seed);
std::vector<size_t> neighbors = grid.getNeighbors(id);
//grid.setSize(id, grid.getSize(id)+4);
for (size_t neighbor : neighbors) {
if (visitedThisFrame.count(neighbor)) {
continue;
}
visitedThisFrame.insert(neighbor);
Vec2 neipos = grid.getPositionID(neighbor);
Vec4 neighborColor = grid.getColor(neighbor);
float distance = seedPOS.distance(neipos);
float angle = seedPOS.directionTo(neipos);
float normalizedAngle = (angle + M_PI) / (2.0f * M_PI);
float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI);
blendFactor = std::clamp(blendFactor, 0.1f, 0.9f);
Vec4 newcolor = Vec4(
seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor),
seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor,
seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)),
1.0f
);
newcolor = newcolor.clamp(0.0f, 1.0f);
grid.setColor(neighbor, newcolor);
newseeds.emplace_back(neighbor, neipos, newcolor);
}
}
seeds.clear();
seeds.shrink_to_fit();
seeds = std::move(newseeds);
}
//bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
bool exportavi(std::vector<frame> frames, AnimationConfig config) {
TIME_FUNCTION;
std::string filename = "output/chromatic_transformation.avi";
std::cout << "Frame count: " << frames.size() << std::endl;
// Log compression statistics for all frames
std::cout << "\n=== Frame Compression Statistics ===" << std::endl;
size_t totalOriginalSize = 0;
size_t totalCompressedSize = 0;
for (int i = 0; i < frames.size(); ++i) {
totalOriginalSize += frames[i].getSourceSize();
totalCompressedSize += frames[i].getTotalCompressedSize();
}
double overallRatio = static_cast<double>(totalOriginalSize) / totalCompressedSize;
double overallSavings = (1.0 - 1.0/overallRatio) * 100.0;
std::cout << "\n=== Overall Compression Summary ===" << std::endl;
std::cout << "Total frames: " << frames.size() << std::endl;
std::cout << "Compressed frames: " << frames.size() << std::endl;
std::cout << "Total original size: " << totalOriginalSize << " bytes ("
<< std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
std::cout << "Total compressed size: " << totalCompressedSize << " bytes ("
<< std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl;
std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl;
std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl;
std::filesystem::path dir = "output";
if (!std::filesystem::exists(dir)) {
if (!std::filesystem::create_directories(dir)) {
std::cout << "Failed to create output directory!" << std::endl;
return false;
}
}
bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps);
if (success) {
// Check if file actually exists
if (std::filesystem::exists(filename)) {
auto file_size = std::filesystem::file_size(filename);
std::cout << "\nAVI file created successfully: " << filename
<< " (" << file_size << " bytes, "
<< std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl;
}
} else {
std::cout << "Failed to save AVI file!" << std::endl;
}
return success;
}
void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) {
TIME_FUNCTION;
isGenerating = true;
try {
Grid2 grid;
if (gradnoise == 0) {
grid = setup(config);
} else if (gradnoise == 1) {
grid = grid.noiseGenGrid(0,0,config.height, config.width, 0.01, 1.0, true, config.noisemod);
}
grid.setDefault(Vec4(0,0,0,0));
{
std:: lock_guard<std::mutex> lock(state.mutex);
state.grid = grid;
state.hasNewFrame = true;
state.currentFrame = 0;
}
std::cout << "generated grid" << std::endl;
Preview(grid);
std::cout << "generated preview" << std::endl;
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid, config);
std::vector<frame> frames;
for (int i = 0; i < config.totalFrames; ++i){
// Check if we should stop the generation
if (!isGenerating) {
std::cout << "Generation cancelled at frame " << i << std::endl;
return;
}
expandPixel(grid,config,seeds);
std::lock_guard<std::mutex> lock(state.mutex);
state.grid = grid;
state.hasNewFrame = true;
state.currentFrame = i;
// Print compression info for this frame
if (i % 10 == 0 ) {
frame bgrframe;
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
bgrframe = grid.getGridAsFrame(frame::colormap::BGR);
frames.push_back(bgrframe);
//bgrframe.decompress();
//BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe);
bgrframe.compressFrameLZ78();
//bgrframe.printCompressionStats();
}
}
exportavi(frames,config);
}
catch (const std::exception& e) {
std::cerr << "errored at: " << e.what() << std::endl;
}
isGenerating = false;
}
// Function to cancel ongoing generation
void cancelGeneration() {
if (isGenerating) {
isGenerating = false;
// Wait for the thread to finish (with timeout to avoid hanging)
if (generationFuture.valid()) {
auto status = generationFuture.wait_for(std::chrono::milliseconds(100));
if (status != std::future_status::ready) {
std::cout << "Waiting for generation thread to finish..." << std::endl;
}
}
}
}
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
}
int main() {
//static bool window = true;
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
std::cerr << "gui stuff is dumb in c++." << std::endl;
glfwTerminate();
return 1;
}
// COPIED VERBATIM FROM IMGUI.
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
const char* glsl_version = "#version 100";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(IMGUI_IMPL_OPENGL_ES3)
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
const char* glsl_version = "#version 300 es";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
#endif
//ImGui::SetNextWindowSize(ImVec2(1110,667));
//auto beg = ImGui::Begin("Gradient thing", &window);
//if (beg) {
// std::cout << "stuff breaks at 223" << std::endl;
bool application_not_closed = true;
//float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr);
if (window == nullptr)
return 1;
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
//IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring.
ImGui::CreateContext();
// std::cout << "context created" << std::endl;
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
//style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
//style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it.
ImGui_ImplGlfw_InitForOpenGL(window, true);
#ifdef __EMSCRIPTEN__
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
#endif
ImGui_ImplOpenGL3_Init(glsl_version);
// std::cout << "created glfw window" << std::endl;
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
static float f = 30.0f;
static int i1 = 1024;
static int i2 = 1024;
static int i3 = 480;
static int i4 = 8;
static int noisemod = 42;
static float fs = 1.0;
std::future<void> mainlogicthread;
Shared state;
Grid2 grid;
AnimationConfig config;
previewText = "Please generate";
int gradnoise = true;
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
{
ImGui::Begin("settings");
ImGui::SliderFloat("fps", &f, 20.0f, 60.0f);
ImGui::SliderInt("width", &i1, 256, 4096);
ImGui::SliderInt("height", &i2, 256, 4096);
ImGui::SliderInt("frame count", &i3, 10, 5000);
ImGui::SliderInt("number of Seeds", &i4, 0, 10);
ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000);
ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0);
ImGui::RadioButton("Gradient", &gradnoise, 0);
ImGui::RadioButton("Perlin Noise", &gradnoise, 1);
if (isGenerating) {
ImGui::BeginDisabled();
}
if (ImGui::Button("Generate Animation")) {
config = AnimationConfig(i1, i2, i3, f, i4, noisemod);
mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise);
}
if (isGenerating && textu != 0) {
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
cancelGeneration();
}
// Check for new frames from the generation thread
bool hasNewFrame = false;
{
std::lock_guard<std::mutex> lock(state.mutex);
if (state.hasNewFrame) {
livePreview(state.grid);
state.hasNewFrame = false;
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
}
}
ImGui::Text(previewText.c_str());
if (textu != 0) {
ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs);
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
} else {
ImGui::Text("Generating preview...");
}
} else if (isGenerating) {
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
cancelGeneration();
}
// Check for new frames from the generation thread
bool hasNewFrame = false;
{
std::lock_guard<std::mutex> lock(state.mutex);
if (state.hasNewFrame) {
livePreview(state.grid);
state.hasNewFrame = false;
previewText = "Generating... Frame: " + std::to_string(state.currentFrame);
}
}
ImGui::Text(previewText.c_str());
} else if (textu != 0){
//ImGui::EndDisabled();
ImGui::Text(previewText.c_str());
if (textu != 0) {
ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f);
ImVec2 uv_min = ImVec2(0.0f, 0.0f);
ImVec2 uv_max = ImVec2(1.0f, 1.0f);
ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max);
} else {
ImGui::Text("Generating preview...");
}
} else {
ImGui::Text("No preview available");
ImGui::Text("Start generation to see live preview");
}
//std::cout << "sleeping" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
//std::cout << "ending" << std::endl;
ImGui::End();
}
// std::cout << "ending frame" << std::endl;
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
// std::cout << "rendering" << std::endl;
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
//mainlogicthread.join();
// std::cout << "swapping buffers" << std::endl;
}
cancelGeneration();
// std::cout << "shutting down" << std::endl;
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
// std::cout << "destroying" << std::endl;
glfwDestroyWindow(window);
if (textu != 0) {
glDeleteTextures(1, &textu);
textu = 0;
}
glfwTerminate();
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
// std::cout << "printing" << std::endl;
return 0;
}
//I need this: https://raais.github.io/ImStudio/
// g++ -std=c++23 -O3 -march=native -o ./bin/g2gradc ./tests/g2chromatic2.cpp -I./imgui -L./imgui -limgui -lstb `pkg-config --cflags --libs glfw3` && ./bin/g2gradc

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
View 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
View 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
View 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

264
tests/plant.cpp Normal file
View File

@@ -0,0 +1,264 @@
#ifndef PLANT_CPP
#define PLANT_CPP
#include "../util/sim/plant.hpp"
#include "../util/grid/camera.hpp"
// Assuming ImGui headers are available via ptest.cpp or similar include paths
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
class PlantSimUI {
private:
PlantSim sim;
Camera cam;
// Rendering / Texture vars
GLuint textu = 0;
std::mutex PreviewMutex;
bool textureInitialized = false;
frame currentPreviewFrame;
// Render Settings
int outWidth = 512;
int outHeight = 512;
bool slowRender = false;
bool globalIllumination = true; // Default to true to see sun emission
int reflectCount = 2;
float maxDist = 200.0f;
float framerate = 10.0f;
// Input state
std::map<int, bool> keyStates;
float deltaTime = 0.016f; // approx 30fps
const char* getSeasonName(float season, float latitude) {
bool north = latitude >= 0;
if (season < 0.25f) return north ? "Spring" : "Autumn";
if (season < 0.50f) return north ? "Summer" : "Winter";
if (season < 0.75f) return north ? "Autumn" : "Spring";
return north ? "Winter" : "Summer";
}
const char* getWeatherName(PlantSim::WeatherState state) {
switch(state) {
case PlantSim::WeatherState::RAIN: return "Raining";
case PlantSim::WeatherState::SNOW: return "Snowing";
default: return "Clear";
}
}
public:
PlantSimUI() {
// Position camera to look at the dirt
cam.origin = v3(0, 5, 30);
cam.lookAt(v3(0, 2, 0));
cam.fov = 45;
// Init the simulation
sim.initWorld();
v3 bg = v3(0.511f, 0.625f, 0.868f);
sim.grid.setBackgroundColor(bg);
sim.grid.setSkylight(bg);
}
~PlantSimUI() {
if (textu != 0) glDeleteTextures(1, &textu);
}
void renderUI(GLFWwindow* window) {
handleCameraControls(window);
ImGui::Begin("Plant Growth Lab");
if (ImGui::BeginTable("PlantLayout", 2, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
ImGui::TableSetupColumn("Viewport", ImGuiTableColumnFlags_WidthStretch, 0.7f);
ImGui::TableNextColumn();
renderControls();
ImGui::TableNextColumn();
renderPreview();
ImGui::EndTable();
}
sim.update(deltaTime);
ImGui::End();
}
private:
void handleCameraControls(GLFWwindow* window) {
glfwPollEvents();
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
}
float speed = 10.0f * deltaTime;
if (keyStates[GLFW_KEY_W]) cam.moveForward(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_S]) cam.moveBackward(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_A]) cam.moveLeft(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_D]) cam.moveRight(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_Q]) cam.rotateYaw(deltaTime);
if (keyStates[GLFW_KEY_E]) cam.rotateYaw(-deltaTime);
}
void renderControls() {
if (ImGui::CollapsingHeader("World State", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Day: %d / %d", sim.config.currentDay + 1, sim.config.daysPerYear);
ImGui::Text("Season: %s", getSeasonName(sim.config.season, sim.config.latitude));
ImGui::Text("Global Temperature: %.1f °C", sim.currentTemperature);
ImVec4 weatherColor = ImVec4(1, 1, 1, 1);
if (sim.currentWeather == PlantSim::WeatherState::RAIN) weatherColor = ImVec4(0.3f, 0.5f, 1.0f, 1.0f);
else if (sim.currentWeather == PlantSim::WeatherState::SNOW) weatherColor = ImVec4(0.8f, 0.9f, 1.0f, 1.0f);
ImGui::TextColored(weatherColor, "Current Weather: %s", getWeatherName(sim.currentWeather));
ImGui::TextColored(weatherColor, "(Time Remaining: %.1fs)", sim.weatherTimer);
ImGui::Separator();
ImGui::Text("Atmospheric Moisture: %.1f", sim.atmosphericMoisture);
float localCo2 = 0.0f;
float localAirTemp = sim.currentTemperature;
auto airNodes = sim.grid.findInRadius(v3(0, sim.config.voxelSize / 2.0f, 0), sim.config.voxelSize, 5);
if (!airNodes.empty()) {
auto ap = std::static_pointer_cast<AirParticle>(airNodes[0]->data);
localCo2 = ap->co2;
localAirTemp = ap->temperature;
}
ImGui::Text("Local Air Temp: %.1f °C", localAirTemp);
ImGui::Text("Ambient CO2: %.1f ppm", localCo2);
if (sim.extendedHeatTimer > 0.0f) {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.0f, 1.0f), "Heat Wave Timer: %.1f days", sim.extendedHeatTimer / std::max(1.0f, sim.config.dayDuration));
} else {
ImGui::Text("Heat Wave Timer: 0.0 days");
}
}
if (ImGui::CollapsingHeader("Plant Health & Structure", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Active Plant Cells:");
ImGui::BulletText("Leaves: %d", sim.leafCount);
ImGui::BulletText("Roots: %d", sim.rootCount);
ImGui::Text("Total Organism Resources:");
ImGui::ProgressBar(std::min(sim.totalPlantEnergy / (std::max(1, sim.leafCount + sim.rootCount) * 20.0f), 1.0f), ImVec2(-1, 0),
("Energy: " + std::to_string((int)sim.totalPlantEnergy)).c_str());
ImGui::ProgressBar(std::min(sim.totalPlantWater / (std::max(1, sim.leafCount + sim.rootCount) * 20.0f), 1.0f), ImVec2(-1, 0),
("Water: " + std::to_string((int)sim.totalPlantWater)).c_str());
}
ImGui::Separator();
if (ImGui::CollapsingHeader("Soil Environment")) {
float currentHydration = 0.0f;
float currentTemp = 0.0f;
float n = 0;
float p = 0;
float k = 0;
float c = 0;
float mg = 0;
auto dirtNodes = sim.grid.findInRadius(v3(0, -sim.config.voxelSize / 2.0f, 0), sim.config.voxelSize, 0);
if (!dirtNodes.empty()) {
auto dp = std::static_pointer_cast<DirtParticle>(dirtNodes[0]->data);
currentHydration = dp->hydration;
currentTemp = dp->temperature;
n = dp->nitrogen;
p = dp->phosphorus;
k = dp->potassium;
c = dp->carbon;
mg = dp->magnesium;
}
ImGui::Text("Soil Temp: %.1f °C", currentTemp);
ImGui::ProgressBar(std::min(currentHydration / 500.0f, 1.0f), ImVec2(-1, 0),
("Water: " + std::to_string((int)currentHydration)).c_str());
ImGui::Text("Nitrogen (N): %.1f", n);
ImGui::Text("Phosphorus (P): %.1f", p);
ImGui::Text("Potassium (K): %.1f", k);
ImGui::Text("Carbon (C): %.1f", c);
ImGui::Text("Magnesium (Mg): %.1f", mg);
}
if (ImGui::CollapsingHeader("Sun & Seasons", ImGuiTreeNodeFlags_DefaultOpen)) {
bool rebuildSun = false;
ImGui::Text("Time & Season");
ImGui::SliderFloat("Time of Day", &sim.config.timeOfDay, 0.0f, 1.0f);
int prevDay = sim.config.currentDay;
if (ImGui::SliderInt("Current Day", &sim.config.currentDay, 0, sim.config.daysPerYear - 1)) {
sim.config.season = (static_cast<float>(sim.config.currentDay) + sim.config.timeOfDay) / sim.config.daysPerYear;
}
ImGui::SliderFloat("Day Duration (s)", &sim.config.dayDuration, 1.0f, 600.0f);
if (ImGui::SliderInt("Days per Year", &sim.config.daysPerYear, 4, 365)) {
if (sim.config.currentDay >= sim.config.daysPerYear) sim.config.currentDay = 0;
}
ImGui::Separator();
ImGui::Text("Geography");
ImGui::SliderFloat("Latitude", &sim.config.latitude, -90.0f, 90.0f);
ImGui::SliderFloat("Axial Tilt", &sim.config.axialTilt, 0.0f, 90.0f);
ImGui::Separator();
ImGui::Text("Sun Appearance");
rebuildSun |= ImGui::SliderFloat("Distance", &sim.config.sunDistance, 10.0f, 100.0f);
rebuildSun |= ImGui::ColorEdit3("Color", sim.config.sunColor.data());
rebuildSun |= ImGui::DragFloat("Intensity", &sim.config.sunIntensity, 0.1f, 0.0f, 100.0f);
ImGui::Text("Weather Constraints");
ImGui::SliderFloat("Precipitation Rate", &sim.config.precipRate, 10.0f, 500.0f);
}
if (ImGui::CollapsingHeader("Simulation")) {
if (ImGui::Button("Reset World", ImVec2(-1, 0))) {
sim.initWorld();
}
}
if (ImGui::CollapsingHeader("Render Settings")) {
ImGui::Checkbox("High Quality (Slow)", &slowRender);
ImGui::Checkbox("Global Illumination", &globalIllumination);
ImGui::DragInt("Bounces", &reflectCount, 1, 0, 8);
}
}
void renderPreview() {
livePreview();
if (textureInitialized) {
float availW = ImGui::GetContentRegionAvail().x;
float aspect = (float)outWidth / (float)outHeight;
ImGui::Image((void*)(intptr_t)textu, ImVec2(availW, availW / aspect));
}
}
void livePreview() {
std::lock_guard<std::mutex> lock(PreviewMutex);
// Update Grid settings based on UI
sim.grid.setMaxDistance(maxDist);
// Render
if (slowRender) {
currentPreviewFrame = sim.grid.renderFrame(cam, outHeight, outWidth, frame::colormap::RGB, 10, reflectCount, globalIllumination, true);
} else {
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outHeight, outWidth, frame::colormap::RGB);
}
// Upload to GPU
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());
textureInitialized = true;
}
};
#endif

136
tests/ptest.cpp Normal file
View File

@@ -0,0 +1,136 @@
#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 "plant.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;
PlantSimUI plantApp;
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);
plantApp.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;
}

View File

@@ -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;
}

View File

@@ -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
View 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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because it is too large Load Diff

253
util/grid/gridmesh.hpp Normal file
View 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
View 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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View 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

View File

@@ -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;
}

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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
View 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
View 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

File diff suppressed because it is too large Load Diff

1023
util/sim/plant.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(float[3] acd) : 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

View File

@@ -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