diff --git a/tests/g2chromatic2.cpp b/tests/g2chromatic2.cpp index 4f5564f..34a2898 100644 --- a/tests/g2chromatic2.cpp +++ b/tests/g2chromatic2.cpp @@ -22,6 +22,10 @@ #include #include +#ifndef M_PI +#define M_PI = 3.1415 +#endif + std::mutex m; std::atomic isGenerating{false}; std::future generationFuture; @@ -109,7 +113,7 @@ std::vector> pickSeeds(Grid2 grid, AnimationConfi 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); + size_t id = grid.getOrCreatePositionVec(point, 0.0, true); grid.setColor(id, color); seeds.push_back(std::make_tuple(id,point, color)); } @@ -220,10 +224,16 @@ bool exportavi(std::vector frames, AnimationConfig config) { return success; } -void mainLogic(const AnimationConfig& config, Shared& state) { +void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) { isGenerating = true; try { - Grid2 grid = setup(config); + Grid2 grid; + if (gradnoise == 0) { + grid = setup(config); + } else if (gradnoise == 1) { + grid = grid.noiseGenGrid(0,0,config.height, config.width); + } + grid.setDefault(Vec4(0,0,0,0)); { std:: lock_guard lock(state.mutex); state.grid = grid; @@ -364,12 +374,14 @@ int main() { static int i2 = 1024; static int i3 = 480; static int i4 = 8; + static float fs = 1.0; std::future mainlogicthread; Shared state; Grid2 grid; AnimationConfig config; previewText = "Please generate"; + int gradnoise = true; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); @@ -379,13 +391,16 @@ int main() { ImGui::NewFrame(); { - ImGui::Begin("Gradient settings"); + 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("framecount", &i3, 10, 5000); ImGui::SliderInt("numSeeds", &i4, 0, 10); + ImGui::SliderFloat("ScalePreview", &fs, 0.0, 2.0); + ImGui::RadioButton("Gradient", &gradnoise, 0); + ImGui::RadioButton("Perlin Noise", &gradnoise, 1); if (isGenerating) { ImGui::BeginDisabled(); @@ -393,17 +408,16 @@ int main() { if (ImGui::Button("Generate Animation")) { config = AnimationConfig(i1, i2, i3, f, i4); - mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state)); + mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise); } - - if (isGenerating) { + + if (isGenerating && textu != 0) { ImGui::EndDisabled(); ImGui::SameLine(); if (ImGui::Button("Cancel")) { cancelGeneration(); } - // Check for new frames from the generation thread bool hasNewFrame = false; { @@ -418,7 +432,41 @@ int main() { ImGui::Text(previewText.c_str()); if (textu != 0) { - ImVec2 imageSize = ImVec2(config.width * 0.3f, config.height * 0.3f); // Scale down for preview + 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 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); @@ -474,5 +522,4 @@ int main() { return 0; } //I need this: https://raais.github.io/ImStudio/ -// or this: https://github.com/tpecholt/imrad/ // 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 \ No newline at end of file diff --git a/util/grid/grid2.hpp b/util/grid/grid2.hpp index de8750b..b3bf6c5 100644 --- a/util/grid/grid2.hpp +++ b/util/grid/grid2.hpp @@ -6,6 +6,7 @@ #include "../vectorlogic/vec4.hpp" #include "../timing_decorator.hpp" #include "../output/frame.hpp" +#include "../noise/pnoise2.hpp" #include #include @@ -195,16 +196,90 @@ protected: SpatialGrid spatialGrid; float spatialCellSize = 2.0f; + // Default background color for empty spaces + Vec4 defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); + + PNoise2 noisegen; public: bool usable = false; + + // Set default background color for empty spaces + void setDefault(const Vec4& color) { + defaultBackgroundColor = color; + } + + void setDefault(float r, float g, float b, float a = 0.0f) { + defaultBackgroundColor = Vec4(r, g, b, a); + } + + // Get current default background color + Vec4 getDefaultBackgroundColor() const { + return defaultBackgroundColor; + } + //get position from id Vec2 getPositionID(size_t id) const { Vec2 it = Positions.at(id); return it; } + Grid2 noiseGenGrid(size_t minx,size_t miny, size_t maxx, size_t maxy + , float minChance = 0.1f, float maxChance = 1.0f, bool color = true) { + std::vector poses; + std::vector colors; + std::vector sizes; + for (int x = minx; x < maxx; x++) { + for (int y = miny; y < maxy; y++) { + Vec2 pos = Vec2(x,y); + float alpha = noisegen.permute(Vec2(x,y)); + if (alpha > minChance && alpha < maxChance) { + if (color) { + float red = noisegen.permute(pos); + float green = noisegen.permute(pos); + float blue = noisegen.permute(pos); + Vec4 newc = Vec4(red,green,blue,alpha); + colors.push_back(newc); + poses.push_back(pos); + sizes.push_back(1.0f); + } + else { + Vec4 newc = Vec4(alpha,alpha,alpha,alpha); + colors.push_back(newc); + poses.push_back(pos); + sizes.push_back(1.0f); + } + } + } + } + bulkAddObjects(poses,colors,sizes); + return *this; + } + + size_t NoiseGenPointB(const Vec2& pos) { + float grayc = noisegen.permute(pos); + Vec4 newc = Vec4(grayc,grayc,grayc,grayc); + return addObject(pos,newc,1.0); + } + + size_t NoiseGenPointRGB(const Vec2& pos) { + float red = noisegen.permute(pos); + float green = noisegen.permute(pos); + float blue = noisegen.permute(pos); + Vec4 newc = Vec4(red,green,blue,1); + return addObject(pos,newc,1.0); + } + + size_t NoiseGenPointRGBA(const Vec2& pos) { + float red = noisegen.permute(pos); + float green = noisegen.permute(pos); + float blue = noisegen.permute(pos); + float alpha = noisegen.permute(pos); + Vec4 newc = Vec4(red,green,blue,alpha); + return addObject(pos,newc,1.0); + } + //get id from position (optional radius, picks first found. radius of 0 becomes epsilon if none are found) - size_t getPositionVec(const Vec2& pos, float radius = 0.0f) { + size_t getPositionVec(const Vec2& pos, float radius = 0.0f) const { TIME_FUNCTION; if (radius == 0.0f) { // Exact match - use spatial grid to find the cell @@ -227,12 +302,41 @@ public: } } - size_t getPositionVec(float x, float y, float radius = 0.0f) { + size_t getOrCreatePositionVec(const Vec2& pos, float radius = 0.0f, bool create = false) { + TIME_FUNCTION; + if (radius == 0.0f) { + // Exact match - use spatial grid to find the cell + Vec2 gridPos = spatialGrid.worldToGrid(pos); + auto cellIt = spatialGrid.grid.find(gridPos); + if (cellIt != spatialGrid.grid.end()) { + for (size_t id : cellIt->second) { + if (Positions.at(id) == pos) { + return id; + } + } + } + if (create) { + return addObject(pos, defaultBackgroundColor, 1.0f); + } + throw std::out_of_range("Position not found"); + } else { + auto results = getPositionVecRegion(pos, radius); + if (!results.empty()) { + return results[0]; // Return first found + } + if (create) { + return addObject(pos, defaultBackgroundColor, 1.0f); + } + throw std::out_of_range("No positions found in radius"); + } + } + + size_t getPositionVec(float x, float y, float radius = 0.0f) const { return getPositionVec(Vec2(x,y), radius); } //get all id in region - std::vector getPositionVecRegion(const Vec2& pos, float radius = 1.0f) { + std::vector getPositionVecRegion(const Vec2& pos, float radius = 1.0f) const { TIME_FUNCTION; float searchRadius = (radius == 0.0f) ? std::numeric_limits::epsilon() : radius; @@ -483,7 +587,6 @@ public: void getGridRegionAsRGB(const Vec2& minCorner, const Vec2& maxCorner, int& width, int& height, std::vector& rgbData) const { TIME_FUNCTION; - // std::cout << "excessdebug g2.483" << std::endl; // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); @@ -494,14 +597,19 @@ public: rgbData.shrink_to_fit(); return; } - // std::cout << "excessdebug g2.494" << std::endl; - // Initialize RGB data (3 bytes per pixel: R, G, B) - std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); + // Initialize RGB data with default background color + std::vector rgbaBuffer(width * height, Vec4(0,0,0,0)); + // for (int x = minCorner.x; x < maxCorner.x; x++) { + // for (int y = minCorner.y; x < maxCorner.y; y++){ + // Vec2 pos = Vec2(x,y); + // size_t posID = getPositionVec(pos, 1.0f, false); + + // } + // } // For each position in the grid, find the corresponding pixel for (const auto& [id, pos] : Positions) { - // std::cout << "excessdebug g2.501." << id << std::endl; size_t size = Sizes.at(id); // Calculate pixel coordinates @@ -514,65 +622,69 @@ public: pixelXM = std::min(width - 1, pixelXM); pixelYm = std::max(0, pixelYm); pixelYM = std::min(height - 1, pixelYM); - // std::cout << "excessdebug g2.514." << id << std::endl; // Ensure within bounds if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; const Vec4& color = Colors.at(id); float srcAlpha = color.a; float invSrcAlpha = 1.0f - srcAlpha; for (int py = pixelYm; py <= pixelYM; ++py){ for (int px = pixelXm; px <= pixelXM; ++px){ - // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; int index = (py * width + px); Vec4 dest = rgbaBuffer[index]; - 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; + // Alpha blending: new_color = src * src_alpha + dest * (1 - src_alpha) + 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; rgbaBuffer[index] = dest; } } } } + + // Convert to RGB bytes rgbData.resize(rgbaBuffer.size() * 3); for (int i = 0; i < rgbaBuffer.size(); ++i) { - const Vec4& color = rgbaBuffer[i]; - int bgrIndex = i * 3; + Vec4& color = rgbaBuffer[i]; + int rgbIndex = i * 3; + float alpha = color.a; + + if (alpha < 1.0) { + float invalpha = 1.0 - alpha; + color.r = defaultBackgroundColor.r * alpha + color.r * invalpha; + color.g = defaultBackgroundColor.g * alpha + color.g * invalpha; + color.b = defaultBackgroundColor.b * alpha + color.b * invalpha; + } // Convert from [0,1] to [0,255] and store as RGB - rgbData[bgrIndex + 0] = static_cast(color.r * 255); - rgbData[bgrIndex + 1] = static_cast(color.g * 255); - rgbData[bgrIndex + 2] = static_cast(color.b * 255); - + rgbData[rgbIndex + 0] = static_cast(color.r * 255); + rgbData[rgbIndex + 1] = static_cast(color.g * 255); + rgbData[rgbIndex + 2] = static_cast(color.b * 255); } } // Get region as BGR void getGridRegionAsBGR(const Vec2& minCorner, const Vec2& maxCorner, - int& width, int& height, std::vector& rgbData) const { + int& width, int& height, std::vector& bgrData) const { TIME_FUNCTION; - // std::cout << "excessdebug g2.483" << std::endl; // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); if (width <= 0 || height <= 0) { width = height = 0; - rgbData.clear(); - rgbData.shrink_to_fit(); + bgrData.clear(); + bgrData.shrink_to_fit(); return; } - // std::cout << "excessdebug g2.494" << std::endl; - // Initialize RGB data (3 bytes per pixel: R, G, B) - std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); + // Initialize RGB data with default background color + std::vector rgbaBuffer(width * height, defaultBackgroundColor); // For each position in the grid, find the corresponding pixel for (const auto& [id, pos] : Positions) { - // std::cout << "excessdebug g2.501." << id << std::endl; size_t size = Sizes.at(id); // Calculate pixel coordinates @@ -585,68 +697,61 @@ public: pixelXM = std::min(width - 1, pixelXM); pixelYm = std::max(0, pixelYm); pixelYM = std::min(height - 1, pixelYM); - // std::cout << "excessdebug g2.514." << id << std::endl; // Ensure within bounds if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; const Vec4& color = Colors.at(id); float srcAlpha = color.a; float invSrcAlpha = 1.0f - srcAlpha; for (int py = pixelYm; py <= pixelYM; ++py){ for (int px = pixelXm; px <= pixelXM; ++px){ - // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; int index = (py * width + px); Vec4 dest = rgbaBuffer[index]; - 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; + // Alpha blending: new_color = src * src_alpha + dest * (1 - src_alpha) + 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; rgbaBuffer[index] = dest; } } } } - rgbData.resize(rgbaBuffer.size() * 3); + + // Convert to BGR bytes + bgrData.resize(rgbaBuffer.size() * 3); for (int i = 0; i < rgbaBuffer.size(); ++i) { const Vec4& color = rgbaBuffer[i]; int bgrIndex = i * 3; - // Convert from [0,1] to [0,255] and store as RGB - // rgbData.push_back(color.r); - // rgbData.push_back(color.g); - // rgbData.push_back(color.b); - rgbData[bgrIndex + 2] = static_cast(color.r * 255); - rgbData[bgrIndex + 1] = static_cast(color.g * 255); - rgbData[bgrIndex + 0] = static_cast(color.b * 255); - + // Convert from [0,1] to [0,255] and store as BGR + bgrData[bgrIndex + 2] = static_cast(color.r * 255); + bgrData[bgrIndex + 1] = static_cast(color.g * 255); + bgrData[bgrIndex + 0] = static_cast(color.b * 255); } } void getGridRegionAsRGBA(const Vec2& minCorner, const Vec2& maxCorner, - int& width, int& height, std::vector& rgbData) const { + int& width, int& height, std::vector& rgbaData) const { TIME_FUNCTION; - // std::cout << "excessdebug g2.483" << std::endl; // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); if (width <= 0 || height <= 0) { width = height = 0; - rgbData.clear(); - rgbData.shrink_to_fit(); + rgbaData.clear(); + rgbaData.shrink_to_fit(); return; } - // std::cout << "excessdebug g2.494" << std::endl; - // Initialize RGB data (3 bytes per pixel: R, G, B) - std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); + // Initialize RGBA data with default background color + std::vector rgbaBuffer(width * height, defaultBackgroundColor); // For each position in the grid, find the corresponding pixel for (const auto& [id, pos] : Positions) { - // std::cout << "excessdebug g2.501." << id << std::endl; size_t size = Sizes.at(id); // Calculate pixel coordinates @@ -659,40 +764,39 @@ public: pixelXM = std::min(width - 1, pixelXM); pixelYm = std::max(0, pixelYm); pixelYM = std::min(height - 1, pixelYM); - // std::cout << "excessdebug g2.514." << id << std::endl; // Ensure within bounds if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; const Vec4& color = Colors.at(id); float srcAlpha = color.a; float invSrcAlpha = 1.0f - srcAlpha; for (int py = pixelYm; py <= pixelYM; ++py){ for (int px = pixelXm; px <= pixelXM; ++px){ - // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; int index = (py * width + px); Vec4 dest = rgbaBuffer[index]; - 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; + // Alpha blending: new_color = src * src_alpha + dest * (1 - src_alpha) + 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; rgbaBuffer[index] = dest; } } } } - rgbData.resize(rgbaBuffer.size() * 4); + + // Convert to RGBA bytes + rgbaData.resize(rgbaBuffer.size() * 4); for (int i = 0; i < rgbaBuffer.size(); ++i) { const Vec4& color = rgbaBuffer[i]; - int bgrIndex = i * 4; + int rgbaIndex = i * 4; - // Convert from [0,1] to [0,255] and store as RGB - rgbData[bgrIndex + 0] = static_cast(color.r * 255); - rgbData[bgrIndex + 1] = static_cast(color.g * 255); - rgbData[bgrIndex + 2] = static_cast(color.b * 255); - rgbData[bgrIndex + 2] = static_cast(color.a * 255); - + // Convert from [0,1] to [0,255] and store as RGBA + rgbaData[rgbaIndex + 0] = static_cast(color.r * 255); + rgbaData[rgbaIndex + 1] = static_cast(color.g * 255); + rgbaData[rgbaIndex + 2] = static_cast(color.b * 255); + rgbaData[rgbaIndex + 3] = static_cast(color.a * 255); } } @@ -724,11 +828,11 @@ public: frame getGridRegionAsFrameRGBA(const Vec2& minCorner, const Vec2& maxCorner) const { TIME_FUNCTION; int width, height; - std::vector rgbData; - getGridRegionAsRGBA(minCorner, maxCorner, width, height, rgbData); + std::vector rgbaData; + getGridRegionAsRGBA(minCorner, maxCorner, width, height, rgbaData); - frame resultFrame(width, height, frame::colormap::RGB); - resultFrame.setData(rgbData); + frame resultFrame(width, height, frame::colormap::RGBA); + resultFrame.setData(rgbaData); return resultFrame; } @@ -837,6 +941,8 @@ public: Colors.rehash(0); Sizes.rehash(0); neighborMap.rehash(0); + // Reset to default background color + defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); } // neighbor map diff --git a/util/noise/pnoise2.hpp b/util/noise/pnoise2.hpp index 53e5a71..5c2db9b 100644 --- a/util/noise/pnoise2.hpp +++ b/util/noise/pnoise2.hpp @@ -20,17 +20,58 @@ public: permutation[i] = i; } std::ranges::shuffle(permutation, rng); - TR = permutation[permutation[1]+1]; - TR = permutation[permutation[0]+1]; - TR = permutation[permutation[1]+0]; - TR = permutation[permutation[0]+0]; + permutation.insert(permutation.end(),permutation.begin(),permutation.end()); } - Vec2 GetConstantVector(Vec2 v) { - Vec2 h = v & 3; + float permute(Vec2 point) { + float x = point.x; + float y = point.y; + int X = (int)floor(x); + int xmod = X & 255; + int Y = (int)floor(point.y); + int ymod = Y & 255; + float xf = point.x - X; + float yf = point.y - Y; + + Vec2 TR = Vec2(xf-1, yf-1); + Vec2 TL = Vec2(xf-0, yf-1); + Vec2 BR = Vec2(xf-1, yf-0); + Vec2 BL = Vec2(xf-0, yf-0); + + int vTR = permutation[permutation[xmod+1]+ymod+1]; + int vTL = permutation[permutation[xmod+0]+ymod+1]; + int vBR = permutation[permutation[xmod+1]+ymod+0]; + int vBL = permutation[permutation[xmod+0]+ymod+0]; + + float dTR = TR.dot(GetConstantVector(vTR)); + float dTL = TL.dot(GetConstantVector(vTL)); + float dBR = BR.dot(GetConstantVector(vBR)); + float dBL = BL.dot(GetConstantVector(vBL)); + + float u = Fade(xf); + float v = Fade(yf); + + return lerp(u,lerp(v,dBL,dTL),lerp(v,dBR,dTR)); + + } + + float lerp(float t, float a1, float a2) { + return a1 + t * (a2 - a1); + } + + float Fade(float t) { + return (((6 * t - 15)* t + 10) * t * t * t); + } + + Vec2 GetConstantVector(float v) { + int h = (int)v & 3; + if (h == 0) return Vec2(1,1); + else if (h == 1) return Vec2(-1,1); + else if (h == 2) return Vec2(-1,-1); + else return Vec2(1,-1); } }; #endif -//https://rtouti.github.io/graphics/perlin-noise-algorithm \ No newline at end of file +//https://rtouti.github.io/graphics/perlin-noise-algorithm diff --git a/util/vectorlogic/vec4.hpp b/util/vectorlogic/vec4.hpp index f664361..dd767b2 100644 --- a/util/vectorlogic/vec4.hpp +++ b/util/vectorlogic/vec4.hpp @@ -77,7 +77,7 @@ public: x = y = z = w = scalar; return *this; } - + Vec4& operator+=(const Vec4& other) { x += other.x; y += other.y;