Files
stupidsimcpp/d.cpp
Yggdrasil75 5a0d81134e init
2025-11-05 11:56:56 -05:00

659 lines
21 KiB
C++

#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 "timing_decorator.hpp"
#include <cstdint>
#include <string>
// Forward declarations
class Vec3;
class Vec4;
class Vec3 {
public:
float x, y, z;
// Constructors
Vec3() : x(0), y(0), z(0) {}
Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
// Arithmetic operations
Vec3 operator+(const Vec3& other) const {
return Vec3(x + other.x, y + other.y, z + other.z);
}
Vec3 operator-(const Vec3& other) const {
return Vec3(x - other.x, y - other.y, z - other.z);
}
Vec3 operator*(float scalar) const {
return Vec3(x * scalar, y * scalar, z * scalar);
}
Vec3 operator/(float scalar) const {
return Vec3(x / scalar, y / scalar, z / scalar);
}
// Dot product
float dot(const Vec3& other) const {
return x * other.x + y * other.y + z * other.z;
}
// Cross product
Vec3 cross(const Vec3& other) const {
return Vec3(
y * other.z - z * other.y,
z * other.x - x * other.z,
x * other.y - y * other.x
);
}
// Length and normalization
float length() const {
return std::sqrt(x * x + y * y + z * z);
}
Vec3 normalized() const {
float len = length();
if (len > 0) {
return *this / len;
}
return *this;
}
// Component-wise absolute value
Vec3 abs() const {
return Vec3(std::abs(x), std::abs(y), std::abs(z));
}
// Component-wise floor
Vec3 floor() const {
return Vec3(std::floor(x), std::floor(y), std::floor(z));
}
// Equality operator for use in hash maps
bool operator==(const Vec3& other) const {
return x == other.x && y == other.y && z == other.z;
}
inline Vec3 operator*(const Vec3& scalar) const {
return Vec3(x * scalar.x, y * scalar.y, z * scalar.z);
}
inline Vec3 operator/(const Vec3& scalar) const {
return Vec3(x / scalar.x, y / scalar.y, z / scalar.z);
}
};
// Hash function for Vec3 to use in unordered_map
namespace std {
template<>
struct hash<Vec3> {
size_t operator()(const Vec3& v) const {
return hash<float>()(v.x) ^ (hash<float>()(v.y) << 1) ^ (hash<float>()(v.z) << 2);
}
};
}
class Vec4 {
public:
float r, g, b, a;
// Constructors
Vec4() : r(0), g(0), b(0), a(1.0f) {}
Vec4(float r, float g, float b, float a = 1.0f) : r(r), g(g), b(b), a(a) {}
// Construct from Vec3 with alpha
Vec4(const Vec3& rgb, float a = 1.0f) : r(rgb.x), g(rgb.y), b(rgb.z), a(a) {}
// Arithmetic operations
Vec4 operator+(const Vec4& other) const {
return Vec4(r + other.r, g + other.g, b + other.b, a + other.a);
}
Vec4 operator-(const Vec4& other) const {
return Vec4(r - other.r, g - other.g, b - other.b, a - other.a);
}
Vec4 operator*(float scalar) const {
return Vec4(r * scalar, g * scalar, b * scalar, a * scalar);
}
Vec4 operator/(float scalar) const {
return Vec4(r / scalar, g / scalar, b / scalar, a / scalar);
}
// Component-wise multiplication
Vec4 operator*(const Vec4& other) const {
return Vec4(r * other.r, g * other.g, b * other.b, a * other.a);
}
// Clamp values between 0 and 1
Vec4 clamped() const {
return Vec4(
std::clamp(r, 0.0f, 1.0f),
std::clamp(g, 0.0f, 1.0f),
std::clamp(b, 0.0f, 1.0f),
std::clamp(a, 0.0f, 1.0f)
);
}
// Convert to Vec3 (ignoring alpha)
Vec3 toVec3() const {
return Vec3(r, g, b);
}
// Convert to 8-bit color values
void toUint8(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) const {
red = static_cast<uint8_t>(std::clamp(r, 0.0f, 1.0f) * 255);
green = static_cast<uint8_t>(std::clamp(g, 0.0f, 1.0f) * 255);
blue = static_cast<uint8_t>(std::clamp(b, 0.0f, 1.0f) * 255);
alpha = static_cast<uint8_t>(std::clamp(a, 0.0f, 1.0f) * 255);
}
void toUint8(uint8_t& red, uint8_t& green, uint8_t& blue) const {
red = static_cast<uint8_t>(std::clamp(r, 0.0f, 1.0f) * 255);
green = static_cast<uint8_t>(std::clamp(g, 0.0f, 1.0f) * 255);
blue = static_cast<uint8_t>(std::clamp(b, 0.0f, 1.0f) * 255);
}
};
class VoxelGrid {
private:
std::unordered_map<Vec3, size_t> positionToIndex;
std::vector<Vec3> positions;
std::vector<Vec4> colors;
Vec3 gridSize;
public:
Vec3 voxelSize;
VoxelGrid(const Vec3& size, const Vec3& voxelSize = Vec3(1, 1, 1))
: gridSize(size), voxelSize(voxelSize) {}
// Add a voxel at position with color
void addVoxel(const Vec3& position, const Vec4& color) {
Vec3 gridPos = worldToGrid(position);
// Check if voxel already exists
auto it = positionToIndex.find(gridPos);
if (it == positionToIndex.end()) {
// New voxel
size_t index = positions.size();
positions.push_back(gridPos);
colors.push_back(color);
positionToIndex[gridPos] = index;
} else {
// Update existing voxel (you might want to blend colors instead)
colors[it->second] = color;
}
}
// Get voxel color at position
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); // Transparent black for empty voxels
}
// Check if position is occupied
bool isOccupied(const Vec3& position) const {
Vec3 gridPos = worldToGrid(position);
return positionToIndex.find(gridPos) != positionToIndex.end();
}
// Convert world coordinates to grid coordinates
Vec3 worldToGrid(const Vec3& worldPos) const {
return (worldPos / voxelSize).floor();
}
// Convert grid coordinates to world coordinates
Vec3 gridToWorld(const Vec3& gridPos) const {
return gridPos * voxelSize;
}
// Get all occupied positions
const std::vector<Vec3>& getOccupiedPositions() const {
return positions;
}
// Get all colors
const std::vector<Vec4>& getColors() const {
return colors;
}
// Get the mapping from position to index
const std::unordered_map<Vec3, size_t>& getPositionToIndexMap() const {
return positionToIndex;
}
// Get grid size
const Vec3& getGridSize() const {
return gridSize;
}
// Get voxel size
const Vec3& getVoxelSize() const {
return voxelSize;
}
// Clear the grid
void clear() {
positions.clear();
colors.clear();
positionToIndex.clear();
}
};
class AmanatidesWooAlgorithm {
public:
struct Ray {
Vec3 origin;
Vec3 direction;
float tMax;
Ray(const Vec3& origin, const Vec3& direction, float tMax = 1000.0f)
: origin(origin), direction(direction.normalized()), tMax(tMax) {}
};
struct TraversalState {
Vec3 currentVoxel;
Vec3 tMax;
Vec3 tDelta;
Vec3 step;
bool hit;
float t;
TraversalState() : hit(false), t(0) {}
};
// Initialize traversal state for a ray
static TraversalState initTraversal(const Ray& ray, const Vec3& voxelSize) {
TraversalState state;
// Find the starting voxel
Vec3 startPos = ray.origin / voxelSize;
state.currentVoxel = startPos.floor();
// Determine step directions and initialize tMax
Vec3 rayDir = ray.direction;
Vec3 invDir = Vec3(
rayDir.x != 0 ? 1.0f / rayDir.x : std::numeric_limits<float>::max(),
rayDir.y != 0 ? 1.0f / rayDir.y : std::numeric_limits<float>::max(),
rayDir.z != 0 ? 1.0f / rayDir.z : std::numeric_limits<float>::max()
);
// Calculate step directions
state.step = Vec3(
rayDir.x > 0 ? 1 : (rayDir.x < 0 ? -1 : 0),
rayDir.y > 0 ? 1 : (rayDir.y < 0 ? -1 : 0),
rayDir.z > 0 ? 1 : (rayDir.z < 0 ? -1 : 0)
);
// Calculate tMax for each axis
Vec3 nextVoxelBoundary = state.currentVoxel;
if (state.step.x > 0) nextVoxelBoundary.x += 1;
if (state.step.y > 0) nextVoxelBoundary.y += 1;
if (state.step.z > 0) nextVoxelBoundary.z += 1;
state.tMax = Vec3(
(nextVoxelBoundary.x - startPos.x) * invDir.x,
(nextVoxelBoundary.y - startPos.y) * invDir.y,
(nextVoxelBoundary.z - startPos.z) * invDir.z
);
// Calculate tDelta
state.tDelta = Vec3(
state.step.x * invDir.x,
state.step.y * invDir.y,
state.step.z * invDir.z
);
state.hit = false;
state.t = 0;
return state;
}
// Traverse the grid along the ray
static bool traverse(const Ray& ray, const VoxelGrid& grid,
std::vector<Vec3>& hitVoxels, std::vector<float>& hitDistances,
int maxSteps = 1000) {
TraversalState state = initTraversal(ray, grid.voxelSize);
Vec3 voxelSize = grid.voxelSize;
hitVoxels.clear();
hitDistances.clear();
for (int step = 0; step < maxSteps; step++) {
// Check if current voxel is occupied
Vec3 worldPos = grid.gridToWorld(state.currentVoxel);
if (grid.isOccupied(worldPos)) {
hitVoxels.push_back(state.currentVoxel);
hitDistances.push_back(state.t);
}
// Find next voxel
if (state.tMax.x < state.tMax.y) {
if (state.tMax.x < state.tMax.z) {
state.currentVoxel.x += state.step.x;
state.t = state.tMax.x;
state.tMax.x += state.tDelta.x;
} else {
state.currentVoxel.z += state.step.z;
state.t = state.tMax.z;
state.tMax.z += state.tDelta.z;
}
} else {
if (state.tMax.y < state.tMax.z) {
state.currentVoxel.y += state.step.y;
state.t = state.tMax.y;
state.tMax.y += state.tDelta.y;
} else {
state.currentVoxel.z += state.step.z;
state.t = state.tMax.z;
state.tMax.z += state.tDelta.z;
}
}
// Check if we've exceeded maximum distance
if (state.t > ray.tMax) {
break;
}
}
return !hitVoxels.empty();
}
};
class BMPWriter {
private:
#pragma pack(push, 1)
struct BMPHeader {
uint16_t signature = 0x4D42; // "BM"
uint32_t fileSize;
uint16_t reserved1 = 0;
uint16_t reserved2 = 0;
uint32_t dataOffset = 54;
};
struct BMPInfoHeader {
uint32_t headerSize = 40;
int32_t width;
int32_t height;
uint16_t planes = 1;
uint16_t bitsPerPixel = 24;
uint32_t compression = 0;
uint32_t imageSize;
int32_t xPixelsPerMeter = 0;
int32_t yPixelsPerMeter = 0;
uint32_t colorsUsed = 0;
uint32_t importantColors = 0;
};
#pragma pack(pop)
public:
static bool saveBMP(const std::string& filename, const std::vector<uint8_t>& pixels, int width, int height) {
BMPHeader header;
BMPInfoHeader infoHeader;
int rowSize = (width * 3 + 3) & ~3; // 24-bit, padded to 4 bytes
int imageSize = rowSize * height;
header.fileSize = sizeof(BMPHeader) + sizeof(BMPInfoHeader) + imageSize;
infoHeader.width = width;
infoHeader.height = height;
infoHeader.imageSize = imageSize;
std::ofstream file(filename, std::ios::binary);
if (!file) {
return false;
}
file.write(reinterpret_cast<const char*>(&header), sizeof(header));
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
// Write pixel data (BMP stores pixels bottom-to-top)
std::vector<uint8_t> row(rowSize);
for (int y = height - 1; y >= 0; --y) {
const uint8_t* src = &pixels[y * width * 3];
std::memcpy(row.data(), src, width * 3);
file.write(reinterpret_cast<const char*>(row.data()), rowSize);
}
return true;
}
static bool saveVoxelGridSlice(const std::string& filename, const VoxelGrid& grid, int sliceZ) {
Vec3 gridSize = grid.getGridSize();
int width = static_cast<int>(gridSize.x);
int height = static_cast<int>(gridSize.y);
std::vector<uint8_t> pixels(width * height * 3, 0);
// Render the slice
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
Vec3 worldPos = grid.gridToWorld(Vec3(x, y, sliceZ));
Vec4 color = grid.getVoxel(worldPos);
int index = (y * width + x) * 3;
color.toUint8(pixels[index + 2], pixels[index + 1], pixels[index]); // BMP is BGR
}
}
return saveBMP(filename, pixels, width, height);
}
static bool saveRayTraceResults(const std::string& filename, const VoxelGrid& grid,
const std::vector<Vec3>& hitVoxels,
const AmanatidesWooAlgorithm::Ray& ray,
int width = 800, int height = 600) {
std::vector<uint8_t> pixels(width * height * 3, 0);
// Background color (dark gray)
for (int i = 0; i < width * height * 3; i += 3) {
pixels[i] = 50; // B
pixels[i + 1] = 50; // G
pixels[i + 2] = 50; // R
}
// Draw the grid bounds
Vec3 gridSize = grid.getGridSize();
drawGrid(pixels, width, height, gridSize);
// Draw hit voxels
for (const auto& voxel : hitVoxels) {
drawVoxel(pixels, width, height, voxel, gridSize, Vec4(1, 0, 0, 1)); // Red for hit voxels
}
// Draw the ray
drawRay(pixels, width, height, ray, gridSize);
return saveBMP(filename, pixels, width, height);
}
private:
static void drawGrid(std::vector<uint8_t>& pixels, int width, int height, const Vec3& gridSize) {
// Draw grid boundaries in white
for (int x = 0; x <= gridSize.x; ++x) {
int screenX = mapToScreenX(x, gridSize.x, width);
for (int y = 0; y < height; ++y) {
int index = (y * width + screenX) * 3;
if (y % 5 == 0) { // Dotted line
pixels[index] = 255; // B
pixels[index + 1] = 255; // G
pixels[index + 2] = 255; // R
}
}
}
for (int y = 0; y <= gridSize.y; ++y) {
int screenY = mapToScreenY(y, gridSize.y, height);
for (int x = 0; x < width; ++x) {
int index = (screenY * width + x) * 3;
if (x % 5 == 0) { // Dotted line
pixels[index] = 255; // B
pixels[index + 1] = 255; // G
pixels[index + 2] = 255; // R
}
}
}
}
static void drawVoxel(std::vector<uint8_t>& pixels, int width, int height,
const Vec3& voxel, const Vec3& gridSize, const Vec4& color) {
int screenX = mapToScreenX(voxel.x, gridSize.x, width);
int screenY = mapToScreenY(voxel.y, gridSize.y, height);
uint8_t r, g, b;
color.toUint8(r, g, b);
// Draw a 4x4 square for the voxel
for (int dy = -2; dy <= 2; ++dy) {
for (int dx = -2; dx <= 2; ++dx) {
int px = screenX + dx;
int py = screenY + dy;
if (px >= 0 && px < width && py >= 0 && py < height) {
int index = (py * width + px) * 3;
pixels[index] = b;
pixels[index + 1] = g;
pixels[index + 2] = r;
}
}
}
}
static void drawRay(std::vector<uint8_t>& pixels, int width, int height,
const AmanatidesWooAlgorithm::Ray& ray, const Vec3& gridSize) {
// Draw ray origin and direction
int originX = mapToScreenX(ray.origin.x, gridSize.x, width);
int originY = mapToScreenY(ray.origin.y, gridSize.y, height);
// Draw ray origin (green)
for (int dy = -3; dy <= 3; ++dy) {
for (int dx = -3; dx <= 3; ++dx) {
int px = originX + dx;
int py = originY + dy;
if (px >= 0 && px < width && py >= 0 && py < height) {
int index = (py * width + px) * 3;
pixels[index] = 0; // B
pixels[index + 1] = 255; // G
pixels[index + 2] = 0; // R
}
}
}
// Draw ray direction (yellow line)
Vec3 endPoint = ray.origin + ray.direction * 10.0f; // Extend ray
int endX = mapToScreenX(endPoint.x, gridSize.x, width);
int endY = mapToScreenY(endPoint.y, gridSize.y, height);
drawLine(pixels, width, height, originX, originY, endX, endY, Vec4(1, 1, 0, 1));
}
static void drawLine(std::vector<uint8_t>& pixels, int width, int height,
int x0, int y0, int x1, int y1, const Vec4& color) {
uint8_t r, g, b;
color.toUint8(r, g, b);
int dx = std::abs(x1 - x0);
int dy = std::abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
while (true) {
if (x0 >= 0 && x0 < width && y0 >= 0 && y0 < height) {
int index = (y0 * width + x0) * 3;
pixels[index] = b;
pixels[index + 1] = g;
pixels[index + 2] = r;
}
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
static int mapToScreenX(float x, float gridWidth, int screenWidth) {
return static_cast<int>((x / gridWidth) * screenWidth);
}
static int mapToScreenY(float y, float gridHeight, int screenHeight) {
return static_cast<int>((y / gridHeight) * screenHeight);
}
};
// Example usage
int main() {
// Create a voxel grid
VoxelGrid grid(Vec3(10, 10, 10), Vec3(1, 1, 1));
// Add some voxels
grid.addVoxel(Vec3(1, 1, 1), Vec4(1, 0, 0, 1)); // Red
grid.addVoxel(Vec3(2, 2, 2), Vec4(0, 1, 0, 0.5f)); // Green with transparency
grid.addVoxel(Vec3(3, 3, 3), Vec4(0, 0, 1, 1)); // Blue
grid.addVoxel(Vec3(4, 4, 4), Vec4(1, 1, 0, 1)); // Yellow
grid.addVoxel(Vec3(5, 5, 5), Vec4(1, 0, 1, 1)); // Magenta
// Create a ray
AmanatidesWooAlgorithm::Ray ray(Vec3(0, 0, 0), Vec3(1, 1, 1).normalized());
// Traverse the grid
std::vector<Vec3> hitVoxels;
std::vector<float> hitDistances;
bool hit = AmanatidesWooAlgorithm::traverse(ray, grid, hitVoxels, hitDistances);
if (hit) {
printf("Ray hit %zu voxels:\n", hitVoxels.size());
for (size_t i = 0; i < hitVoxels.size(); i++) {
Vec4 color = grid.getVoxel(grid.gridToWorld(hitVoxels[i]));
printf(" Voxel at (%.1f, %.1f, %.1f), distance: %.2f, color: (%.1f, %.1f, %.1f, %.1f)\n",
hitVoxels[i].x, hitVoxels[i].y, hitVoxels[i].z,
hitDistances[i],
color.r, color.g, color.b, color.a);
}
}
// Save results to BMP files
printf("\nSaving results to BMP files...\n");
// Save a slice of the voxel grid
if (BMPWriter::saveVoxelGridSlice("voxel_grid_slice.bmp", grid, 1)) {
printf("Saved voxel grid slice to 'voxel_grid_slice.bmp'\n");
} else {
printf("Failed to save voxel grid slice\n");
}
// Save ray tracing visualization
if (BMPWriter::saveRayTraceResults("ray_trace_results.bmp", grid, hitVoxels, ray)) {
printf("Saved ray trace results to 'ray_trace_results.bmp'\n");
} else {
printf("Failed to save ray trace results\n");
}
return 0;
}