diff --git a/tests/g3chromatic.cpp b/tests/g3chromatic.cpp index cefdb3c..59deec6 100644 --- a/tests/g3chromatic.cpp +++ b/tests/g3chromatic.cpp @@ -56,16 +56,16 @@ struct AnimationConfig { Grid3 setup(AnimationConfig config) { TIME_FUNCTION; Grid3 grid; - std::vector pos; - std::vector colors; + std::vector pos; + std::vector 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(Vec3(x,y,z)); - colors.push_back(Vec4(r, g, b, 1.0f)); + pos.push_back(Vec3f(x,y,z)); + colors.push_back(Vec4ui8(r, g, b, 1.0f)); } } } @@ -76,7 +76,7 @@ Grid3 setup(AnimationConfig config) { void Preview(AnimationConfig config, Grid3& grid) { TIME_FUNCTION; - frame rgbData = grid.getGridAsFrame(Vec2(config.width, config.height), Ray3(Vec3(config.width + 10,config.height + 10,config.depth + 10), Vec3(0)), frame::colormap::RGB); + 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) { @@ -87,7 +87,7 @@ void Preview(AnimationConfig config, Grid3& grid) { void livePreview(const Grid3& grid, AnimationConfig config) { // std::lock_guard lock(previewMutex); - // currentPreviewFrame = grid.getGridAsFrame(Vec2(config.width, config.height), Ray3(Vec3(config.width + 10,config.height + 10,config.depth + 10), Vec3(0)), frame::colormap::RGBA); + // 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); @@ -102,7 +102,7 @@ void livePreview(const Grid3& grid, AnimationConfig config) { // updatePreview = true; } -std::vector> pickSeeds(Grid3& grid, AnimationConfig config) { +std::vector> pickSeeds(Grid3& grid, AnimationConfig config) { TIME_FUNCTION; // std::cout << "picking seeds" << std::endl; std::random_device rd; @@ -112,11 +112,11 @@ std::vector> pickSeeds(Grid3& grid, AnimationConf std::uniform_int_distribution<> zDist(0, config.depth - 1); std::uniform_real_distribution<> colorDist(0.2f, 0.8f); - std::vector> seeds; + std::vector> seeds; for (int i = 0; i < config.numSeeds; ++i) { - Vec3 point(xDist(gen), yDist(gen), zDist(gen)); - Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255); + 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) { @@ -132,10 +132,10 @@ std::vector> pickSeeds(Grid3& grid, AnimationConf return seeds; } -void expandPixel(Grid3& grid, AnimationConfig config, std::vector>& seeds) { +void expandPixel(Grid3& grid, AnimationConfig config, std::vector>& seeds) { TIME_FUNCTION; std::cout << "expanding pixel" << std::endl; - std::vector> newseeds; + std::vector> newseeds; int counter = 0; std::unordered_set visitedThisFrame; @@ -144,10 +144,10 @@ void expandPixel(Grid3& grid, AnimationConfig config, std::vector& seed : seeds) { + for (const std::tuple& seed : seeds) { size_t id = std::get<0>(seed); - Vec3 seedPOS = std::get<1>(seed); - Vec4 seedColor = std::get<2>(seed); + Vec3f seedPOS = std::get<1>(seed); + Vec4ui8 seedColor = std::get<2>(seed); std::vector neighbors = grid.getNeighbors(id); for (size_t neighbor : neighbors) { std::cout << "counter at 1: " << counter++ << std::endl; @@ -155,13 +155,13 @@ void expandPixel(Grid3& grid, AnimationConfig config, std::vector lock(state.mutex); state.grid = grid; @@ -274,7 +274,7 @@ void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) { std::cout << "generated grid" << std::endl; Preview(config, grid); std::cout << "generated preview" << std::endl; - std::vector> seeds = pickSeeds(grid, config); + std::vector> seeds = pickSeeds(grid, config); std::vector frames; for (int i = 0; i < config.totalFrames; ++i){ @@ -294,7 +294,7 @@ void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) { //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(Vec3(config.width + 10,config.height + 10,config.depth + 10), Vec3(0)), frame::colormap::BGR); + 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(); diff --git a/util/grid/grid3.hpp b/util/grid/grid3.hpp index 00c3b62..992288b 100644 --- a/util/grid/grid3.hpp +++ b/util/grid/grid3.hpp @@ -2,7 +2,7 @@ #define GRID3_HPP #include -#include "../vectorlogic/vec3.hpp" +#include "../vectorlogic/Vec3.hpp" #include "../vectorlogic/vec4.hpp" #include "../timing_decorator.hpp" #include "../output/frame.hpp" @@ -20,41 +20,41 @@ constexpr int CHUNK_SIZE = 16; class GenericVoxel { protected: size_t id; - Vec4 color; - Vec3 pos; + Vec4ui8 color; + Vec3f pos; public: //constructors - GenericVoxel(size_t id, Vec4 color, Vec3 pos) : id(id), color(color), pos(pos) {}; - GenericVoxel() : id(0), color(Vec4()), pos(Vec3()) {}; + GenericVoxel(size_t id, Vec4ui8 color, Vec3f pos) : id(id), color(color), pos(pos) {}; + GenericVoxel() : id(0), color(Vec4ui8()), pos(Vec3f()) {}; //getters size_t getId() const { return id; } - Vec3 getPos() const { + Vec3f getPos() const { return pos; } - Vec4 getColor() const { + Vec4ui8 getColor() const { return color; } //setters - void setColor(Vec4 newColor) { + void setColor(Vec4ui8 newColor) { color = newColor; } - void setPos(Vec3 newPos) { + void setPos(Vec3f newPos) { pos = newPos; } void setId(size_t newId) { id = newId; } - void move(Vec3 newPos) { + void move(Vec3f newPos) { pos = newPos; } - void recolor(Vec4 newColor) { + void recolor(Vec4ui8 newColor) { color.recolor(newColor); } }; @@ -63,33 +63,33 @@ public: /// @details Maintains two hashmaps to allow O(1) lookup in either direction. class reverselookupassistant3 { private: - std::unordered_map Positions; - /// "Positions" reversed - stores the reverse mapping from Vec3 to ID. - std::unordered_map ƨnoiƚiƨoꟼ; + std::unordered_map Positions; + /// "Positions" reversed - stores the reverse mapping from Vec3f to ID. + std::unordered_map ƨnoiƚiƨoꟼ; size_t next_id; public: /// @brief Get the Position associated with a specific ID. /// @throws std::out_of_range if the ID does not exist. - Vec3 at(size_t id) const { + Vec3f at(size_t id) const { auto it = Positions.at(id); return it; } /// @brief Get the ID associated with a specific Position. /// @throws std::out_of_range if the Position does not exist. - size_t at(const Vec3& pos) const { + size_t at(const Vec3f& pos) const { size_t id = ƨnoiƚiƨoꟼ.at(pos); return id; } /// @brief Finds a position by ID (Wrapper for at). - Vec3 find(size_t id) { + Vec3f find(size_t id) { return Positions.at(id); } /// @brief Registers a new position and assigns it a unique ID. /// @return The newly generated ID. - size_t set(const Vec3& pos) { + size_t set(const Vec3f& pos) { size_t id = next_id++; Positions[id] = pos; ƨnoiƚiƨoꟼ[pos] = id; @@ -98,14 +98,14 @@ public: /// @brief Removes an entry by ID. size_t remove(size_t id) { - Vec3& pos = Positions[id]; + Vec3f& pos = Positions[id]; Positions.erase(id); ƨnoiƚiƨoꟼ.erase(pos); return id; } /// @brief Removes an entry by Position. - size_t remove(const Vec3& pos) { + size_t remove(const Vec3f& pos) { size_t id = ƨnoiƚiƨoꟼ[pos]; Positions.erase(id); ƨnoiƚiƨoꟼ.erase(pos); @@ -141,8 +141,8 @@ public: next_id = 0; } - using iterator = typename std::unordered_map::iterator; - using const_iterator = typename std::unordered_map::const_iterator; + using iterator = typename std::unordered_map::iterator; + using const_iterator = typename std::unordered_map::const_iterator; iterator begin() { return Positions.begin(); @@ -167,31 +167,51 @@ public: return (Positions.find(id) != Positions.end()); } - bool contains(const Vec3& pos) const { + bool contains(const Vec3f& pos) const { return (ƨnoiƚiƨoꟼ.find(pos) != ƨnoiƚiƨoꟼ.end()); } }; -class Chunk3 : GenericVoxel { +class Chunk3 : public GenericVoxel { private: - Vec3 chunkCoord; + Vec3f chunkCoord; std::unordered_set voxelIDs; - std::vector storedValues; bool isCompressed = false; int detailLevel; std::vector fullVoxels; + std::vector compressedVoxels; public: - Chunk3(const Vec3& coord) : chunkCoord(coord) {} + //overload GenericVoxel + Vec4ui8 getColor() { + if (isCompressed) { + return color; + } else { + if (fullVoxels.empty()) { + return Vec4ui8(); + } + + Vec4ui8 accumulatedColor(0, 0, 0, 0); + + for (const auto& voxel : fullVoxels) { + accumulatedColor = accumulatedColor + voxel.getColor(); + } + + float count = static_cast(fullVoxels.size()); + return accumulatedColor / count; + } + } - Vec3 getCoord() const { return chunkCoord; } + Chunk3(const Vec3f& coord) : chunkCoord(coord) {} - std::pair getBounds() const { - Vec3 minBound( + Vec3f getCoord() const { return chunkCoord; } + + std::pair getBounds() const { + Vec3f minBound( chunkCoord.x*CHUNK_SIZE, chunkCoord.y*CHUNK_SIZE, chunkCoord.z*CHUNK_SIZE ); - Vec3 maxBound( + Vec3f maxBound( minBound.x+CHUNK_SIZE, minBound.y+CHUNK_SIZE, minBound.z+CHUNK_SIZE @@ -199,14 +219,14 @@ public: return {minBound, maxBound}; } - Vec3 worldToChunkPos(const Vec3& worldPos) const { + Vec3f worldToChunkPos(const Vec3f& worldPos) const { auto [minBound, _] = getBounds(); return worldPos - minBound; } std::vector compress() { - for (auto value : storedValues) { - Vec4 sc = value.getColor() / CHUNK_SIZE; + for (auto value : fullVoxels) { + } } @@ -217,26 +237,26 @@ class SpatialGrid3 { private: float cellSize; public: - std::unordered_map, Vec3::Hash> grid; + std::unordered_map, Vec3f::Hash> grid; /// @brief Initializes the spatial grid. /// @param cellSize The dimension of the spatial buckets. Larger cells mean more items per bucket but fewer buckets. SpatialGrid3(float cellSize = 2.0f) : cellSize(cellSize) {} /// @brief Converts world coordinates to spatial grid coordinates. - Vec3 worldToGrid(const Vec3& worldPos) const { + Vec3f worldToGrid(const Vec3f& worldPos) const { return (worldPos / cellSize).floor(); } /// @brief Adds an object ID to the spatial index at the given position. - void insert(size_t id, const Vec3& pos) { - Vec3 gridPos = worldToGrid(pos); + void insert(size_t id, const Vec3f& pos) { + Vec3f gridPos = worldToGrid(pos); grid[gridPos].insert(id); } /// @brief Removes an object ID from the spatial index. - void remove(size_t id, const Vec3& pos) { - Vec3 gridPos = worldToGrid(pos); + void remove(size_t id, const Vec3f& pos) { + Vec3f gridPos = worldToGrid(pos); auto cellIt = grid.find(gridPos); if (cellIt != grid.end()) { cellIt->second.erase(id); @@ -247,9 +267,9 @@ public: } /// @brief Moves an object within the spatial index (removes from old cell, adds to new if changed). - void update(size_t id, const Vec3& oldPos, const Vec3& newPos) { - Vec3 oldGridPos = worldToGrid(oldPos); - Vec3 newGridPos = worldToGrid(newPos); + void update(size_t id, const Vec3f& oldPos, const Vec3f& newPos) { + Vec3f oldGridPos = worldToGrid(oldPos); + Vec3f newGridPos = worldToGrid(newPos); if (oldGridPos != newGridPos) { remove(id, oldPos); @@ -258,7 +278,7 @@ public: } /// @brief Returns all IDs located in the specific grid cell containing 'center'. - std::unordered_set find(const Vec3& center) const { + std::unordered_set find(const Vec3f& center) const { auto cellIt = grid.find(worldToGrid(center)); if (cellIt != grid.end()) { return cellIt->second; @@ -270,13 +290,13 @@ public: /// @param center The world position center. /// @param radius The search radius (defines the bounds of grid cells to check). /// @return A vector of candidate IDs (Note: this returns objects in valid grid cells, further distance checks may be required). - std::vector queryRange(const Vec3& center, float radius) const { + std::vector queryRange(const Vec3f& center, float radius) const { std::vector results; float radiusSq = radius * radius; // Calculate grid bounds for the query - Vec3 minGrid = worldToGrid(center - Vec3(radius, radius, radius)); - Vec3 maxGrid = worldToGrid(center + Vec3(radius, radius, radius)); + Vec3f minGrid = worldToGrid(center - Vec3f(radius, radius, radius)); + Vec3f maxGrid = worldToGrid(center + Vec3f(radius, radius, radius)); size_t estimatedSize = (maxGrid.x - minGrid.x + 1) * (maxGrid.y - minGrid.y + 1) * (maxGrid.z - minGrid.z + 1) * 10; results.reserve(estimatedSize); @@ -285,7 +305,7 @@ public: for (int x = minGrid.x; x <= maxGrid.x; ++x) { for (int y = minGrid.y; y <= maxGrid.y; ++y) { for (int z = minGrid.z; z <= minGrid.z; ++z) { - auto cellIt = grid.find(Vec3(x, y, z)); + auto cellIt = grid.find(Vec3f(x, y, z)); if (cellIt != grid.end()) { results.insert(results.end(), cellIt->second.begin(), cellIt->second.end()); } @@ -316,40 +336,40 @@ protected: float spatialCellSize = neighborRadius * 1.5f; // Default background color for empty spaces - Vec4 defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); + Vec4ui8 defaultBackgroundColor = Vec4ui8(0, 0, 0, 0); PNoise2 noisegen; bool regenpreventer = false; public: - Grid3& noiseGenGrid(Vec3 min, Vec3 max, float minChance = 0.1f + Grid3& noiseGenGrid(Vec3f min, Vec3f max, float minChance = 0.1f , float maxChance = 1.0f, bool color = true, int noisemod = 42) { TIME_FUNCTION; noisegen = PNoise2(noisemod); std::cout << "generating a noise grid with the following: "<< min << " by " << max << "chance min: " << minChance << " max: " << maxChance << " gen colors: " << color << std::endl; - std::vector poses; - std::vector colors; + std::vector poses; + std::vector colors; for (int x = min.x; x < max.x; x++) { for (int y = min.y; y < max.y; y++) { for (int z = min.z; z < max.z; z++) { float nx = (x+noisemod)/(max.x+EPSILON)/0.1; float ny = (y+noisemod)/(max.y+EPSILON)/0.1; float nz = (z+noisemod)/(max.z+EPSILON)/0.1; - Vec3 pos = Vec3(nx,ny,nz); + Vec3f pos = Vec3f(nx,ny,nz); float alpha = noisegen.permute(pos); if (alpha > minChance && alpha < maxChance) { if (color) { - float red = noisegen.permute(Vec3(nx, ny, nz)*0.3); - float green = noisegen.permute(Vec3(nx, ny, nz)*0.6); - float blue = noisegen.permute(Vec3(nx, ny, nz)*0.9); - Vec4 newc = Vec4(red,green,blue,1.0); + float red = noisegen.permute(Vec3f(nx, ny, nz)*0.3); + float green = noisegen.permute(Vec3f(nx, ny, nz)*0.6); + float blue = noisegen.permute(Vec3f(nx, ny, nz)*0.9); + Vec4 newc = Vec4ui8(red,green,blue,1.0); colors.push_back(newc); - poses.push_back(Vec3(x,y,z)); + poses.push_back(Vec3f(x,y,z)); } else { - Vec4 newc = Vec4(alpha,alpha,alpha,1.0); + Vec4 newc = Vec4ui8(alpha,alpha,alpha,1.0); colors.push_back(newc); - poses.push_back(Vec3(x,y,z)); + poses.push_back(Vec3f(x,y,z)); } } } @@ -360,7 +380,7 @@ public: return *this; } - size_t addObject(const Vec3& pos, const Vec4& color, float size = 1.0f) { + size_t addObject(const Vec3f& pos, const Vec4ui8& color, float size = 1.0f) { size_t id = Positions.set(pos); Pixels.emplace(id, GenericVoxel(id, color, pos)); spatialGrid.insert(id, pos); @@ -368,19 +388,19 @@ public: } /// @brief Sets the default background color. - void setDefault(const Vec4& color) { + void setDefault(const Vec4ui8& color) { defaultBackgroundColor = color; } /// @brief Moves an object to a new position and updates spatial indexing. - void setPosition(size_t id, const Vec3& newPosition) { - Vec3 oldPosition = Positions.at(id); + void setPosition(size_t id, const Vec3f& newPosition) { + Vec3f oldPosition = Positions.at(id); Pixels.at(id).move(newPosition); spatialGrid.update(id, oldPosition, newPosition); Positions.at(id).move(newPosition); } - void setColor(size_t id, const Vec4 color) { + void setColor(size_t id, const Vec4ui8 color) { Pixels.at(id).recolor(color); } @@ -389,20 +409,20 @@ public: //optimizeSpatialGrid(); } - Vec4 getDefaultBackgroundColor() const { + Vec4ui8 getDefaultBackgroundColor() const { return defaultBackgroundColor; } - Vec3 getPositionID(size_t id) const { - Vec3 it = Positions.at(id); + Vec3f getPositionID(size_t id) const { + Vec3f it = Positions.at(id); return it; } - size_t getPositionVec(const Vec3& pos, float radius = 0.0f) const { + size_t getPositionVec(const Vec3f& pos, float radius = 0.0f) const { TIME_FUNCTION; if (radius == 0.0f) { // Exact match - use spatial grid to find the cell - Vec3 gridPos = spatialGrid.worldToGrid(pos); + Vec3f gridPos = spatialGrid.worldToGrid(pos); auto cellIt = spatialGrid.grid.find(gridPos); if (cellIt != spatialGrid.grid.end()) { for (size_t id : cellIt->second) { @@ -421,10 +441,10 @@ public: } } - size_t getOrCreatePositionVec(const Vec3& pos, float radius = 0.0f, bool create = true) { + size_t getOrCreatePositionVec(const Vec3f& pos, float radius = 0.0f, bool create = true) { //TIME_FUNCTION; //called too many times and average time is less than 0.0000001 so ignore it. if (radius == 0.0f) { - Vec3 gridPos = spatialGrid.worldToGrid(pos); + Vec3f gridPos = spatialGrid.worldToGrid(pos); auto cellIt = spatialGrid.grid.find(gridPos); if (cellIt != spatialGrid.grid.end()) { for (size_t id : cellIt->second) { @@ -449,7 +469,7 @@ public: } } - std::vector getPositionVecRegion(const Vec3& pos, float radius = 1.0f) const { + std::vector getPositionVecRegion(const Vec3f& pos, float radius = 1.0f) const { //TIME_FUNCTION; float searchRadius = (radius == 0.0f) ? std::numeric_limits::epsilon() : radius; @@ -469,16 +489,16 @@ public: return results; } - Vec4 getColor(size_t id) { + Vec4ui8 getColor(size_t id) { return Pixels.at(id).getColor(); } - std::pair getBoundingBox(Vec3& minCorner, Vec3& maxCorner) const { + std::pair getBoundingBox(Vec3f& minCorner, Vec3f& maxCorner) const { TIME_FUNCTION; if (Positions.empty()) { std::cout << "empty" << std::endl; - minCorner = Vec3(0, 0, 0); - maxCorner = Vec3(0, 0, 0); + minCorner = Vec3f(0, 0, 0); + maxCorner = Vec3f(0, 0, 0); } // Initialize with first position @@ -499,8 +519,8 @@ public: return std::make_pair(minCorner, maxCorner); } - frame getGridRegionAsFrame(const Vec3& minCorner, const Vec3& maxCorner, const Vec2& res, - const Ray3& View, frame::colormap outChannels = frame::colormap::RGB) const { + frame getGridRegionAsFrame(const Vec3f& minCorner, const Vec3f& maxCorner, const Vec2& res, + const Ray3& View, frame::colormap outChannels = frame::colormap::RGB) const { TIME_FUNCTION; // Calculate volume dimensions @@ -520,8 +540,8 @@ public: frame outframe(outputWidth, outputHeight, outChannels); - std::unordered_map colorBuffer; - std::unordered_map colorAccumBuffer; + std::unordered_map colorBuffer; + std::unordered_map colorAccumBuffer; std::unordered_map countBuffer; std::unordered_map depthBuffer; @@ -531,11 +551,11 @@ public: countBuffer.reserve(bufferSize); depthBuffer.reserve(bufferSize); - Vec3 viewDirection = View.direction; - Vec3 viewOrigin = View.origin; + Vec3f viewDirection = View.direction; + Vec3f viewOrigin = View.origin; - Vec3 viewRight = Vec3(1, 0, 0); - Vec3 viewUp = Vec3(0, 1, 0); + Vec3f viewRight = Vec3f(1, 0, 0); + Vec3f viewUp = Vec3f(0, 1, 0); float xScale = outputWidth / width; float yScale = outputHeight / height; @@ -553,10 +573,10 @@ public: float relY = pos.y - minCorner.y; float relZ = pos.z - minCorner.z; - Vec3 toVoxel = pos - viewOrigin; + Vec3f toVoxel = pos - viewOrigin; float distance = toVoxel.length(); - Vec3 viewPlanePos = pos - (toVoxel.dot(viewDirection)) * viewDirection; + Vec3f viewPlanePos = pos - (toVoxel.dot(viewDirection)) * viewDirection; float screenX = viewPlanePos.dot(viewRight); float screenY = viewPlanePos.dot(viewUp); @@ -569,7 +589,7 @@ public: Vec2 pixelPos(pixX, pixY); - Vec4 voxelColor = Pixels.at(id).getColor(); + Vec4ui8 voxelColor = Pixels.at(id).getColor(); float depth = relZ; @@ -603,7 +623,7 @@ public: Vec2 pixelPos(x, y); size_t index = (y * outputWidth + x) * 4; - Vec4 finalColor; + Vec4ui8 finalColor; auto countIt = countBuffer.find(pixelPos); if (countIt != countBuffer.end() && countIt->second > 0) { @@ -633,7 +653,7 @@ public: Vec2 pixelPos(x, y); size_t index = (y * outputWidth + x) * 3; - Vec4 finalColor; + Vec4ui8 finalColor; auto countIt = countBuffer.find(pixelPos); if (countIt != countBuffer.end() && countIt->second > 0) { @@ -663,7 +683,7 @@ public: Vec2 pixelPos(x, y); size_t index = (y * outputWidth + x) * 3; - Vec4 finalColor; + Vec4ui8 finalColor; auto countIt = countBuffer.find(pixelPos); if (countIt != countBuffer.end() && countIt->second > 0) { @@ -688,16 +708,16 @@ public: return outframe; } - frame getGridAsFrame(const Vec2& res, const Ray3& View, frame::colormap outChannels = frame::colormap::RGB) const { - Vec3 Min; - Vec3 Max; + frame getGridAsFrame(const Vec2& res, const Ray3& View, frame::colormap outChannels = frame::colormap::RGB) const { + Vec3f Min; + Vec3f Max; auto a = getBoundingBox(Min, Max); return getGridRegionAsFrame(a.first, a.second, res, View, outChannels); } size_t removeID(size_t id) { - Vec3 oldPosition = Positions.at(id); + Vec3f oldPosition = Positions.at(id); Positions.remove(id); Pixels.erase(id); unassignedIDs.push_back(id); @@ -705,17 +725,17 @@ public: return id; } - void bulkUpdatePositions(const std::unordered_map& newPositions) { + void bulkUpdatePositions(const std::unordered_map& newPositions) { TIME_FUNCTION; for (const auto& [id, newPos] : newPositions) { - Vec3 oldPosition = Positions.at(id); + Vec3f oldPosition = Positions.at(id); Positions.at(id).move(newPos); Pixels.at(id).move(newPos); spatialGrid.update(id, oldPosition, newPos); } } - std::vector bulkAddObjects(const std::vector poses, std::vector colors) { + std::vector bulkAddObjects(const std::vector poses, std::vector colors) { TIME_FUNCTION; std::vector ids; ids.reserve(poses.size()); @@ -749,7 +769,7 @@ public: Pixels.clear(); spatialGrid.clear(); Pixels.rehash(0); - defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); + defaultBackgroundColor = Vec4ui8(0, 0, 0, 0); } void optimizeSpatialGrid() { @@ -766,7 +786,7 @@ public: } std::vector getNeighbors(size_t id) const { - Vec3 pos = Positions.at(id); + Vec3f pos = Positions.at(id); std::vector candidates = spatialGrid.queryRange(pos, neighborRadius); std::vector neighbors; float radiusSq = neighborRadius * neighborRadius; @@ -787,7 +807,7 @@ public: } std::vector getNeighborsRange(size_t id, float dist) const { - Vec3 pos = Positions.at(id); + Vec3f pos = Positions.at(id); std::vector candidates = spatialGrid.queryRange(pos, neighborRadius); std::vector neighbors; @@ -804,17 +824,17 @@ public: Grid3& backfillGrid() { TIME_FUNCTION; - Vec3 Min; - Vec3 Max; + Vec3f Min; + Vec3f Max; getBoundingBox(Min, Max); - std::vector newPos; - std::vector newColors; + std::vector newPos; + std::vector newColors; for (size_t x = Min.x; x < Max.x; x++) { for (size_t y = Min.y; y < Max.y; y++) { for (size_t z = Min.z; z < Max.z; z++) { - Vec3 pos = Vec3(x,y,z); + Vec3f pos = Vec3f(x,y,z); if (Positions.contains(pos)) continue; - Vec4 color = defaultBackgroundColor; + Vec4ui8 color = defaultBackgroundColor; float size = 0.1; newPos.push_back(pos); newColors.push_back(color); diff --git a/util/noise/pnoise2.hpp b/util/noise/pnoise2.hpp index 9e80527..d4de1fa 100644 --- a/util/noise/pnoise2.hpp +++ b/util/noise/pnoise2.hpp @@ -7,7 +7,7 @@ #include #include #include "../vectorlogic/vec2.hpp" -#include "../vectorlogic/vec3.hpp" +#include "../vectorlogic/Vec3.hpp" #include "../timing_decorator.hpp" class PNoise2 { @@ -31,16 +31,16 @@ private: else return Vec2(1,-1); } - Vec3 GetConstantVector3(int v) { + Vec3ui8 GetConstantVector3(int v) { int h = v & 7; - if (h == 0) return Vec3(1,1,1); - else if (h == 1) return Vec3(-1,1, 1); - else if (h == 2) return Vec3(-1,-1, 1); - else if (h == 3) return Vec3(-1,-1, 1); - else if (h == 4) return Vec3(-1,-1,-1); - else if (h == 5) return Vec3(-1,-1, -1); - else if (h == 6) return Vec3(-1,-1, -1); - else return Vec3(1,-1, -1); + if (h == 0) return Vec3ui8(1,1,1); + else if (h == 1) return Vec3ui8(-1,1, 1); + else if (h == 2) return Vec3ui8(-1,-1, 1); + else if (h == 3) return Vec3ui8(-1,-1, 1); + else if (h == 4) return Vec3ui8(-1,-1,-1); + else if (h == 5) return Vec3ui8(-1,-1, -1); + else if (h == 6) return Vec3ui8(-1,-1, -1); + else return Vec3ui8(1,-1, -1); } static double grad(int hash, double x, double y, double z = 0.0) { @@ -106,7 +106,8 @@ public: return retval; } - float permute(Vec3 point) { + template + float permute(Vec3 point) { TIME_FUNCTION; int X = (int)floor(point.x) & 255; int Y = (int)floor(point.y) & 255; @@ -115,15 +116,15 @@ public: float yf = point.y - Y; float zf = point.z - Z; - Vec3 FBL = Vec3(xf-0, yf-0, zf-0); - Vec3 FBR = Vec3(xf-1, yf-0, zf-0); - Vec3 FTL = Vec3(xf-0, yf-1, zf-0); - Vec3 FTR = Vec3(xf-1, yf-1, zf-0); + Vec3ui8 FBL = Vec3ui8(xf-0, yf-0, zf-0); + Vec3ui8 FBR = Vec3ui8(xf-1, yf-0, zf-0); + Vec3ui8 FTL = Vec3ui8(xf-0, yf-1, zf-0); + Vec3ui8 FTR = Vec3ui8(xf-1, yf-1, zf-0); - Vec3 RBL = Vec3(xf-0, yf-0, zf-1); - Vec3 RBR = Vec3(xf-1, yf-0, zf-1); - Vec3 RTL = Vec3(xf-0, yf-1, zf-1); - Vec3 RTR = Vec3(xf-1, yf-1, zf-1); + Vec3ui8 RBL = Vec3ui8(xf-0, yf-0, zf-1); + Vec3ui8 RBR = Vec3ui8(xf-1, yf-0, zf-1); + Vec3ui8 RTL = Vec3ui8(xf-0, yf-1, zf-1); + Vec3ui8 RTR = Vec3ui8(xf-1, yf-1, zf-1); int vFBL = permutation[permutation[permutation[Z+0]+X+0]+Y+0]; int vFBR = permutation[permutation[permutation[Z+0]+X+1]+Y+0]; @@ -165,5 +166,4 @@ public: }; -#endif -//https://rtouti.github.io/graphics/perlin-noise-algorithm +#endif \ No newline at end of file diff --git a/util/output/bmpwriter.hpp b/util/output/bmpwriter.hpp index 791d42a..b3c2a0c 100644 --- a/util/output/bmpwriter.hpp +++ b/util/output/bmpwriter.hpp @@ -7,7 +7,7 @@ #include #include #include -#include "../vectorlogic/vec3.hpp" +#include "../vectorlogic/Vec3.hpp" #include "frame.hpp" class BMPWriter { @@ -49,9 +49,9 @@ private: } public: - // Save a 2D vector of Vec3 (RGB) colors as BMP - // Vec3 components: x = red, y = green, z = blue (values in range [0,1]) - static bool saveBMP(const std::string& filename, const std::vector>& pixels) { + // Save a 2D vector of Vec3ui8 (RGB) colors as BMP + // Vec3ui8 components: x = red, y = green, z = blue (values in range [0,1]) + static bool saveBMP(const std::string& filename, const std::vector>& pixels) { if (pixels.empty() || pixels[0].empty()) { return false; } @@ -70,13 +70,13 @@ public: } // Alternative interface with width/height and flat vector (row-major order) - static bool saveBMP(const std::string& filename, const std::vector& pixels, int width, int height) { + static bool saveBMP(const std::string& filename, const std::vector& pixels, int width, int height) { if (pixels.size() != width * height) { return false; } // Convert to 2D vector format - std::vector> pixels2D(height, std::vector(width)); + std::vector> pixels2D(height, std::vector(width)); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { pixels2D[y][x] = pixels[y * width + x]; @@ -157,7 +157,7 @@ public: } private: - static bool saveBMP(const std::string& filename, const std::vector>& pixels, int width, int height) { + static bool saveBMP(const std::string& filename, const std::vector>& pixels, int width, int height) { // Create directory if needed if (!createDirectoryIfNeeded(filename)) { return false; @@ -186,7 +186,7 @@ private: std::vector row(rowSize, 0); for (int y = height - 1; y >= 0; --y) { for (int x = 0; x < width; ++x) { - const Vec3& color = pixels[y][x]; + const Vec3ui8& color = pixels[y][x]; // Convert from [0,1] float to [0,255] uint8_t uint8_t r = static_cast(std::clamp(color.x * 255.0f, 0.0f, 255.0f)); diff --git a/util/ray3.hpp b/util/ray3.hpp index 260cf6d..9ba2b6d 100644 --- a/util/ray3.hpp +++ b/util/ray3.hpp @@ -1,31 +1,32 @@ #ifndef RAY3_HPP #define RAY3_HPP -#include "vectorlogic/vec3.hpp" +#include "vectorlogic/Vec3.hpp" +template class Ray3 { public: - Vec3 origin; - Vec3 direction; + Vec3 origin; + Vec3 direction; - Ray3() : origin(Vec3()), direction(Vec3(1, 0, 0)) {} - Ray3(const Vec3& origin, const Vec3& direction) + Ray3() : origin(Vec3()), direction(Vec3(1, 0, 0)) {} + Ray3(const Vec3& origin, const Vec3& direction) : origin(origin), direction(direction.normalized()) {} // Get point at parameter t along the ray - Vec3 at(float t) const { + Vec3 at(float t) const { return origin + direction * t; } // Reflect ray off a surface with given normal - Ray3 reflect(const Vec3& point, const Vec3& normal) const { - Vec3 reflectedDir = direction.reflect(normal); + Ray3 reflect(const Vec3& point, const Vec3& normal) const { + Vec3 reflectedDir = direction.reflect(normal); return Ray3(point, reflectedDir); } // Check if ray intersects with a sphere - bool intersectsSphere(const Vec3& center, float radius, float& t1, float& t2) const { - Vec3 oc = origin - center; + bool intersectsSphere(const Vec3& center, T radius, T& t1, T& t2) const { + Vec3 oc = origin - center; float a = direction.dot(direction); float b = 2.0f * oc.dot(direction); float c = oc.dot(oc) - radius * radius; @@ -44,7 +45,7 @@ public: } // Check if ray intersects with a plane (defined by point and normal) - bool intersectsPlane(const Vec3& planePoint, const Vec3& planeNormal, float& t) const { + bool intersectsPlane(const Vec3& planePoint, const Vec3& planeNormal, T& t) const { float denom = planeNormal.dot(direction); if (std::abs(denom) < 1e-10f) { @@ -56,9 +57,9 @@ public: } // Get the distance from a point to this ray - float distanceToPoint(const Vec3& point) const { - Vec3 pointToOrigin = point - origin; - Vec3 crossProduct = direction.cross(pointToOrigin); + float distanceToPoint(const Vec3& point) const { + Vec3 pointToOrigin = point - origin; + Vec3 crossProduct = direction.cross(pointToOrigin); return crossProduct.length() / direction.length(); } diff --git a/util/vectorlogic/vec3.hpp b/util/vectorlogic/vec3.hpp index c68819b..2af1f96 100644 --- a/util/vectorlogic/vec3.hpp +++ b/util/vectorlogic/vec3.hpp @@ -6,17 +6,18 @@ #include #include +template class Vec3 { public: - float x, y, z; + T x, y, z; Vec3() : x(0), y(0), z(0) {} - Vec3(float x, float y, float z) : x(x), y(y), z(z) {} - Vec3(float scalar) : x(scalar), y(scalar), z(scalar) {} + Vec3(T x, T y, T z) : x(x), y(y), z(z) {} + Vec3(T scalar) : x(scalar), y(scalar), z(scalar) {} - Vec3(const class Vec2& vec2, float z = 0.0f); + Vec3(const class Vec2& vec2, T z = 0); - Vec3& move(const Vec3 newpos) { + Vec3& move(const Vec3& newpos) { x = newpos.x; y = newpos.y; z = newpos.z; @@ -40,11 +41,11 @@ public: return Vec3(x / other.x, y / other.y, z / other.z); } - Vec3 operator+(float scalar) const { + Vec3 operator+(T scalar) const { return Vec3(x + scalar, y + scalar, z + scalar); } - Vec3 operator-(float scalar) const { + Vec3 operator-(T scalar) const { return Vec3(x - scalar, y - scalar, z - scalar); } @@ -52,15 +53,15 @@ public: return Vec3(-x, -y, -z); } - Vec3 operator*(float scalar) const { + Vec3 operator*(T scalar) const { return Vec3(x * scalar, y * scalar, z * scalar); } - Vec3 operator/(float scalar) const { + Vec3 operator/(T scalar) const { return Vec3(x / scalar, y / scalar, z / scalar); } - Vec3& operator=(float scalar) { + Vec3& operator=(T scalar) { x = y = z = scalar; return *this; } @@ -93,28 +94,28 @@ public: return *this; } - Vec3& operator+=(float scalar) { + Vec3& operator+=(T scalar) { x += scalar; y += scalar; z += scalar; return *this; } - Vec3& operator-=(float scalar) { + Vec3& operator-=(T scalar) { x -= scalar; y -= scalar; z -= scalar; return *this; } - Vec3& operator*=(float scalar) { + Vec3& operator*=(T scalar) { x *= scalar; y *= scalar; z *= scalar; return *this; } - Vec3& operator/=(float scalar) { + Vec3& operator/=(T scalar) { x /= scalar; y /= scalar; z /= scalar; @@ -125,7 +126,7 @@ public: return x * other.x + y * other.y + z * other.z; } - Vec3 cross(const Vec3& other) const { + Vec3& cross(const Vec3& other) const { return Vec3( y * other.z - z * other.y, z * other.x - x * other.z, @@ -133,25 +134,25 @@ public: ); } - float length() const { - return std::sqrt(x * x + y * y + z * z); + T length() const { + return static_cast(std::sqrt(static_cast(x * x + y * y + z * z))); } - float lengthSquared() const { + T lengthSquared() const { return x * x + y * y + z * z; } - float distance(const Vec3& other) const { + T distance(const Vec3& other) const { return (*this - other).length(); } - float distanceSquared(const Vec3& other) const { + T distanceSquared(const Vec3& other) const { Vec3 diff = *this - other; return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; } Vec3 normalized() const { - float len = length(); + T len = length(); if (len > 0) { return *this / len; } @@ -222,7 +223,7 @@ public: ); } - Vec3 clamp(float minVal, float maxVal) const { + Vec3 clamp(T minVal, T maxVal) const { return Vec3( std::clamp(x, minVal, maxVal), std::clamp(y, minVal, maxVal), @@ -240,19 +241,19 @@ public: std::abs(z - other.z) < epsilon; } - friend Vec3 operator+(float scalar, const Vec3& vec) { + friend Vec3 operator+(T scalar, const Vec3& vec) { return Vec3(scalar + vec.x, scalar + vec.y, scalar + vec.z); } - friend Vec3 operator-(float scalar, const Vec3& vec) { + friend Vec3 operator-(T scalar, const Vec3& vec) { return Vec3(scalar - vec.x, scalar - vec.y, scalar - vec.z); } - friend Vec3 operator*(float scalar, const Vec3& vec) { + friend Vec3 operator*(T scalar, const Vec3& vec) { return Vec3(scalar * vec.x, scalar * vec.y, scalar * vec.z); } - friend Vec3 operator/(float scalar, const Vec3& vec) { + friend Vec3 operator/(T scalar, const Vec3& vec) { return Vec3(scalar / vec.x, scalar / vec.y, scalar / vec.z); } @@ -260,17 +261,17 @@ public: return *this - 2.0f * this->dot(normal) * normal; } - Vec3 lerp(const Vec3& other, float t) const { + Vec3 lerp(const Vec3& other, T t) const { t = std::clamp(t, 0.0f, 1.0f); return *this + (other - *this) * t; } - Vec3 slerp(const Vec3& other, float t) const { + Vec3 slerp(const Vec3& other, T t) const { t = std::clamp(t, 0.0f, 1.0f); - float dot = this->dot(other); + T dot = this->dot(other); dot = std::clamp(dot, -1.0f, 1.0f); - float theta = std::acos(dot) * t; + T theta = std::acos(dot) * t; Vec3 relative = other - *this * dot; relative = relative.normalized(); @@ -326,11 +327,11 @@ public: return direction.angleTo(other); } - float& operator[](int index) { + T& operator[](int index) { return (&x)[index]; } - const float& operator[](int index) const { + const T& operator[](int index) const { return (&x)[index]; } @@ -345,16 +346,22 @@ public: }; }; -inline std::ostream& operator<<(std::ostream& os, const Vec3& vec) { +using Vec3f = Vec3; +using Vec3d = Vec3; +using Vec3i = Vec3; +using Vec3ui8 = Vec3; + +template +inline std::ostream& operator<<(std::ostream& os, const Vec3& vec) { os << vec.toString(); return os; } namespace std { - template<> - struct hash { - size_t operator()(const Vec3& v) const { - return hash()(v.x) ^ (hash()(v.y) << 1) ^ (hash()(v.z) << 2); + template + struct hash> { + size_t operator()(const Vec3& v) const { + return hash()(v.x) ^ (hash()(v.y) << 1) ^ (hash()(v.z) << 2); } }; } diff --git a/util/vectorlogic/vec4.hpp b/util/vectorlogic/vec4.hpp index 41b737e..7b96d9b 100644 --- a/util/vectorlogic/vec4.hpp +++ b/util/vectorlogic/vec4.hpp @@ -8,24 +8,25 @@ #include #include +template class Vec4 { public: union { - struct { float x, y, z, w; }; - struct { float r, g, b, a; }; - struct { float s, t, p, q; }; // For texture coordinates + struct { T x, y, z, w; }; + struct { T r, g, b, a; }; + struct { T s, t, p, q; }; // For texture coordinates }; // Constructors Vec4() : x(0), y(0), z(0), w(0) {} - Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} - Vec4(float scalar) : x(scalar), y(scalar), z(scalar), w(scalar) {} + Vec4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {} + Vec4(T scalar) : x(scalar), y(scalar), z(scalar), w(scalar) {} - Vec4(const Vec3& rgb, float w = 1.0f) : x(rgb.x), y(rgb.y), z(rgb.z), w(w) {} - static Vec4 RGB(float r, float g, float b, float a = 1.0f) { return Vec4(r, g, b, a); } - static Vec4 RGBA(float r, float g, float b, float a) { return Vec4(r, g, b, a); } + Vec4(const Vec3& rgb, T w = 1) : x(rgb.x), y(rgb.y), z(rgb.z), w(w) {} + static Vec4 RGB(T r, T g, T b, T a = 1) { return Vec4(r, g, b, a); } + static Vec4 RGBA(T r, T g, T b, T a) { return Vec4(r, g, b, a); } - Vec4& recolor(const Vec4 newColor) { + Vec4& recolor(const Vec4& newColor) { r = newColor.r; g = newColor.g; b = newColor.b; @@ -34,7 +35,7 @@ public: } Vec4 average(const Vec4& other) const { - return Vec4((x+other.x)/2,(y+other.y)/2,(z+other.z)/2,(w+other.w)/2); + return Vec4((x + other.x) / 2, (y + other.y) / 2, (z + other.z) / 2, (w + other.w) / 2); } Vec4 operator+(const Vec4& other) const { @@ -53,11 +54,11 @@ public: return Vec4(x / other.x, y / other.y, z / other.z, w / other.w); } - Vec4 operator+(float scalar) const { + Vec4 operator+(T scalar) const { return Vec4(x + scalar, y + scalar, z + scalar, w + scalar); } - Vec4 operator-(float scalar) const { + Vec4 operator-(T scalar) const { return Vec4(x - scalar, y - scalar, z - scalar, w - scalar); } @@ -65,15 +66,15 @@ public: return Vec4(-x, -y, -z, -w); } - Vec4 operator*(float scalar) const { + Vec4 operator*(T scalar) const { return Vec4(x * scalar, y * scalar, z * scalar, w * scalar); } - Vec4 operator/(float scalar) const { + Vec4 operator/(T scalar) const { return Vec4(x / scalar, y / scalar, z / scalar, w / scalar); } - Vec4& operator=(float scalar) { + Vec4& operator=(T scalar) { x = y = z = w = scalar; return *this; } @@ -110,7 +111,7 @@ public: return *this; } - Vec4& operator+=(float scalar) { + Vec4& operator+=(T scalar) { x += scalar; y += scalar; z += scalar; @@ -118,7 +119,7 @@ public: return *this; } - Vec4& operator-=(float scalar) { + Vec4& operator-=(T scalar) { x -= scalar; y -= scalar; z -= scalar; @@ -126,7 +127,7 @@ public: return *this; } - Vec4& operator*=(float scalar) { + Vec4& operator*=(T scalar) { x *= scalar; y *= scalar; z *= scalar; @@ -134,7 +135,7 @@ public: return *this; } - Vec4& operator/=(float scalar) { + Vec4& operator/=(T scalar) { x /= scalar; y /= scalar; z /= scalar; @@ -142,50 +143,50 @@ public: return *this; } - float dot(const Vec4& other) const { + T dot(const Vec4& other) const { return x * other.x + y * other.y + z * other.z + w * other.w; } // 4D cross product (returns vector perpendicular to 3 given vectors in 4D space) Vec4 cross(const Vec4& v1, const Vec4& v2, const Vec4& v3) const { - float a = v1.y * (v2.z * v3.w - v2.w * v3.z) - - v1.z * (v2.y * v3.w - v2.w * v3.y) + - v1.w * (v2.y * v3.z - v2.z * v3.y); + T a = v1.y * (v2.z * v3.w - v2.w * v3.z) - + v1.z * (v2.y * v3.w - v2.w * v3.y) + + v1.w * (v2.y * v3.z - v2.z * v3.y); - float b = -v1.x * (v2.z * v3.w - v2.w * v3.z) + - v1.z * (v2.x * v3.w - v2.w * v3.x) - - v1.w * (v2.x * v3.z - v2.z * v3.x); + T b = -v1.x * (v2.z * v3.w - v2.w * v3.z) + + v1.z * (v2.x * v3.w - v2.w * v3.x) - + v1.w * (v2.x * v3.z - v2.z * v3.x); - float c = v1.x * (v2.y * v3.w - v2.w * v3.y) - - v1.y * (v2.x * v3.w - v2.w * v3.x) + - v1.w * (v2.x * v3.y - v2.y * v3.x); + T c = v1.x * (v2.y * v3.w - v2.w * v3.y) - + v1.y * (v2.x * v3.w - v2.w * v3.x) + + v1.w * (v2.x * v3.y - v2.y * v3.x); - float d = -v1.x * (v2.y * v3.z - v2.z * v3.y) + - v1.y * (v2.x * v3.z - v2.z * v3.x) - - v1.z * (v2.x * v3.y - v2.y * v3.x); + T d = -v1.x * (v2.y * v3.z - v2.z * v3.y) + + v1.y * (v2.x * v3.z - v2.z * v3.x) - + v1.z * (v2.x * v3.y - v2.y * v3.x); return Vec4(a, b, c, d); } - float length() const { - return std::sqrt(x * x + y * y + z * z + w * w); + T length() const { + return static_cast(std::sqrt(static_cast(x * x + y * y + z * z + w * w))); } - float lengthSquared() const { + T lengthSquared() const { return x * x + y * y + z * z + w * w; } - float distance(const Vec4& other) const { + T distance(const Vec4& other) const { return (*this - other).length(); } - float distanceSquared(const Vec4& other) const { + T distanceSquared(const Vec4& other) const { Vec4 diff = *this - other; return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z + diff.w * diff.w; } Vec4 normalized() const { - float len = length(); + T len = length(); if (len > 0) { return *this / len; } @@ -194,8 +195,8 @@ public: // Homogeneous normalization (divide by w) Vec4 homogenized() const { - if (w != 0.0f) { - return Vec4(x / w, y / w, z / w, 1.0f); + if (w != 0) { + return Vec4(x / w, y / w, z / w, 1); } return *this; } @@ -203,36 +204,45 @@ public: // 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) + std::clamp(r, static_cast(0), static_cast(1)), + std::clamp(g, static_cast(0), static_cast(1)), + std::clamp(b, static_cast(0), static_cast(1)), + std::clamp(a, static_cast(0), static_cast(1)) ); } // Convert to Vec3 (ignoring alpha) - Vec3 toVec3() const { - return Vec3(r, g, b); + 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(std::clamp(r, 0.0f, 1.0f) * 255); - green = static_cast(std::clamp(g, 0.0f, 1.0f) * 255); - blue = static_cast(std::clamp(b, 0.0f, 1.0f) * 255); - alpha = static_cast(std::clamp(a, 0.0f, 1.0f) * 255); + template + typename std::enable_if::value>::type + toUint8(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) const { + red = static_cast(std::clamp(r, static_cast(0), static_cast(1)) * 255); + green = static_cast(std::clamp(g, static_cast(0), static_cast(1)) * 255); + blue = static_cast(std::clamp(b, static_cast(0), static_cast(1)) * 255); + alpha = static_cast(std::clamp(a, static_cast(0), static_cast(1)) * 255); } - void toUint8(uint8_t& red, uint8_t& green, uint8_t& blue) const { - red = static_cast(std::clamp(r, 0.0f, 1.0f) * 255); - green = static_cast(std::clamp(g, 0.0f, 1.0f) * 255); - blue = static_cast(std::clamp(b, 0.0f, 1.0f) * 255); + template + typename std::enable_if::value>::type + toUint8(uint8_t& red, uint8_t& green, uint8_t& blue) const { + red = static_cast(std::clamp(r, static_cast(0), static_cast(1)) * 255); + green = static_cast(std::clamp(g, static_cast(0), static_cast(1)) * 255); + blue = static_cast(std::clamp(b, static_cast(0), static_cast(1)) * 255); } + // Get XYZ components as Vec3 - class Vec3 xyz() const; + Vec3 xyz() const { + return Vec3(x, y, z); + } // Get RGB components as Vec3 - class Vec3 rgb() const; + Vec3 rgb() const { + return Vec3(r, g, b); + } bool operator==(const Vec4& other) const { return x == other.x && y == other.y && z == other.z && w == other.w; @@ -296,7 +306,7 @@ public: std::max(z, other.z), std::max(w, other.w)); } - Vec4 clamp(float minVal, float maxVal) const { + Vec4 clamp(T minVal, T maxVal) const { return Vec4( std::clamp(x, minVal, maxVal), std::clamp(y, minVal, maxVal), @@ -315,54 +325,58 @@ public: ); } - bool isZero(float epsilon = 1e-10f) const { + bool isZero(T epsilon = static_cast(1e-10)) const { return std::abs(x) < epsilon && std::abs(y) < epsilon && std::abs(z) < epsilon && std::abs(w) < epsilon; } - bool equals(const Vec4& other, float epsilon = 1e-10f) const { + bool equals(const Vec4& other, T epsilon = static_cast(1e-10)) const { return std::abs(x - other.x) < epsilon && std::abs(y - other.y) < epsilon && std::abs(z - other.z) < epsilon && std::abs(w - other.w) < epsilon; } - friend Vec4 operator+(float scalar, const Vec4& vec) { + friend Vec4 operator+(T scalar, const Vec4& vec) { return Vec4(scalar + vec.x, scalar + vec.y, scalar + vec.z, scalar + vec.w); } - friend Vec4 operator-(float scalar, const Vec4& vec) { + friend Vec4 operator-(T scalar, const Vec4& vec) { return Vec4(scalar - vec.x, scalar - vec.y, scalar - vec.z, scalar - vec.w); } - friend Vec4 operator*(float scalar, const Vec4& vec) { + friend Vec4 operator*(T scalar, const Vec4& vec) { return Vec4(scalar * vec.x, scalar * vec.y, scalar * vec.z, scalar * vec.w); } - friend Vec4 operator/(float scalar, const Vec4& vec) { + friend Vec4 operator/(T scalar, const Vec4& vec) { return Vec4(scalar / vec.x, scalar / vec.y, scalar / vec.z, scalar / vec.w); } - Vec4 lerp(const Vec4& other, float t) const { - t = std::clamp(t, 0.0f, 1.0f); + Vec4 lerp(const Vec4& other, T t) const { + t = std::clamp(t, static_cast(0), static_cast(1)); return *this + (other - *this) * t; } - // Convert to grayscale using standard RGB weights - float grayscale() const { - return r * 0.299f + g * 0.587f + b * 0.114f; + // Convert to grayscale using standard RGB weights (only valid for float/double) + template + typename std::enable_if::value, T>::type grayscale() const { + return r * static_cast(0.299) + g * static_cast(0.587) + b * static_cast(0.114); } - // Color inversion (1.0 - color) - Vec4 inverted() const { - return Vec4(1.0f - r, 1.0f - g, 1.0f - b, a); + // Color inversion (1.0 - color) (only valid for float/double) + template + typename std::enable_if::value, Vec4>::type + inverted() const { + return Vec4(static_cast(1) - r, static_cast(1) - g, + static_cast(1) - b, a); } - float& operator[](int index) { + T& operator[](int index) { return (&x)[index]; } - const float& operator[](int index) const { + const T& operator[](int index) const { return (&x)[index]; } @@ -371,29 +385,40 @@ public: std::to_string(z) + ", " + std::to_string(w) + ")"; } - std::string toColorString() const { + template + typename std::enable_if::value, std::string>::type + toColorString() const { return "RGBA(" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + ", " + std::to_string(a) + ")"; } struct Hash { std::size_t operator()(const Vec4& v) const { - return std::hash()(v.x) ^ (std::hash()(v.y) << 1) ^ (std::hash()(v.z) << 2) ^ (std::hash()(v.w) << 3); + return std::hash()(v.x) ^ (std::hash()(v.y) << 1) ^ + (std::hash()(v.z) << 2) ^ (std::hash()(v.w) << 3); } }; }; -inline std::ostream& operator<<(std::ostream& os, const Vec4& vec) { +// Type aliases for common use cases +using Vec4f = Vec4; +using Vec4d = Vec4; +using Vec4i = Vec4; +using Vec4u = Vec4; +using Vec4ui8 = Vec4; + +template +inline std::ostream& operator<<(std::ostream& os, const Vec4& vec) { os << vec.toString(); return os; } namespace std { - template<> - struct hash { - size_t operator()(const Vec4& v) const { - return hash()(v.x) ^ (hash()(v.y) << 1) ^ - (hash()(v.z) << 2) ^ (hash()(v.w) << 3); + template + struct hash> { + size_t operator()(const Vec4& v) const { + return hash()(v.x) ^ (hash()(v.y) << 1) ^ + (hash()(v.z) << 2) ^ (hash()(v.w) << 3); } }; }