mirror of
https://github.com/andrewchilicki/LumonMDR.git
synced 2025-07-01 18:17:27 -04:00
Init
This commit is contained in:
2
libs/CMakeLists.txt
Normal file
2
libs/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_subdirectory(Image)
|
||||
add_subdirectory(Numbers)
|
15
libs/Image/CMakeLists.txt
Normal file
15
libs/Image/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
add_library(Image
|
||||
Image.h
|
||||
ImageDisplay.cpp ImageDisplay.h
|
||||
)
|
||||
|
||||
target_include_directories(Image PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/external/stb
|
||||
)
|
||||
|
||||
target_link_libraries(Image PUBLIC
|
||||
ImGui
|
||||
OpenGL::GL
|
||||
glfw
|
||||
)
|
8
libs/Image/Image.h
Normal file
8
libs/Image/Image.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <GL/glew.h>
|
||||
|
||||
struct Image
|
||||
{
|
||||
GLuint texture;
|
||||
int width, height;
|
||||
};
|
127
libs/Image/ImageDisplay.cpp
Normal file
127
libs/Image/ImageDisplay.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "ImageDisplay.h"
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
class ImageDisplayImpl : public ImageDisplay {
|
||||
public:
|
||||
explicit ImageDisplayImpl(std::string assetDir) : assetDir(std::move(assetDir)) {}
|
||||
|
||||
~ImageDisplayImpl() override {
|
||||
// Clean up cached textures
|
||||
for (const auto& pair : imageCache) {
|
||||
glDeleteTextures(1, &pair.second.texture);
|
||||
}
|
||||
}
|
||||
|
||||
void drawImGuiImage(const std::string& imagePath, float scale, std::optional<ImVec4> tint) final
|
||||
{
|
||||
auto filePath = assetDir + imagePath;
|
||||
if (auto image = getImageForFile(filePath))
|
||||
{
|
||||
ImGui::Image((ImTextureID)(intptr_t)image->texture, ImVec2(image->width*scale, image->height*scale),
|
||||
ImVec2(0,0), ImVec2(1,1), tint.value_or(ImVec4(1,1,1,1)));
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int> getImageSize(const std::string &imagePath) final
|
||||
{
|
||||
auto filePath = assetDir + imagePath;
|
||||
if (auto image = getImageForFile(filePath))
|
||||
{
|
||||
return std::make_pair(image->width, image->height);
|
||||
}
|
||||
return std::make_pair(0, 0);
|
||||
}
|
||||
|
||||
std::optional<Image> getImageForFile(const std::string& filePath)
|
||||
{
|
||||
if (auto it = imageCache.find(filePath); it != imageCache.end())
|
||||
{
|
||||
// Return from cache
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Load as new into cache
|
||||
return loadImageFromFile(filePath);
|
||||
}
|
||||
|
||||
std::optional<Image> loadImageFromFile(const std::string &filePath)
|
||||
{
|
||||
// Load texture from file
|
||||
FILE* f = fopen(filePath.c_str(), "rb");
|
||||
if (f == NULL)
|
||||
{
|
||||
std::cerr << "Failed to open file " << filePath << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t file_size = (size_t)ftell(f);
|
||||
if (file_size == -1){
|
||||
std::cerr << "Failed to get file size of file " << filePath << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
fseek(f, 0, SEEK_SET);
|
||||
void* file_data = IM_ALLOC(file_size);
|
||||
fread(file_data, 1, file_size, f);
|
||||
|
||||
int out_width = 0;
|
||||
int out_height = 0;
|
||||
GLuint out_texture = 0;
|
||||
bool ret = loadTextureFromMemory(file_data, file_size, &out_texture, &out_width, &out_height);
|
||||
IM_FREE(file_data);
|
||||
|
||||
if (ret) {
|
||||
// Cache the loaded image
|
||||
auto newImage = Image{out_texture, out_width, out_height};
|
||||
imageCache.emplace(filePath, newImage);
|
||||
std::cout << "New image saved to cache: " << filePath << std::endl;
|
||||
return newImage;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// From https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
||||
bool loadTextureFromMemory(const void* data, size_t data_size, GLuint* out_texture, int* out_width, int* out_height)
|
||||
{
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL, 4);
|
||||
if (image_data == NULL)
|
||||
return false;
|
||||
|
||||
GLuint image_texture;
|
||||
glGenTextures(1, &image_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, image_texture);
|
||||
|
||||
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_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
*out_texture = image_texture;
|
||||
*out_width = image_width;
|
||||
*out_height = image_height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string assetDir;
|
||||
std::unordered_map<std::string, Image> imageCache;
|
||||
};
|
||||
|
||||
std::shared_ptr<ImageDisplay> createImageDisplay(const std::string& assetDir)
|
||||
{
|
||||
return std::make_shared<ImageDisplayImpl>(assetDir);
|
||||
}
|
18
libs/Image/ImageDisplay.h
Normal file
18
libs/Image/ImageDisplay.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ImageDisplay {
|
||||
public:
|
||||
virtual void drawImGuiImage(const std::string& imagePath, float scale, std::optional<ImVec4> tint) = 0;
|
||||
virtual std::pair<int, int> getImageSize(const std::string &imagePath) = 0;
|
||||
|
||||
virtual ~ImageDisplay() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<ImageDisplay> createImageDisplay(const std::string& assetDir);
|
9
libs/Numbers/CMakeLists.txt
Normal file
9
libs/Numbers/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
add_library(Numbers
|
||||
Number.h
|
||||
NumberGrid.cpp NumberGrid.h
|
||||
)
|
||||
|
||||
target_include_directories(Numbers PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/external/perlin-noise
|
||||
)
|
48
libs/Numbers/Number.h
Normal file
48
libs/Numbers/Number.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct BadGroup
|
||||
{
|
||||
BadGroup(int id, std::vector<int> numberIds, int binIdx) : id(id), numberIds(std::move(numberIds)), binIdx(binIdx) {}
|
||||
|
||||
int id;
|
||||
std::vector<int> numberIds;
|
||||
|
||||
bool isActive = false;
|
||||
bool superActive = false;
|
||||
double scale = 0;
|
||||
bool reachedMax = false;
|
||||
|
||||
int binIdx = 0;
|
||||
bool refined = false;
|
||||
};
|
||||
|
||||
using BadGroupPtr = std::shared_ptr<BadGroup>;
|
||||
|
||||
struct NumberDisplayInfos
|
||||
{
|
||||
explicit NumberDisplayInfos(bool horizontalOffset) : horizontalOffset(horizontalOffset) {}
|
||||
|
||||
bool horizontalOffset;
|
||||
float centerX = -1.f, centerY = -1.f;
|
||||
float refinedX = -1.f, refinedY = -1.f;
|
||||
bool isVisible = false;
|
||||
};
|
||||
|
||||
struct Number
|
||||
{
|
||||
Number(int id, int gridX, int gridY, int num, bool horizontalOffset) : id(id), gridX(gridX), gridY(gridY), num(num), displayInfos(NumberDisplayInfos(horizontalOffset)) {}
|
||||
|
||||
int id;
|
||||
int gridX, gridY;
|
||||
int num;
|
||||
NumberDisplayInfos displayInfos;
|
||||
|
||||
BadGroupPtr badGroup = nullptr;
|
||||
|
||||
float regenerateScale = 0.f;
|
||||
};
|
||||
|
||||
using NumberPtr = std::shared_ptr<Number>;
|
254
libs/Numbers/NumberGrid.cpp
Normal file
254
libs/Numbers/NumberGrid.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "NumberGrid.h"
|
||||
|
||||
#include "PerlinNoise.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
|
||||
class NumberGridImpl : public NumberGrid {
|
||||
public:
|
||||
explicit NumberGridImpl(int gridSize)
|
||||
{
|
||||
generateGrid(gridSize);
|
||||
}
|
||||
|
||||
std::map<int, std::map<int, NumberPtr>> getGrid() final
|
||||
{
|
||||
return grid;
|
||||
}
|
||||
|
||||
void update() final
|
||||
{
|
||||
visibleBadGroups.clear();
|
||||
bool activeGroupStillVisible = false;
|
||||
bool newActiveBadGroup = false;
|
||||
|
||||
for (auto it = badGroups.begin(); it != badGroups.end(); )
|
||||
{
|
||||
if (it->second->numberIds.empty())
|
||||
{
|
||||
it = badGroups.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Update visible groups and check if active group is still visible
|
||||
for (const auto &[groupId, badGroup] : badGroups)
|
||||
{
|
||||
for (const auto &numId : badGroup->numberIds)
|
||||
{
|
||||
auto num = numberIdMap.at(numId);
|
||||
if (num->displayInfos.isVisible)
|
||||
{
|
||||
visibleBadGroups.emplace(groupId);
|
||||
|
||||
if (badGroup->isActive && groupId == *activeBadGroup)
|
||||
{
|
||||
activeGroupStillVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activeBadGroup && !activeGroupStillVisible)
|
||||
{
|
||||
activeBadGroup.reset();
|
||||
newActiveBadGroup = true;
|
||||
newBadGroupCountdown = randomNumber(5, 15) * 100;
|
||||
}
|
||||
|
||||
// Select a new active group if necessary
|
||||
if (!activeBadGroup && !visibleBadGroups.empty() && newBadGroupCountdown == 0)
|
||||
{
|
||||
auto randomIndex = randomNumber(0, static_cast<int>(visibleBadGroups.size()) - 1);
|
||||
auto it = visibleBadGroups.begin();
|
||||
std::advance(it, randomIndex);
|
||||
activeBadGroup = *it;
|
||||
}
|
||||
|
||||
// Update active groups / their scale
|
||||
for (const auto &[groupId, badGroup] : badGroups)
|
||||
{
|
||||
badGroup->isActive = activeBadGroup && groupId == *activeBadGroup;
|
||||
if (badGroup->isActive)
|
||||
{
|
||||
if (newActiveBadGroup)
|
||||
{
|
||||
badGroup->scale = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!badGroup->reachedMax)
|
||||
{
|
||||
if (badGroup->scale < 0.23)
|
||||
{
|
||||
badGroup->scale += (0.0001 * randomNumber(1, 10));
|
||||
}
|
||||
} else
|
||||
{
|
||||
badGroup->scale -= (0.0001 * randomNumber(1, 10));
|
||||
}
|
||||
|
||||
if (badGroup->scale >= 0.23)
|
||||
{
|
||||
if (!badGroup->superActive || badGroup->scale >= 0.24)
|
||||
{
|
||||
badGroup->reachedMax = true;
|
||||
} else
|
||||
{
|
||||
badGroup->scale += 0.00001;
|
||||
}
|
||||
} else if (badGroup->scale <= 0.0)
|
||||
{
|
||||
badGroup->isActive = false;
|
||||
badGroup->superActive = false;
|
||||
badGroup->reachedMax = false;
|
||||
activeBadGroup.reset();
|
||||
newBadGroupCountdown = randomNumber(5, 15) * 100;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
badGroup->scale = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBadGroupCountdown > 0)
|
||||
{
|
||||
newBadGroupCountdown--;
|
||||
}
|
||||
}
|
||||
|
||||
NumberPtr getGridNumber(int x, int y) final
|
||||
{
|
||||
if (auto itr = grid.find(x); itr != grid.end())
|
||||
{
|
||||
if (auto itr2 = itr->second.find(y); itr2 != itr->second.end())
|
||||
{
|
||||
return itr2->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NumberPtr getGridNumber(int id) final
|
||||
{
|
||||
if (auto itr = numberIdMap.find(id); itr != numberIdMap.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::map<int, BadGroupPtr> getBadGroups() const final
|
||||
{
|
||||
return badGroups;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int, std::map<int, NumberPtr>> grid;
|
||||
std::map<int, NumberPtr> numberIdMap;
|
||||
|
||||
std::map<int, BadGroupPtr> badGroups;
|
||||
|
||||
std::set<int> visibleBadGroups;
|
||||
std::optional<int> activeBadGroup = std::nullopt;
|
||||
int newBadGroupCountdown = 500;
|
||||
|
||||
siv::PerlinNoise perlinBadNumbers{ 505 };
|
||||
float badScale = 0.4f;
|
||||
float badThresh = 0.7f;
|
||||
bool newBad = false;
|
||||
|
||||
void generateGrid(int size)
|
||||
{
|
||||
int numberId = 0;
|
||||
std::set<int> badNumbers;
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
grid[x][y] = std::make_shared<Number>(numberId, x, y, randomNumber(0,9), randomBool());
|
||||
numberIdMap[numberId] = grid[x][y];
|
||||
|
||||
// Determine if bad
|
||||
if (perlinBadNumbers.noise2D_01(x*badScale,y*badScale) > badThresh)
|
||||
{
|
||||
badNumbers.insert(numberId);
|
||||
}
|
||||
|
||||
numberId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign 'bad groups'
|
||||
auto checkAdjacent = [&](int x, int y) -> BadGroupPtr
|
||||
{
|
||||
if (auto gridNum = getGridNumber(x, y))
|
||||
{
|
||||
return gridNum->badGroup;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
int badGroup = 0;
|
||||
for (const auto &badNumId : badNumbers)
|
||||
{
|
||||
auto gridNumber = numberIdMap.at(badNumId);
|
||||
if (!gridNumber->badGroup)
|
||||
{
|
||||
for (int checkX = -1; checkX <= 1; checkX++)
|
||||
{
|
||||
for (int checkY = -1; checkY <= 1; checkY++)
|
||||
{
|
||||
if (checkX == 0 && checkY == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (auto badGroupPtr = checkAdjacent(gridNumber->gridX + checkX, gridNumber->gridY + checkY))
|
||||
{
|
||||
gridNumber->badGroup = badGroupPtr;
|
||||
gridNumber->badGroup->numberIds.emplace_back(gridNumber->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gridNumber->badGroup)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gridNumber->badGroup)
|
||||
{
|
||||
gridNumber->badGroup = std::make_shared<BadGroup>(badGroup++, std::vector{gridNumber->id}, randomNumber(0,4));
|
||||
badGroups.emplace(badGroup, gridNumber->badGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int randomNumber(int min, int max) final
|
||||
{
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dist(min, max);
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
static bool randomBool()
|
||||
{
|
||||
return rand() > (RAND_MAX / 2);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::shared_ptr<NumberGrid> createNumberGrid(int gridSize)
|
||||
{
|
||||
return std::make_shared<NumberGridImpl>(gridSize);
|
||||
}
|
24
libs/Numbers/NumberGrid.h
Normal file
24
libs/Numbers/NumberGrid.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Number.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class NumberGrid
|
||||
{
|
||||
public:
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual std::map<int, std::map<int, NumberPtr>> getGrid() = 0;
|
||||
virtual NumberPtr getGridNumber(int x, int y) = 0;
|
||||
virtual NumberPtr getGridNumber(int id) = 0;
|
||||
|
||||
virtual std::map<int, BadGroupPtr> getBadGroups() const = 0;
|
||||
|
||||
virtual int randomNumber(int min, int max) = 0;
|
||||
|
||||
virtual ~NumberGrid() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<NumberGrid> createNumberGrid(int gridSize);
|
Reference in New Issue
Block a user