This commit is contained in:
2024-10-03 20:09:10 -05:00
parent 2ff1a159bc
commit ad317da97e
303 changed files with 1613 additions and 21403 deletions

View File

@ -1,23 +1,19 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Build Project
add_executable(${DAWN_TARGET_NAME})
# Add in base library
add_subdirectory(dawn)
# Compile entry targets
if(DAWN_TARGET STREQUAL "linux-x64-glfw")
add_subdirectory(dawnlinux)
add_subdirectory(dawnglfw)
add_subdirectory(dawnopengl)
add_subdirectory(dawnrpg)
else()
message(FATAL_ERROR "You need to define an entry target")
endif()
# Compress the game assets.
add_dependencies(${DAWN_TARGET_NAME} dawnassets)
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Build Project
add_executable(${DAWN_TARGET_NAME})
# Add base
add_subdirectory(dawn)
add_subdirectory(dawnterm)
# Compile entries
if(DAWN_TARGET STREQUAL "linux-x64-glfw")
add_subdirectory(dawnlinux)
add_subdirectory(dawntermlinux)
else()
message(FATAL_ERROR "Unknown target: ${DAWN_TARGET}")
endif()

View File

@ -1,40 +1,27 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libraries
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
glm::glm
archive_static
freetype
nlohmann_json::nlohmann_json
)
# Includes
target_include_directories(${DAWN_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(audio)
add_subdirectory(component)
add_subdirectory(display)
add_subdirectory(environment)
add_subdirectory(game)
# add_subdirectory(games)
# add_subdirectory(input)
add_subdirectory(locale)
add_subdirectory(prefab)
# add_subdirectory(physics)
add_subdirectory(poker)
add_subdirectory(save)
add_subdirectory(scene)
# add_subdirectory(state)
add_subdirectory(time)
add_subdirectory(util)
add_subdirectory(ui)
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libraries
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
)
# Includes
target_include_directories(${DAWN_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(rpg)
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
input.c
time.c
game.c
)

View File

@ -1,9 +1,12 @@
# Copyright (c) 2022 Dominic Masters
# Copyright (c) 2024 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
assert.cpp
assert.c
)

View File

@ -5,7 +5,8 @@
* https://opensource.org/licenses/MIT
*/
#include "assert.hpp"
#include "assert.h"
#include <stdarg.h>
void assertTrueImplement(
const char *file,
@ -26,10 +27,11 @@ void assertTrueImplement(
func
);
va_list argptr;
va_start(argptr, message);
vfprintf(stderr, message, argptr);
va_end(argptr);
// Print message.
va_list args;
va_start(args, message);
vfprintf(stderr, message, args);
va_end(args);
fprintf(stderr, "\n");
throw std::runtime_error("Assert failed.");
abort();
}

View File

@ -6,7 +6,7 @@
*/
#pragma once
#include "dawnlibs.hpp"
#include "dawn.h"
/**
* Asserts that a given statement must evaluate to true or the assertion fails
@ -63,7 +63,7 @@ void assertTrueImplement(
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertNotNull(x, ...) assertTrue(x != nullptr, __VA_ARGS__)
#define assertNotNull(x, ...) assertTrue(x != NULL, __VA_ARGS__)
/**
* Asserts that a given pointer is null.
@ -71,29 +71,7 @@ void assertTrueImplement(
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertNull(x, ...) assertTrue(x == nullptr, __VA_ARGS__)
/**
* Asserts that a given map has a specific key.
* @param map Map to check.
* @param key Key to check for.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertMapHasKey(map, key, ...) assertTrue( \
map.find(key) != map.end(), __VA_ARGS__ \
)
/**
* Asserts that a given map does not have a specific key.
* @param map Map to check.
* @param key Key to check for.
* @param message Message (sprintf format) to send to the logger.
* @param args Optional TParam args for the sprintf message to accept.
*/
#define assertMapNotHasKey(map, key, ...) assertTrue( \
map.find(key) == map.end(), __VA_ARGS__ \
)
#define assertNull(x, ...) assertTrue(x == NULL, __VA_ARGS__)
/**
* Asserts that a given value has a specific flag turned off.

View File

@ -1,200 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "AssetDataLoader.hpp"
#include "util/Math.hpp"
using namespace Dawn;
ssize_t assetDataLoaderArchiveRead(
struct archive *archive,
void *d,
const void **buffer
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(d, "Data is NULL!");
assertNotNull(buffer, "Buffer is NULL!");
AssetDataLoader *loader = (AssetDataLoader*)d;
*buffer = loader->buffer;
size_t read = fread(
loader->buffer, 1, ASSET_LOADER_BUFFER_SIZE, loader->assetArchiveFile
);
if(ferror(loader->assetArchiveFile)) return ARCHIVE_FATAL;
return read;
}
int64_t assetDataLoaderArchiveSeek(
struct archive *archive,
void *d,
int64_t offset,
int32_t whence
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(d, "Data is NULL!");
assertTrue(offset > 0, "Offset must be greater than 0!");
AssetDataLoader *loader = (AssetDataLoader*)d;
int32_t ret = fseek(loader->assetArchiveFile, offset, whence);
assertTrue(ret == 0, "Failed to seek!");
return ftell(loader->assetArchiveFile);
}
int32_t assetDataLoaderArchiveOpen(struct archive *a, void *d) {
assertNotNull(a, "Archive is NULL!");
assertNotNull(d, "Data is NULL!");
AssetDataLoader *loader = (AssetDataLoader*)d;
int32_t ret = fseek(loader->assetArchiveFile, 0, SEEK_SET);
assertTrue(ret == 0, "Failed to seek to start of file!");
return ARCHIVE_OK;
}
int32_t assetDataLoaderArchiveClose(struct archive *a, void *d) {
assertNotNull(a, "Archive is NULL!");
assertNotNull(d, "Data is NULL!");
return assetDataLoaderArchiveOpen(a, d);
}
// // // // // // // // // // // // // // // // // // // // // // // // // // //
AssetDataLoader::AssetDataLoader(std::string fileName) : fileName(fileName) {
assertTrue(
fileName.size() > 0,
"IAssetDataLoader::IAssetDataLoader: fileName must be greater than 0"
);
}
void AssetDataLoader::open() {
assertNull(this->assetArchiveFile, "AssetDataLoader::open: File is already open");
assertNull(this->assetArchive, "AssetDataLoader::open: Archive is already open");
assertNull(this->assetArchiveEntry, "AssetDataLoader::open: Entry is already open");
this->assetArchiveFile = this->openAssetArchiveFile();
assertNotNull(this->assetArchiveFile, "AssetDataLoader::open: Failed to open archive file!");
// Open archive reader
assetArchive = archive_read_new();
assertNotNull(assetArchive, "AssetDataLoader::open: Failed to create archive reader");
// Set up the reader
archive_read_support_format_tar(assetArchive);
// Open reader
archive_read_set_open_callback(assetArchive, &assetDataLoaderArchiveOpen);
archive_read_set_read_callback(assetArchive, &assetDataLoaderArchiveRead);
archive_read_set_seek_callback(assetArchive, &assetDataLoaderArchiveSeek);
archive_read_set_close_callback(assetArchive, &assetDataLoaderArchiveClose);
archive_read_set_callback_data(assetArchive, this);
int32_t ret = archive_read_open1(assetArchive);
assertTrue(ret == ARCHIVE_OK, "AssetDataLoader::open: Failed to open archive!");
position = 0;
// Iterate over each file to find the one for this asset loader.
while(archive_read_next_header(assetArchive, &assetArchiveEntry) == ARCHIVE_OK) {
const char_t *headerFile = (char_t*)archive_entry_pathname(assetArchiveEntry);
if(std::string(headerFile) == this->fileName) return;
int32_t ret = archive_read_data_skip(assetArchive);
assertTrue(ret == ARCHIVE_OK, "AssetDataLoader::open: Failed to skip data!");
}
assertUnreachable("AssetDataLoader::open: Failed to find file!");
}
int32_t AssetDataLoader::close() {
assertNotNull(this->assetArchiveFile, "AssetDataLoader::close: File is NULL");
assertNotNull(this->assetArchive, "AssetDataLoader::close: Archive is NULL!");
assertNotNull(this->assetArchiveEntry, "AssetDataLoader::close: Entry is NULL!");
// Close the archive
int32_t ret = archive_read_free(this->assetArchive);
assertTrue(ret == ARCHIVE_OK, "AssetDataLoader::close: Failed to close archive!");
this->assetArchive = nullptr;
this->assetArchiveEntry = nullptr;
// Close the file
int32_t res = fclose(this->assetArchiveFile);
this->assetArchiveFile = nullptr;
return res;
}
size_t AssetDataLoader::read(uint8_t *buffer, size_t size) {
assertNotNull(buffer, "Buffer is NULL!");
assertTrue(size > 0, "Size must be greater than 0!");
assertNotNull(this->assetArchive, "assetRead: Archive is NULL!");
assertNotNull(this->assetArchiveEntry, "assetRead: Entry is NULL!");
ssize_t read = archive_read_data(this->assetArchive, buffer, size);
this->position += read;
if(read == ARCHIVE_FATAL) {
assertUnreachable(archive_error_string(this->assetArchive));
}
assertTrue(read != ARCHIVE_RETRY, "assetRead: Failed to read data (RETRY)!");
assertTrue(read != ARCHIVE_WARN, "assetRead: Failed to read data (WARN)!");
return read;
}
size_t AssetDataLoader::readUntil(
uint8_t *buffer,
const size_t maxSize,
const char_t delimiter
) {
size_t totalRead = this->read(buffer, maxSize);
size_t i = 0;
while(i < totalRead) {
if(buffer[i] == delimiter) break;
i++;
}
buffer[i++] = '\0';
return i;
}
size_t AssetDataLoader::getSize() {
assertTrue(this->assetArchiveEntry != nullptr, "AssetDataLoader::getSize: Entry is NULL!");
assertTrue(archive_entry_size_is_set(assetArchiveEntry), "assetGetSize: Entry size is not set!");
return archive_entry_size(assetArchiveEntry);
}
size_t AssetDataLoader::skip(size_t n) {
assertTrue(n >= 0, "AssetDataLoader::skip: Byte count must be greater than 0.");
uint8_t dumpBuffer[ASSET_LOADER_BUFFER_SIZE];
size_t skipped = 0;
size_t n2, n3;
while(n != 0) {
n2 = Math::min<size_t>(n, ASSET_LOADER_BUFFER_SIZE);
n3 = this->read(dumpBuffer, n2);
assertTrue(n3 == n2, "AssetDataLoader::skip: Failed to skip bytes!");
n -= n3;
}
return skipped;
}
size_t AssetDataLoader::setPosition(const size_t position) {
assertTrue(position >= 0, "Position must be greater than or equal to 0");
this->rewind();
return this->skip(position);
}
void AssetDataLoader::rewind() {
// TODO: See if I can optimize this
this->close();
this->open();
}
size_t AssetDataLoader::getPosition() {
assertNotNull(this->assetArchiveFile, "AssetDataLoader::getPosition: File is not open!");
return this->position;
}
AssetDataLoader::~AssetDataLoader() {
if(this->assetArchiveFile != nullptr) this->close();
}

View File

@ -1,166 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
extern "C" {
#include <archive.h>
#include <archive_entry.h>
}
#define ASSET_LOADER_BUFFER_SIZE 32768
/**
* Method invoked by the libarchive internals to read bytes from the archive
* file pointer.
*
* @param archive Archive requesting the read.
* @param data Data pointer passed to the archive.
* @param buffer Pointer to where the buffer pointer should be stored.
* @return Count of bytes read.
*/
ssize_t assetDataLoaderArchiveRead(
struct archive *archive,
void *data,
const void **buffer
);
/**
* Method invoked by the libarchive internals to seek the archive file pointer.
*
* @param archive Archive requesting the seek.
* @param data Data pointer passed to the archive.
* @param offset Offset to seek to.
* @param whence Whence to seek from.
* @return The new offset.
*/
int64_t assetDataLoaderArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
);
/**
* Method invoked by the libarchive internals to open the archive file pointer.
*
* @param archive Archive requesting the open.
* @param data Data pointer passed to the archive.
* @return 0 if success, otherwise for failure.
*/
int32_t assetDataLoaderArchiveOpen(struct archive *a, void *data);
/**
* Method invoked by the libarchive internals to close the archive file pointer.
*
* @param archive Archive requesting the close.
* @param data Data pointer passed to the archive.
* @return 0 if success, otherwise for failure.
*/
int32_t assetDataLoaderArchiveClose(struct archive *a, void *data);
namespace Dawn {
class AssetDataLoader {
protected:
struct archive *assetArchive = nullptr;
struct archive_entry *assetArchiveEntry = nullptr;
size_t position;
std::string fileName;
public:
uint8_t buffer[ASSET_LOADER_BUFFER_SIZE];
FILE *assetArchiveFile = nullptr;
/**
* Unimplemented method intended to be implemented by the platform that
* will be used to request a File pointer to the asset.
*
* @return Pointer to the opened asset archive.
*/
FILE * openAssetArchiveFile();
/**
* Create a new asset loader. Asset Loaders can be used to load data from
* a file in a myriad of ways.
*
* @param fileName File name of the asset that is to be loaded.
*/
AssetDataLoader(std::string filename);
/**
* Platform-centric method to open a file buffer to an asset.
*/
void open();
/**
* Closes the previously ppened asset.
* @return 0 if successful, otherwise false.
*/
int32_t close();
/**
* Read bytes from buffer.
* @param buffer Pointer to a ubyte array to buffer data into.
* @param size Length of the data buffer (How many bytes to read).
* @return The count of bytes read.
*/
size_t read(uint8_t *buffer, size_t size);
/**
* Reads bytes from the buffer until a given delimiter is found. Returned
* position will be the index of the delimiter within the buffer.
*
* @param buffer Buffer to read into.
* @param maxSize Maximum size of the buffer.
* @param delimiter Delimiter to read until.
* @return The count of bytes read (including null terminator)
*/
size_t readUntil(
uint8_t *buffer,
const size_t maxSize,
const char_t delimiter
);
/**
* Get the size of the asset.
* @return The size of the asset in bytes.
*/
size_t getSize();
/**
* Skips the read head forward to a given position.
*
* @param n Count of bytes to progress the read head by.
* @return Count of bytes progressed.
*/
size_t skip(size_t n);
/**
* Rewind the read head to the beginning of the file.
*/
void rewind();
/**
* Sets the absolute position of the read head within the buffer of the
* file.
*
* @param absolutePosition Absolute position to set the read head to.
*/
size_t setPosition(const size_t absolutePosition);
/**
* Returns the current position of the read head.
*
* @return The current read head position.
*/
size_t getPosition();
/**
* Cleanup the asset loader.
*/
virtual ~AssetDataLoader();
};
}

View File

@ -1,17 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AssetLoader.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
AssetLoader::AssetLoader(const std::string name) : name(name) {
assertTrue(name.size() > 0, "Asset::Asset: Name cannot be empty");
}
AssetLoader::~AssetLoader() {
this->loaded = false;
}

View File

@ -1,41 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class AssetLoader {
public:
const std::string name;
bool_t loaded = false;
/**
* Create an abstract Asset object.
*
* @param name Name of the asset.
*/
AssetLoader(const std::string name);
/**
* Virtual function that will be called by the asset manager on a
* synchronous basis. This will only trigger if the blocks are false and
* the loaded is also false.
*/
virtual void updateSync() = 0;
/**
* Virtual function called by the asset manager asynchronously every tick.
* This will only trigger if blocks are false and the loaded state is also
* false.
*/
virtual void updateAsync() = 0;
/**
* Dispose the asset item.
*/
virtual ~AssetLoader();
};
}

View File

@ -1,101 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AssetManager.hpp"
#include "loaders/TextureLoader.hpp"
#include "loaders/TrueTypeLoader.hpp"
#include "loaders/JSONLoader.hpp"
using namespace Dawn;
void AssetManager::init() {
}
void AssetManager::update() {
auto itPending = pendingAssetLoaders.begin();
while(itPending != pendingAssetLoaders.end()) {
auto loader = *itPending;
loader->updateSync();
loader->updateAsync();
if(loader->loaded) {
finishedAssetLoaders.push_back(loader);
itPending = pendingAssetLoaders.erase(itPending);
} else {
itPending++;
}
}
}
void AssetManager::removeExisting(const std::string filename) {
auto existing = std::find_if(
pendingAssetLoaders.begin(), pendingAssetLoaders.end(),
[&](auto &loader) {
return loader->name == filename;
}
);
if(existing != pendingAssetLoaders.end()) {
pendingAssetLoaders.erase(existing);
}
existing = std::find_if(
finishedAssetLoaders.begin(), finishedAssetLoaders.end(),
[&](auto &loader) {
return loader->name == filename;
}
);
if(existing != finishedAssetLoaders.end()) {
finishedAssetLoaders.erase(existing);
}
}
bool_t AssetManager::isEverythingLoaded() {
return pendingAssetLoaders.size() == 0;
}
bool_t AssetManager::isLoaded(const std::string filename) {
auto existing = this->getExisting<AssetLoader>(filename);
if(existing) return existing->loaded;
return false;
}
template<>
std::shared_ptr<TrueTypeTexture> AssetManager::get<TrueTypeTexture>(
const std::string filename,
const uint32_t fontSize
) {
auto existing = this->getExisting<TrueTypeLoader>(filename);
if(existing) {
// Check pointer hasn't gone stale, if it has remove it and create new.
auto texture = existing->getTexture(fontSize);
if(texture) return texture;
this->removeExisting(filename);
}
std::shared_ptr<TrueTypeLoader> loader = std::make_shared<TrueTypeLoader>(
filename
);
pendingAssetLoaders.push_back(std::static_pointer_cast<AssetLoader>(loader));
return loader->getTexture(fontSize);
}
template<>
std::shared_ptr<json> AssetManager::get<json>(
const std::string filename
) {
auto existing = this->getExisting<JSONLoader>(filename);
if(existing) return existing->data;
std::shared_ptr<JSONLoader> loader = std::make_shared<JSONLoader>(
filename
);
pendingAssetLoaders.push_back(std::static_pointer_cast<AssetLoader>(loader));
return loader->data;
}
AssetManager::~AssetManager() {
}

View File

@ -1,107 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "asset/AssetLoader.hpp"
namespace Dawn {
class AssetManager final {
private:
std::vector<std::shared_ptr<AssetLoader>> pendingAssetLoaders;
std::vector<std::shared_ptr<AssetLoader>> finishedAssetLoaders;
/**
* Returns an existing asset loader if it exists.
*
* @param filename The filename of the asset to get.
* @return The asset loader if it exists, otherwise nullptr.
*/
template<class T>
std::shared_ptr<T> getExisting(const std::string filename) {
auto existing = std::find_if(
pendingAssetLoaders.begin(), pendingAssetLoaders.end(),
[&](auto &loader) {
return loader->name == filename;
}
);
if(existing == pendingAssetLoaders.end()) {
existing = std::find_if(
finishedAssetLoaders.begin(), finishedAssetLoaders.end(),
[&](auto &loader) {
return loader->name == filename;
}
);
if(existing == finishedAssetLoaders.end()) return nullptr;
}
return std::static_pointer_cast<T>(*existing);
}
/**
* Removes an existing asset loader if it exists.
*
* @param filename The filename of the asset to remove.
*/
void removeExisting(const std::string filename);
public:
/**
* Initializes this asset manager so it can begin accepting assets.
*/
void init();
/**
* Updates the asset manager.
*/
void update();
/**
* Returns whether the asset manager has loaded all of the currently
* managed assets.
*
* @return True if all assets have been loaded.
*/
bool_t isEverythingLoaded();
/**
* Returns whether the asset manager has loaded the given asset.
*
* @param filename The filename of the asset to check.
* @return True if the asset has been loaded.
*/
bool_t isLoaded(const std::string filename);
/**
* Returns the asset loader for the given asset.
*
* @param filename The filename of the asset to get.
* @param fontSize The font size to get the truetype asset of.
* @return The asset loader for the given asset.
*/
template<class T>
std::shared_ptr<T> get(const std::string filename);
/**
* Returns the asset loader for the given asset.
*
* @param filename The filename of the asset to get.
* @param fontSize The font size to get the truetype asset of.
* @return The asset loader for the given asset.
*/
template<class T>
std::shared_ptr<T> get(
const std::string filename,
const uint32_t fontSize
);
/**
* Dispose the asset manager, and all attached assets.
*/
~AssetManager();
};
}

View File

@ -1,15 +0,0 @@
# Copyright (c) 2022 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
AssetLoader.cpp
AssetDataLoader.cpp
AssetManager.cpp
)
# Subdirs
add_subdirectory(loaders)

View File

@ -1,12 +0,0 @@
# Copyright (c) 2022 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
TextureLoader.cpp
TrueTypeLoader.cpp
JSONLoader.cpp
)

View File

@ -1,46 +0,0 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "JSONLoader.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
JSONLoader::JSONLoader(const std::string name) :
AssetLoader(name),
loader(name + ".json"),
state(JSONLoaderLoadState::INITIAL)
{
data = std::make_shared<json>();
}
void JSONLoader::updateAsync() {
if(this->state != JSONLoaderLoadState::INITIAL) return;
this->state = JSONLoaderLoadState::LOADING_FILE;
this->loader.open();
auto size = this->loader.getSize();
auto buffer = new uint8_t[size + 1];
assertNotNull(buffer, "Failed to allocate buffer!");
this->state = JSONLoaderLoadState::PARSING_DATA;
auto read = this->loader.read(buffer, size);
assertTrue(read == size, "Failed to read entire file!");
buffer[size] = '\0';
*data = json::parse(buffer);
delete[] buffer;
this->state = JSONLoaderLoadState::DONE;
this->loaded = true;
}
void JSONLoader::updateSync() {
}
JSONLoader::~JSONLoader() {
this->data = nullptr;
}

View File

@ -1,42 +0,0 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "asset/AssetLoader.hpp"
#include "asset/AssetDataLoader.hpp"
namespace Dawn {
enum class JSONLoaderLoadState {
INITIAL,
LOADING_FILE,
PARSING_DATA,
DONE
};
class JSONLoader : public AssetLoader {
protected:
AssetDataLoader loader;
enum JSONLoaderLoadState state;
public:
std::shared_ptr<json> data;
/**
* Constructs a JSON asset loader. You should instead use the parent
* asset managers' abstracted load method
*
* @param name File name asset to load, omitting the extension.
*/
JSONLoader(const std::string name);
void updateSync() override;
void updateAsync() override;
/**
* Dispose / Cleanup the JSON asset.
*/
~JSONLoader();
};
}

View File

@ -1,147 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TextureLoader.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
TextureLoader::TextureLoader(const std::string name) :
AssetLoader(name),
loader(name + ".texture"),
state(TextureLoaderLoadState::INITIAL)
{
sharedTexture = std::make_shared<Texture>();
weakTexture = sharedTexture;
}
void TextureLoader::updateAsync() {
if(this->state != TextureLoaderLoadState::INITIAL) return;
this->state = TextureLoaderLoadState::ASYNC_LOADING;
this->loader.open();
// Read in the header.
uint8_t buffer[TEXTURE_LOADER_HEADER_SIZE];
size_t pos = 0;
// Read Version
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
std::string version = std::string((char*)buffer);
assertTrue(version == "DT_2.00", "Invalid Texture Version!");
// Read Texture Width
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
width = std::stoi(std::string((char*)buffer));
assertTrue(width > 0, "Invalid Texture Width!");
// Read Texture Height
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
height = std::stoi(std::string((char*)buffer));
assertTrue(height > 0, "Invalid Texture Height!");
// Texture Format (RGBA, RGB, etc)
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
int32_t iFormat = std::stoi(std::string((char*)buffer));
switch(iFormat) {
case 1: format = TextureFormat::R; break;
case 2: format = TextureFormat::RG; break;
case 3: format = TextureFormat::RGB; break;
case 4: format = TextureFormat::RGBA; break;
default: assertUnreachable("Invalid Texture Format %i!", iFormat);
}
// Wrap X
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
int32_t iWrapX = std::stoi(std::string((char*)buffer));
switch(iWrapX) {
case 0: wrapX = TextureWrapMode::REPEAT; break;
case 1: wrapX = TextureWrapMode::MIRRORED_REPEAT; break;
case 2: wrapX = TextureWrapMode::CLAMP_TO_EDGE; break;
case 3: wrapX = TextureWrapMode::CLAMP_TO_BORDER; break;
default: assertUnreachable("Invalid Texture Wrap X %i!", iWrapX);
}
// Wrap Y
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
int32_t iWrapY = std::stoi(std::string((char*)buffer));
switch(iWrapY) {
case 0: wrapY = TextureWrapMode::REPEAT; break;
case 1: wrapY = TextureWrapMode::MIRRORED_REPEAT; break;
case 2: wrapY = TextureWrapMode::CLAMP_TO_EDGE; break;
case 3: wrapY = TextureWrapMode::CLAMP_TO_BORDER; break;
default: assertUnreachable("Invalid Texture Wrap Y %i!", iWrapY);
}
// Filter Min
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
int32_t iFilterMin = std::stoi(std::string((char*)buffer));
switch(iFilterMin) {
case 0: filterMin = TextureFilterMode::NEAREST; break;
case 1: filterMin = TextureFilterMode::LINEAR; break;
default: assertUnreachable("Invalid Texture Filter Min %i!", iFilterMin);
}
// Filter Mag
this->loader.setPosition(pos);
pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
int32_t iFilterMag = std::stoi(std::string((char*)buffer));
switch(iFilterMag) {
case 0: filterMag = TextureFilterMode::NEAREST; break;
case 1: filterMag = TextureFilterMode::LINEAR; break;
default: assertUnreachable("Invalid Texture Filter Mag %i!", iFilterMag);
}
// Data begins here. This part is done synchronously directly to the GPU.
this->loader.setPosition(pos);
size_t bufferSize = width * height * iFormat;
data = new uint8_t[bufferSize];
assertNotNull(data, "Failed to allocate texture data!");
this->loader.read(data, bufferSize);
// Handoff to sync to buffer to GPU.
this->state = TextureLoaderLoadState::ASYNC_DONE;
}
void TextureLoader::updateSync() {
if(this->state != TextureLoaderLoadState::ASYNC_DONE) return;
this->state = TextureLoaderLoadState::SYNC_LOADING;
assertNotNull(this->sharedTexture, "Texture is null!");
assertNotNull(this->data, "Texture data is null!");
// Setup Texture
this->sharedTexture->setSize(
this->width,
this->height,
this->format,
TextureDataFormat::UNSIGNED_BYTE
);
this->sharedTexture->buffer(this->data);
// Free data buffer
delete[] this->data;
this->data = nullptr;
// Leat go of the held pointer
this->sharedTexture = nullptr;
// Hand off and call done
this->state = TextureLoaderLoadState::SYNC_DONE;
this->loaded = true;
}
TextureLoader::~TextureLoader() {
if(this->data != nullptr) {
delete[] this->data;
this->data = nullptr;
}
this->sharedTexture = nullptr;
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "asset/AssetLoader.hpp"
#include "asset/AssetDataLoader.hpp"
#include "display/Texture.hpp"
#define TEXTURE_LOADER_HEADER_SIZE 256
namespace Dawn {
enum class TextureLoaderLoadState {
INITIAL,
ASYNC_LOADING,
ASYNC_DONE,
SYNC_LOADING,
SYNC_DONE
};
class TextureLoader : public AssetLoader {
protected:
AssetDataLoader loader;
enum TextureLoaderLoadState state;
uint8_t *data = nullptr;
int32_t width = -1, height = -1;
enum TextureFormat format;
enum TextureWrapMode wrapX;
enum TextureWrapMode wrapY;
enum TextureFilterMode filterMin;
enum TextureFilterMode filterMag;
public:
std::shared_ptr<Texture> sharedTexture;
std::weak_ptr<Texture> weakTexture;
/**
* Constructs a texture asset loader. You should instead use the parent
* asset managers' abstracted load method
*
* @param name File name asset to load, omitting the extension.
*/
TextureLoader(const std::string name);
void updateSync() override;
void updateAsync() override;
/**
* Dispose / Cleanup the texture asset. Will also dispose the underlying
* texture itself.
*/
~TextureLoader();
};
}

View File

@ -1,97 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TrueTypeLoader.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
TrueTypeLoader::TrueTypeLoader(const std::string name) :
AssetLoader(name),
loader(name + ".ttf")
{
// Init the font.
auto ret = FT_Init_FreeType(&fontLibrary);
assertTrue(ret == 0, "Failed to initialize FreeType library.");
}
void TrueTypeLoader::updateSync() {
if(state != TrueTypeLoaderState::ASYNC_DONE) return;
state = TrueTypeLoaderState::SYNC_LOADING;
// Init all the textures.
auto it = textures.begin();
while(it != textures.end()) {
auto texture = it->second.lock();
if(texture) {
texture->setFace(face);
it++;
continue;
}
it = textures.erase(it);
}
// Done
state = TrueTypeLoaderState::SYNC_DONE;
this->loaded = true;
}
void TrueTypeLoader::updateAsync() {
if(state != TrueTypeLoaderState::INITIAL) return;
state = TrueTypeLoaderState::ASYNC_LOADING;
// Load the data.
this->loader.open();
size_t size = loader.getSize();
buffer = new uint8_t[size];
// Read the data.
size_t readSize = loader.read(buffer, size);
assertTrue(readSize == size, "Failed to read all data from TrueTypeLoader.");
// Init the font.
auto ret = FT_New_Memory_Face(fontLibrary, buffer, size, 0, &face);
assertTrue(ret == 0, "Failed to load font face.");
// Now close the asset loader
loader.close();
state = TrueTypeLoaderState::ASYNC_DONE;
}
std::shared_ptr<TrueTypeTexture> TrueTypeLoader::getTexture(
const uint32_t fontSize
) {
// Check if we have the texture already and it hasn't gone stale.
auto it = textures.find(fontSize);
if(it != textures.end()) {
if(!it->second.expired()) return it->second.lock();
textures.erase(it);
}
// Create the texture.
auto texture = std::make_shared<TrueTypeTexture>(fontSize);
textures[fontSize] = texture;
if(this->loaded) texture->setFace(face);
return texture;
}
TrueTypeLoader::~TrueTypeLoader() {
if(
this->state == TrueTypeLoaderState::SYNC_DONE ||
this->state == TrueTypeLoaderState::SYNC_LOADING ||
this->state == TrueTypeLoaderState::ASYNC_DONE
) {
FT_Done_Face(face);
}
FT_Done_FreeType(fontLibrary);
if(buffer != nullptr) {
delete[] buffer;
buffer = nullptr;
}
}

View File

@ -1,57 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "asset/AssetLoader.hpp"
#include "asset/AssetDataLoader.hpp"
#include "display/font/TrueTypeTexture.hpp"
namespace Dawn {
enum class TrueTypeLoaderState {
INITIAL,
ASYNC_LOADING,
ASYNC_DONE,
SYNC_LOADING,
SYNC_DONE
};
class TrueTypeLoader : public AssetLoader {
protected:
FT_Library fontLibrary;
FT_Face face;
AssetDataLoader loader;
std::unordered_map<uint32_t, std::weak_ptr<TrueTypeTexture>> textures;
enum TrueTypeLoaderState state = TrueTypeLoaderState::INITIAL;
uint8_t *buffer = nullptr;
public:
/**
* Constructs a TrueTypeLoader. You should instead use the parent
* asset managers' abstracted load method
*
* @param name File name asset to load, omitting the extension.
*/
TrueTypeLoader(const std::string name);
void updateSync() override;
void updateAsync() override;
/**
* Returns the texture for the given font size.
*
* @param fontSize Font size to get the texture for.
* @return Texture for the given character.
*/
std::shared_ptr<TrueTypeTexture> getTexture(
const uint32_t fontSize
);
/**
* Dispose / Cleanup the truetype asset. Will also dispose the underlying
* truetype itself.
*/
~TrueTypeLoader();
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
IAudioManager.cpp
)

View File

@ -1,14 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "IAudioManager.hpp"
using namespace Dawn;
IAudioManager::IAudioManager() {
}
IAudioManager::~IAudioManager() {
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class IAudioManager {
public:
/**
* Construct a new IAudioManager.
*/
IAudioManager();
/**
* Initializes the audio manager system.
*/
virtual void init() = 0;
/**
* Ticks/Update the audio manager system.
*/
virtual void update() = 0;
/**
* Deinitializes the audio manager system.
*/
virtual ~IAudioManager();
};
}

View File

@ -1,14 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
SimpleComponent.cpp
)
# Subdirs
add_subdirectory(display)
add_subdirectory(ui)
add_subdirectory(vn)

View File

@ -1,27 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SimpleComponent.hpp"
using namespace Dawn;
void SimpleComponent::onInit() {
this->initMethod(*this, events);
}
void SimpleComponent::onDispose() {
for(auto &event : events) {
event();
}
}
std::shared_ptr<SimpleComponent> Dawn::addSimpleComponent(
std::shared_ptr<SceneItem> item,
std::function<void(SceneComponent&, std::vector<std::function<void()>>&)> init
) {
auto cmp = item->addComponent<SimpleComponent>();
cmp->initMethod = init;
return cmp;
}

View File

@ -1,31 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/Scene.hpp"
namespace Dawn {
class SimpleComponent final : public SceneComponent {
private:
std::vector<std::function<void()>> events;
public:
std::function<void(
SceneComponent&,
std::vector<std::function<void()>>&
)> initMethod;
void onInit() override;
void onDispose() override;
};
std::shared_ptr<SimpleComponent> addSimpleComponent(
std::shared_ptr<SceneItem> item,
std::function<void(
SceneComponent&,
std::vector<std::function<void()>>&
)> init
);
}

View File

@ -1,13 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Camera.cpp
MeshRenderer.cpp
)
# Subdirs
add_subdirectory(material)

View File

@ -1,67 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "Camera.hpp"
#include "game/Game.hpp"
using namespace Dawn;
void Camera::onInit() {
this->onResizeListener = this->getRenderTarget()->onResize.listen([&](
float_t width, float_t height
) {
this->onResize.emit(this->getRenderTarget(), width, height);
});
}
void Camera::onDispose() {
renderTarget = nullptr;
}
std::shared_ptr<RenderTarget> Camera::getRenderTarget() {
if(this->renderTarget) return this->renderTarget;
return getGame()->renderHost.getBackBufferRenderTarget();
}
glm::mat4 Camera::getProjection() {
switch(this->type) {
case CameraType::ORTHOGONAL:
return glm::ortho(
(float_t)this->orthoLeft,
(float_t)this->orthoRight,
(float_t)this->orthoBottom,
(float_t)this->orthoTop,
(float_t)this->clipNear,
(float_t)this->clipFar
);
case CameraType::PERSPECTIVE:
return glm::perspective(
(float_t)this->fov,
this->getAspect(),
(float_t)this->clipNear,
(float_t)this->clipFar
);
}
assertUnreachable("Invalid Camera Type!");
return glm::mat4(1.0f);
}
float_t Camera::getAspect() {
auto rt = this->getRenderTarget();
return rt->getWidth() / rt->getHeight();
}
void Camera::setRenderTarget(std::shared_ptr<RenderTarget> renderTarget) {
onResizeListener();
this->renderTarget = renderTarget;
this->onResizeListener = this->getRenderTarget()->onResize.listen([&](
float_t width, float_t height
) {
this->onResize.emit(this->getRenderTarget(), width, height);
});
}

View File

@ -1,67 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItem.hpp"
#include "display/RenderTarget.hpp"
namespace Dawn {
enum CameraType {
PERSPECTIVE,
ORTHOGONAL
};
class Camera final : public SceneComponent {
private:
std::shared_ptr<RenderTarget> renderTarget;
std::function<void()> onResizeListener;
public:
Event<std::shared_ptr<RenderTarget>, float_t, float_t> onResize;
float_t clipNear = 0.01f;
float_t clipFar = 1000.0f;
enum CameraType type = CameraType::PERSPECTIVE;
float_t fov = 0.785398f;
float_t orthoLeft = -1.0f;
float_t orthoRight = 1.0f;
float_t orthoBottom = -1.0f;
float_t orthoTop = 1.0f;
void onInit() override;
void onDispose() override;
/**
* Returns the aspect ratio that the camera is using. In future I may
* allow you to specify a custom ratio for stylistic reasons but for now I
* just take the ratio of the specific frame buffer.
*
* @return The aspect ratio as a ratio of w/h.
*/
float_t getAspect();
/**
* Returns the render target for this camera.
*
* @return Render target.
*/
std::shared_ptr<RenderTarget> getRenderTarget();
/**
* Returns the projection matrix for this camera.
*
* @return Projection matrix.
*/
glm::mat4 getProjection();
/**
* Sets the render target for this camera.
*
* @param renderTarget The render target to set.
*/
void setRenderTarget(std::shared_ptr<RenderTarget> renderTarget);
};
}

View File

@ -1,56 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/pass/RenderPass.hpp"
#include "display/pass/RenderPassContext.hpp"
#include "display/mesh/Mesh.hpp"
namespace Dawn {
class IRenderableComponent {
public:
/**
* Retreive the list of render passes for this component.
*
* @param ctx Context for the render pass.
* @return List of render passes.
*/
virtual std::vector<std::shared_ptr<IRenderPass>> getPasses(
struct RenderPassContext &ctx
) = 0;
};
/**
* Short-hand function to create a render pass.
*
* @tparam S Shader type.
* @tparam D Shader's data type
* @param self Instance of the IRenderableComponent that is creating the pass.
* @param data Data to use for the render pass.
* @return Created render pass.
*/
template<class S, typename D>
std::shared_ptr<IRenderPass> createRenderPass(
SceneComponent &self,
const D data,
const std::unordered_map<
shadertexturebinding_t, std::shared_ptr<Texture>
> textures = {},
const std::shared_ptr<Mesh> mesh = nullptr,
const enum MeshDrawMode drawMode = MeshDrawMode::TRIANGLES,
int32_t indiceStart = 0,
int32_t indiceCount = -1
) {
return std::make_shared<RenderPass<S,D>>(
self,
data,
textures,
mesh,
drawMode,
indiceStart,
indiceCount
);
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "MeshRenderer.hpp"
using namespace Dawn;
void MeshRenderer::onInit() {
}
void MeshRenderer::onDispose() {
mesh = nullptr;
}

View File

@ -1,18 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
#include "scene/SceneItem.hpp"
namespace Dawn {
class MeshRenderer final : public SceneComponent {
public:
std::shared_ptr<Mesh> mesh;
void onInit() override;
void onDispose() override;
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Material.cpp
SimpleTexturedMaterial.cpp
)

View File

@ -1,16 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Material.hpp"
using namespace Dawn;
void Material::onInit() {
}
void Material::onDispose() {
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneComponent.hpp"
#include "component/display/IRenderableComponent.hpp"
namespace Dawn {
class Material :
public SceneComponent,
public IRenderableComponent
{
protected:
public:
void onInit() override;
void onDispose() override;
};
}

View File

@ -1,51 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SimpleTexturedMaterial.hpp"
using namespace Dawn;
struct Color SimpleTexturedMaterial::getColor() {
return this->data.color;
}
std::shared_ptr<Texture> SimpleTexturedMaterial::getTexture() {
return this->texture;
}
void SimpleTexturedMaterial::setTexture(std::shared_ptr<Texture> texture) {
this->texture = texture;
}
void SimpleTexturedMaterial::setColor(const struct Color color) {
this->data.color = color;
}
std::vector<std::shared_ptr<IRenderPass>> SimpleTexturedMaterial::getPasses(
struct RenderPassContext &ctx
) {
this->data.model = this->getItem()->getWorldTransform();
this->data.projection = ctx.camera->getProjection();
this->data.view = ctx.camera->getItem()->getWorldTransform();
auto textures = std::unordered_map<
shadertexturebinding_t, std::shared_ptr<Texture>
>();
if(this->texture) {
this->data.hasTexture = true;
this->data.texture = 0;
textures[this->data.texture] = this->texture;
} else {
this->data.hasTexture = false;
}
return {
createRenderPass<SimpleTexturedShader, struct SimpleTexturedShaderData>(
*this,
data,
textures
)
};
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "component/display/material/Material.hpp"
#include "display/shader/SimpleTexturedShader.hpp"
#include "display/Texture.hpp"
namespace Dawn {
class SimpleTexturedMaterial : public Material {
private:
struct SimpleTexturedShaderData data;
std::shared_ptr<Texture> texture;
public:
/**
* Returns the color of this material.
*/
struct Color getColor();
/**
* Returns the texture of this material.
*
* @return The texture of this material.
*/
std::shared_ptr<Texture> getTexture();
/**
* Sets the texture of this material.
*
* @param texture The texture to set.
*/
void setTexture(std::shared_ptr<Texture> texture);
/**
* Sets the color of this material.
*
* @param color The color to set.
*/
void setColor(const struct Color color);
std::vector<std::shared_ptr<IRenderPass>> getPasses(
struct RenderPassContext &ctx
) override;
};
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
UICanvas.cpp
)

View File

@ -1,149 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UICanvas.hpp"
#include "display/pass/RenderPass.hpp"
#include "display/mesh/QuadMesh.hpp"
#include "ui/UIElement.hpp"
using namespace Dawn;
void UICanvas::onInit() {
mesh = std::make_shared<Mesh>();
mesh->createBuffers(
QUAD_VERTICE_COUNT * UI_SHADER_QUAD_COUNT,
QUAD_INDICE_COUNT * UI_SHADER_QUAD_COUNT
);
for(int32_t i = 0; i < UI_SHADER_QUAD_COUNT; i++) {
QuadMesh::bufferWithIndex(
mesh,
glm::vec4(0, 0, 1, 1),
glm::vec4(0, 0, 1, 1),
i * QUAD_VERTICE_COUNT,
i * QUAD_INDICE_COUNT,
i * QUAD_VERTICE_COUNT
);
}
}
void UICanvas::onDispose() {
mesh = nullptr;
}
std::vector<std::shared_ptr<IRenderPass>> UICanvas::getPasses(
struct RenderPassContext &ctx
) {
if(this->elements.empty()) return {};
glm::mat4 projection;
glm::mat4 view;
// Setup the projection and views
data.projection = glm::ortho(
0.0f, ctx.renderTarget->getWidth(),
ctx.renderTarget->getHeight(), 0.0f,
0.0f, 1.0f
);
data.view = glm::mat4(1.0f);
data.model = glm::mat4(1.0f);
// Reset the passes
this->passes.clear();
this->textureBindings.clear();
this->textures.clear();
quadCount = 0;
nextBinding = 0;
// Alignment root
const glm::vec2 rootPosition = { 0, 0 };
const glm::vec2 rootSize = {
ctx.renderTarget->getWidth(),
ctx.renderTarget->getHeight()
};
const float_t rootScale = 1.0f;
// Get the quads for each component
auto itComponents = elements.begin();
auto self = std::ref(*this);
while(itComponents != elements.end()) {
auto component = *itComponents;
component->updateAlignment(rootPosition, rootSize, rootScale);
component->getQuads(self);
++itComponents;
}
// Flush the remaining quads
flushPass();
return passes;
}
void UICanvas::addQuad(
const glm::vec4 quad,
const glm::vec4 uvs,
const struct Color color,
const enum UIShaderQuadStyle style,
const std::shared_ptr<Texture> text
) {
glm::vec4 styleData;
styleData[0] = (float_t)style;
if(text == nullptr) {
styleData[1] = -1;
} else {
shadertexturebinding_t texture;
auto bindingIt = textureBindings.find(text);
if(bindingIt == textureBindings.end()) {
if(nextBinding >= UI_SHADER_TEXTURE_COUNT) {
flushPass();
}
textureBindings[text] = nextBinding;
textures[nextBinding] = text;
data.textures[nextBinding] = nextBinding;
texture = nextBinding++;
} else {
texture = bindingIt->second;
}
styleData[1] = (float_t)texture;
}
data.quads[quadCount] = {
quad,
uvs,
color,
styleData
};
quadCount++;
if(quadCount == UI_SHADER_QUAD_COUNT) flushPass();
}
void UICanvas::flushPass() {
if(quadCount == 0) return;
auto pass = createRenderPass<UIShader, UIShaderData>(
std::ref(*this),
data,
textures,
mesh,
MeshDrawMode::TRIANGLES,
0,
quadCount * QUAD_INDICE_COUNT
);
passes.push_back(pass);
quadCount = 0;
nextBinding = 0;
textures.clear();
textureBindings.clear();
}
void UICanvas::addElement(std::shared_ptr<UIElement> element) {
elements.push_back(element);
}
void UICanvas::removeElement(std::shared_ptr<UIElement> element) {
auto it = std::find(elements.begin(), elements.end(), element);
if(it != elements.end()) elements.erase(it);
}

View File

@ -1,80 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItem.hpp"
#include "component/display/IRenderableComponent.hpp"
#include "display/shader/UIShader.hpp"
namespace Dawn {
class UIElement;
class UICanvas :
public SceneComponent,
public IRenderableComponent
{
private:
std::shared_ptr<Mesh> mesh;
UIShaderData data;
std::vector<std::shared_ptr<UIElement>> elements;
size_t quadCount = 0;
shadertexturebinding_t nextBinding = 0;
std::unordered_map<
shadertexturebinding_t, std::shared_ptr<Texture>
> textures;
std::map<
std::shared_ptr<Texture>, shadertexturebinding_t
> textureBindings;
std::vector<std::shared_ptr<IRenderPass>> passes;
protected:
virtual void onInit() override;
virtual void onDispose() override;
/**
* Flushes all pending quads to the render pass. This doesn't actually
* render anything, it just flushes the data buffer to a new pass.
*/
void flushPass();
public:
std::vector<std::shared_ptr<IRenderPass>> getPasses(
struct RenderPassContext &ctx
) override;
/**
* Adds a quad to the canvas and performs a flush if necessary.
*
* @param quad The quad to add.
* @param uvs The UVs to use for the quad.
* @param color The color to use for the quad.
* @param style Style that the quad should be rendered in.
* @param texture The texture to use for the quad, can be null.
*/
void addQuad(
const glm::vec4 quad,
const glm::vec4 uvs,
const struct Color color,
const enum UIShaderQuadStyle style,
const std::shared_ptr<Texture> texture = nullptr
);
/**
* Adds a component to the canvas.
*
* @param component The component to add.
*/
void addElement(std::shared_ptr<UIElement> component);
/**
* Removes a component from the canvas.
*
* @param component The component to remove.
*/
void removeElement(std::shared_ptr<UIElement> component);
};
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VNManager.cpp
)

View File

@ -1,65 +0,0 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VNManager.hpp"
using namespace Dawn;
void VNManager::onInit() {
assertNotNull(canvas, "Canvas must be set.");
assertNotNull(texture, "Texture must be set.");
container = std::make_shared<UIContainer>();
container->align = { 0, 128, 0, 0 };
container->alignX = UIAlignmentType::STRETCH;
container->alignY = UIAlignmentType::END;
borders = std::make_shared<UIRectangle>();
borders->align = { 0, 0, 0, 0 };
borders->alignX = UIAlignmentType::STRETCH;
borders->alignY = UIAlignmentType::STRETCH;
borders->color = COLOR_BLUE;
container->appendChild(borders);
background = std::make_shared<UIRectangle>();
background->align = { 16, 16, 16, 16 };
background->alignX = UIAlignmentType::STRETCH;
background->alignY = UIAlignmentType::STRETCH;
background->color = COLOR_RED;
container->appendChild(background);
label = std::make_shared<UILabel>();
label->align = { 16, 16, 16, 16 };
label->alignX = UIAlignmentType::STRETCH;
label->alignY = UIAlignmentType::STRETCH;
label->setFont(texture);
label->setText(text);
container->appendChild(label);
canvas->addElement(container);
listeners.push_back(getScene()->onUnpausedUpdate.listen([&](const float_t d) {
}));
auto w = label->getWidth();
auto cw = container->getWidth();
}
void VNManager::onDispose() {
canvas->removeElement(container);
container = nullptr;
label = nullptr;
background = nullptr;
borders = nullptr;
texture = nullptr;
canvas = nullptr;
}
void VNManager::setText(const std::wstring &text) {
this->text = text;
if(label) label->setText(text);
}

View File

@ -1,36 +0,0 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItem.hpp"
#include "ui/container/UIContainer.hpp"
#include "ui/elements/UILabel.hpp"
#include "ui/elements/UIRectangle.hpp"
namespace Dawn {
class VNManager : public SceneComponent {
protected:
std::shared_ptr<UIContainer> container;
std::shared_ptr<UILabel> label;
std::shared_ptr<UIRectangle> borders;
std::shared_ptr<UIRectangle> background;
std::wstring text;
public:
std::shared_ptr<UICanvas> canvas;
std::shared_ptr<TrueTypeTexture> texture;
void onInit() override;
void onDispose() override;
/**
* Sets the text to display.
*
* @param text The text to display.
*/
void setText(const std::wstring &text);
};
}

21
src/dawn/dawn.h Normal file
View File

@ -0,0 +1,21 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#include <errno.h>
typedef bool bool_t;
typedef char char_t;
typedef unsigned char uchar_t;

View File

@ -1,53 +0,0 @@
/**
* Copyright (c) 2022 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
// Static Libs
extern "C" {
// Standard Libs
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#include <errno.h>
typedef bool bool_t;
typedef char char_t;
}
#include <vector>
#include <iostream>
#include <thread>
#include <map>
#include <array>
#include <memory>
#include <algorithm>
#include <sstream>
#include <string>
#include <functional>
#include <cstdarg>
#include <glm/glm.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
// #include <glm/gtx/component_wise.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/scalar_constants.hpp>
// #include <glm/gtx/intersect.hpp>
#include <glm/gtc/type_ptr.hpp>
#define GLM_ENABLE_EXPERIMENTAL 1
#include <glm/gtx/matrix_decompose.hpp>
#include <nlohmann/json.hpp>
using json = nlohmann::json;

View File

@ -1,17 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Color.cpp
RenderPipeline.cpp
IRenderHost.cpp
)
# Subdirs
add_subdirectory(font)
add_subdirectory(mesh)
add_subdirectory(shader)

View File

@ -1,84 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Color.hpp"
#include "util/String.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
struct Color Color::fromString(const std::string str) {
// Convert to lowercase
auto lower = String::toLowercase(str);
if(String::includes(lower, "cornflower")) {
return COLOR_CORNFLOWER_BLUE;
} else if(String::includes(lower, "magenta")) {
return COLOR_MAGENTA;
} else if(String::includes(lower, "white")) {
return COLOR_WHITE;
} else if(String::includes(lower, "black")) {
return COLOR_BLACK;
} else if(String::includes(lower, "red")) {
return COLOR_RED;
} else if(String::includes(lower, "green")) {
return COLOR_GREEN;
} else if(String::includes(lower, "blue")) {
return COLOR_BLUE;
} else if(String::includes(lower, "transparent")) {
return COLOR_TRANSPARENT;
}
// Hex code?
if(lower[0] == '#') {
// Remove the hash
lower = lower.substr(1);
// Convert to RGB
if(lower.length() == 3) {
// Convert to 6 digit hex
lower = lower[0] + lower[0] + lower[1] + lower[1] + lower[2] + lower[2];
}
// Convert to RGB
return {
(float_t)std::stoi(lower.substr(0, 2), nullptr, 16) / 255.0f,
(float_t)std::stoi(lower.substr(2, 2), nullptr, 16) / 255.0f,
(float_t)std::stoi(lower.substr(4, 2), nullptr, 16) / 255.0f,
1.0f
};
}
// Split by comma
auto splitByComma = String::split(str, ",");
if(splitByComma.size() == 3) {
// RGB
return {
(float_t)std::stof(splitByComma[0]),
(float_t)std::stof(splitByComma[1]),
(float_t)std::stof(splitByComma[2]),
1.0f
};
} else if(splitByComma.size() == 4) {
// RGBA
return {
(float_t)std::stof(splitByComma[0]),
(float_t)std::stof(splitByComma[1]),
(float_t)std::stof(splitByComma[2]),
(float_t)std::stof(splitByComma[3])
};
}
// TODO: Parse other kinds of colors
assertUnreachable("Failed to find a color match for %s", str);
return {};
}

View File

@ -1,89 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
struct ColorU8 {
uint8_t r, g, b, a;
};
struct Color final {
/**
* Returns a color from a string.
*
* @param str String to parse.
* @return Color parsed.
*/
static struct Color fromString(const std::string str);
float_t r, g, b, a;
const struct Color& operator = (const struct Color &val) {
this->r = val.r;
this->g = val.g;
this->b = val.b;
this->a = val.a;
return *this;
}
struct Color operator * (const float_t &x) {
return {
r * x,
g * x,
b * x,
a * x
};
}
struct Color operator - (const struct Color &color) {
return {
r - color.r,
g - color.g,
b - color.b,
a - color.a
};
}
struct Color operator + (const struct Color &color) {
return {
r + color.r,
g + color.g,
b + color.b,
a + color.a
};
}
const bool_t operator == (const struct Color &other) {
return r == other.r && g == other.g && b == other.b && a == other.a;
}
operator struct ColorU8() const {
return {
(uint8_t)(r * 255),
(uint8_t)(g * 255),
(uint8_t)(b * 255),
(uint8_t)(a * 255)
};
}
};
#define COLOR_DEF(r,g,b,a) { r, g, b, a }
#define COLOR_WHITE COLOR_DEF(1.0f, 1.0f, 1.0f, 1.0f)
#define COLOR_RED COLOR_DEF(1.0f, 0, 0, 1.0f)
#define COLOR_GREEN COLOR_DEF(0, 1.0f, 0, 1.0f)
#define COLOR_BLUE COLOR_DEF(0, 0, 1.0f, 1.0f)
#define COLOR_BLACK COLOR_DEF(0, 0, 0, 1.0f)
#define COLOR_MAGENTA COLOR_DEF(1.0f, 0, 1.0f, 1.0f)
#define COLOR_DARK_GREY COLOR_DEF(0.2f, 0.2f, 0.2f, 1.0f)
#define COLOR_LIGHT_GREY COLOR_DEF(0.8f, 0.8f, 0.8f, 1.0f)
#define COLOR_CORNFLOWER_BLUE COLOR_DEF(0.4f, 0.6f, 0.9f, 1.0f)
#define COLOR_WHITE_TRANSPARENT COLOR_DEF(1.0f, 1.0f, 1.0f, 0.0f)
#define COLOR_BLACK_TRANSPARENT COLOR_DEF(0.0f, 0.0f, 0.0f, 0.0f)
#define COLOR_YELLOW COLOR_DEF(1.0f, 1.0f, 0.0f, 1.0f)
#define COLOR_CYAN COLOR_DEF(0.0f, 1.0f, 1.0f, 1.0f)
#define COLOR_TRANSPARENT COLOR_WHITE_TRANSPARENT
}

View File

@ -1,14 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "IRenderHost.hpp"
using namespace Dawn;
IRenderHost::IRenderHost() : renderPipeline(), shaderManager() {
}
IRenderHost::~IRenderHost() {
}

View File

@ -1,61 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "display/RenderTarget.hpp"
#include "display/RenderPipeline.hpp"
#include "display/shader/ShaderManager.hpp"
namespace Dawn {
class Game;
class IRenderHost {
public:
RenderPipeline renderPipeline;
ShaderManager shaderManager;
/**
* Creates a render host.
*/
IRenderHost();
/**
* Initializes the render host, called by the game during the initial
* set up of the engine.
*
* @param game Game that requested the render host to initialize.
*/
virtual void init(const std::shared_ptr<Game> game) = 0;
/**
* Performs an update/tick of the render host. This would be the game
* asking the RenderHost to do the rendering.
*/
virtual void update(const std::shared_ptr<Game> game) = 0;
/**
* Overridable request from the game that asks if the RenderHost has any
* reason that it should need to close. For most libraries this would be
* whether or not the window was closed.
*
* @return True if the render host requests the game to gracefully exit.
*/
virtual bool_t isCloseRequested() = 0;
/**
* Returns the back buffer render target. This is the render target that
* is used to render to the screen.
*
* @return The back buffer render target.
*/
virtual std::shared_ptr<RenderTarget> getBackBufferRenderTarget() = 0;
/**
* Destroys the render host.
*/
virtual ~IRenderHost();
};
}

View File

@ -1,117 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/Color.hpp"
namespace Dawn {
enum class TextureFormat {
R = 1,
RG = 2,
RGB = 3,
RGBA = 4
};
enum class TextureWrapMode {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3
};
enum class TextureFilterMode {
NEAREST = 0,
LINEAR = 1
};
enum class TextureDataFormat {
UNSIGNED_BYTE = sizeof(uint8_t),
FLOAT = sizeof(float_t)
};
class ITexture {
public:
enum TextureWrapMode wrapModeX = TextureWrapMode::REPEAT;
enum TextureWrapMode wrapModeY = TextureWrapMode::REPEAT;
enum TextureFilterMode filterModeMin = TextureFilterMode::NEAREST;
enum TextureFilterMode filterModeMag = TextureFilterMode::NEAREST;
enum TextureFilterMode mipMapFilterModeMin = TextureFilterMode::NEAREST;
enum TextureFilterMode mipMapFilterModeMag = TextureFilterMode::NEAREST;
/**
* Returns the width of the texture.
*
* @return Width of the texture.
*/
virtual int32_t getWidth() = 0;
/**
* Returns the height of the texture.
*
* @return Height of the texture.
*/
virtual int32_t getHeight() = 0;
/**
* Initializes a texture.
*
* @param width Width of the texture (in pixels).
* @param height Height of the texture (in pixels).
* @param format Data format of the texture to use.
* @param dataFormat Data format of the texture to use.
*/
virtual void setSize(
const int32_t width,
const int32_t height,
const enum TextureFormat format,
const enum TextureDataFormat dataFormat
) = 0;
/**
* Returns true only when the texture has been loaded, sized and put on
* the gpu for rendering.
*
* @return True if ready, otherwise false.
*/
virtual bool_t isReady() = 0;
/**
* Buffer pixel data onto the GPU. Pixel buffering is rather costly so
* avoid doing this too often.
*
* @param pixels Array of pixels you're trying to buffer.
*/
virtual void buffer(const struct ColorU8 pixels[]) = 0;
/**
* Buffer pixel data onto the GPU. Pixel buffering is rather costly so
* avoid doing this too often.
*
* @param pixels Array of pixels you're trying to buffer.
*/
virtual void buffer(const struct Color pixels[]) = 0;
/**
* Buffer pixel data onto the GPU. Pixel buffering is rather costly so
* avoid doing this too often.
*
* @param pixels Array of pixels you're trying to buffer.
*/
virtual void buffer(const uint8_t pixels[]) = 0;
/**
* Binds the texture to the given slot (for use by the shaders).
*
* @param slot Slot to bind to.
*/
virtual void bind(const uint8_t slot) = 0;
/**
* Disposes of the texture.
*/
virtual ~ITexture() {
}
};
}

View File

@ -1,105 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "assert/assert.hpp"
#include "RenderPipeline.hpp"
#include "game/Game.hpp"
#include "scene/Scene.hpp"
#include "component/display/Camera.hpp"
#include "component/display/IRenderableComponent.hpp"
using namespace Dawn;
RenderPipeline::RenderPipeline() {
}
void RenderPipeline::render(
const std::shared_ptr<Game> game
) {
assertNotNull(game, "Game cannot be null");
auto scene = game->getCurrentScene();
if(!scene) return;
this->renderScene(game, scene);
}
void RenderPipeline::renderScene(
const std::shared_ptr<Game> game,
const std::shared_ptr<Scene> scene
) {
assertNotNull(game, "Game cannot be null");
assertNotNull(scene, "Scene cannot be null");
// TODO: Render Subscenes First
// Get a list of all cameras in the scene
auto cameras = scene->findComponents<Camera>();
auto backBuffer = scene->getGame()->renderHost.getBackBufferRenderTarget();
std::shared_ptr<Camera> backbufferCamera = nullptr;
for(auto camera : cameras) {
auto rt = camera->getRenderTarget();
// Is this camera the backbuffer camera?
if(rt == backBuffer) {
backbufferCamera = camera;
continue;
}
// Render scene with this camera
renderSceneCamera(game, scene, camera, rt);
}
if(backbufferCamera) {
// Render the backbuffer camera
renderSceneCamera(game, scene, backbufferCamera, backBuffer);
}
}
void RenderPipeline::renderSceneCamera(
const std::shared_ptr<Game> game,
const std::shared_ptr<Scene> scene,
const std::shared_ptr<Camera> camera,
const std::shared_ptr<RenderTarget> renderTarget
) {
assertNotNull(game, "Game cannot be null");
assertNotNull(scene, "Scene cannot be null");
assertNotNull(camera, "Camera cannot be null");
assertNotNull(renderTarget, "RenderTarget cannot be null");
struct RenderPassContext ctx = {
game,
scene,
camera,
renderTarget
};
// Get list of renderables
std::vector<std::shared_ptr<IRenderPass>> renderPasses;
auto renderables = scene->findComponents<IRenderableComponent>();
for(auto renderable : renderables) {
auto rp = renderable->getPasses(ctx);
renderPasses.insert(renderPasses.end(), rp.begin(), rp.end());
}
// TODO: Make clearing the buffers editable!
renderTarget->bind();
renderTarget->clear(
RENDER_TARGET_CLEAR_COLOR |
RENDER_TARGET_CLEAR_DEPTH
);
for(auto renderPass : renderPasses) {
renderPass->bind();
renderPass->setData();
renderPass->upload();
renderPass->draw();
}
}
RenderPipeline::~RenderPipeline() {
}

View File

@ -1,63 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class Game;
class Scene;
class Camera;
class RenderTarget;
class RenderPipeline {
public:
/**
* Creates a new RenderPipeline.
*/
RenderPipeline();
/**
* Renders the game. This will render the current scene.
*
* @param game Game to render.
*/
void render(
const std::shared_ptr<Game> game
);
/**
* Renders a specific scene. This will render all cameras within the
* scene.
*
* @param game Game to render.
* @param scene Scene to render.
*/
void renderScene(
const std::shared_ptr<Game> game,
const std::shared_ptr<Scene> scene
);
/**
* Renders a specific scene with a specific camera.
*
* @param game Game to render.
* @param scene Scene to render.
* @param camera Camera to render.
* @param renderTarget Render target to render to.
*/
void renderSceneCamera(
const std::shared_ptr<Game> game,
const std::shared_ptr<Scene> scene,
const std::shared_ptr<Camera> camera,
const std::shared_ptr<RenderTarget> renderTarget
);
/**
* Destroys the RenderPipeline.
*/
virtual ~RenderPipeline();
};
}

View File

@ -1,71 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "event/Event.hpp"
#define RENDER_TARGET_CLEAR_COLOR (1 << 0)
#define RENDER_TARGET_CLEAR_DEPTH (1 << 1)
namespace Dawn {
class RenderTarget {
public:
Event<float_t, float_t> onResize;
/**
* Return the width of the render target.
*
* @return The width of the render target.
*/
virtual float_t getWidth() = 0;
/**
* Return the height of the render target.
*
* @return The height of the render target.
*/
virtual float_t getHeight() = 0;
/**
* Returns the scale (as in pixel density) of the render target. This is
* typically 1.0f, but on high DPI displays this may be 2.0f or higher.
*
* @return The scale of the render target.
*/
virtual float_t getScale() = 0;
/**
* Sets the clear color of the render target when the clear method for
* the color buffer is requested.
*
* @param color Color to use for the clear operation.
*/
virtual void setClearColor(const struct Color color) = 0;
/**
* Request the existing data in the render target to be cleared out. We
* typically assume the render target can support multiple buffer types,
* so you can opt to only clear certain buffer types.
*
* @param clearFlags Flags to request what is going to be cleared.
*/
virtual void clear(const int32_t clearFlags) = 0;
/**
* Bind the render target for rendering to. The proceeding render requests
* will want to render to this render target directly. In future I may
* see if we can have multiple render targets bound at once to make this
* operation perform faster.
*/
virtual void bind() = 0;
/**
* Destroys the render target.
*/
virtual ~RenderTarget() {
}
};
}

18
src/dawn/display/color.h Normal file
View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
#define COLOR_BLACK 0x00
#define COLOR_WHITE 0x01
#define COLOR_RED 0x02
#define COLOR_GREEN 0x03
#define COLOR_BLUE 0x04
#define COLOR_YELLOW 0x05
#define COLOR_MAGENTA 0x06
#define COLOR_CYAN 0x07

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
/**
* Initializes the display subsystem.
*/
void displayInit();
/**
* Updates the display subsystem, this is basically asking the display renderer
* to perform a render.
*/
void displayUpdate();
/**
* Cleans up the display subsystem.
*/
void displayDispose();

View File

@ -1,9 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
TrueTypeTexture.cpp
)

View File

@ -1,16 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
struct TrueTypeCharacter {
glm::vec2 advance;
glm::vec2 size;
glm::vec2 offset;
glm::vec4 quad;
};
}

View File

@ -1,198 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TrueTypeTexture.hpp"
#include "assert/assert.hpp"
#include "util/Math.hpp"
#include "display/mesh/QuadMesh.hpp"
using namespace Dawn;
TrueTypeTexture::TrueTypeTexture(const uint32_t fontSize) :
fontSize(fontSize)
{
assertTrue(fontSize > 0, "Font size cannot be zero");
texture = std::make_shared<Texture>();
}
void TrueTypeTexture::setFace(const FT_Face face) {
this->face = face;
assertTrue(fontSize < 256, "Font size cannot be greater than 256");
// Set freetype font size prior to baking.
auto ret = FT_Set_Pixel_Sizes(face, 0, fontSize);
if(ret != 0) {
assertUnreachable("Failed to set font size %i", ret);
}
// Set the texture size
texture->setSize(
fontSize * 24,
fontSize * 24,
TextureFormat::R,
TextureDataFormat::UNSIGNED_BYTE
);
// Texture buffer
uint8_t *buffer = new uint8_t[texture->getWidth() * texture->getHeight()];
// Fill with zeros
std::memset(buffer, 0, texture->getWidth() * texture->getHeight());
size_t offset = 0;
struct TrueTypeCharacter info;
int32_t textureX = 0, textureY = 0;
int32_t rowHeight = 0;
// Character sets
std::vector<wchar_t> characterBlocks;
// Latin
for(wchar_t c = 0x0020; c < 0x007F; c++) characterBlocks.push_back(c);
// Latin-1 Supplement
for(wchar_t c = 0x00A0; c < 0x00FF; c++) characterBlocks.push_back(c);
// Latin Extended-A
for(wchar_t c = 0x0100; c < 0x017F; c++) characterBlocks.push_back(c);
// Latin Extended-B
for(wchar_t c = 0x0180; c < 0x024F; c++) characterBlocks.push_back(c);
// Hiragana
for(wchar_t c = 0x3040; c < 0x309F; c++) characterBlocks.push_back(c);
// Katakana
for(wchar_t c = 0x30A0; c < 0x30FF; c++) characterBlocks.push_back(c);
// For each character in the character set
for(wchar_t c : characterBlocks) {
// Load the character
if(FT_Load_Char(face, c, FT_LOAD_RENDER)) {
assertUnreachable("Failed to load character (1)");
}
// Store the character information
info.advance.x = (float_t)(face->glyph->advance.x >> 6);
info.advance.y = (float_t)(face->glyph->advance.y >> 6);
info.size = glm::vec2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
// Determine the texture position
if(textureX + face->glyph->bitmap.width >= texture->getWidth()) {
textureX = 0;
textureY += rowHeight + 2;// Tiny gap between rows
rowHeight = face->glyph->bitmap.rows;
} else {
rowHeight = Math::max<int32_t>(rowHeight, face->glyph->bitmap.rows);
}
// Set the quad positions
info.offset = glm::vec2(
face->glyph->bitmap_left,
-face->glyph->bitmap_top
);
info.quad = glm::vec4(
textureX,
textureY,
textureX + face->glyph->bitmap.width,
textureY + face->glyph->bitmap.rows
) / glm::vec4(
texture->getWidth(),
texture->getHeight(),
texture->getWidth(),
texture->getHeight()
);
// Store the cached character data.
this->characterData[c] = info;
// Determine pixel offset.
offset = textureX + (textureY * texture->getWidth());
assertTrue(
offset + (face->glyph->bitmap.rows * texture->getWidth()) <=
texture->getWidth() * texture->getHeight(),
"Font texture buffer overflow will occur."
);
// Buffer pixels, we have to do this one row at a time due to the
// differences in width between the glyph and the texture.
const size_t countPerRow = face->glyph->bitmap.width;
int32_t i = 0;
while(i != face->glyph->bitmap.rows) {
std::memcpy(
buffer + offset + (i * texture->getWidth()),
face->glyph->bitmap.buffer + (i * countPerRow),
countPerRow
);
i++;
}
// Increment textureX
textureX += face->glyph->bitmap.width + 2;// I add a tiny gap between chars
}
this->texture->buffer(buffer);
delete[] buffer;
}
struct TrueTypeCharacter TrueTypeTexture::getCharacterData(wchar_t c) {
return this->characterData[c];
}
glm::vec2 TrueTypeTexture::bufferStringToMesh(
std::shared_ptr<Mesh> mesh,
const std::wstring text,
glm::vec2 &position,
bool_t flipY
) {
assertNotNull(mesh, "Mesh must be supplied and not null");
assertTrue(text.size() > 0, "Text must be at least one character long.");
// Create mesh buffers
mesh->createBuffers(
text.length() * QUAD_VERTICE_COUNT,
text.length() * QUAD_INDICE_COUNT
);
// Foreach char
size_t i = 0;
glm::vec2 size = { 0, 0 };
for(wchar_t c : text) {
// Get the character data
auto info = this->getCharacterData(c);
// Buffer the quad
glm::vec4 quad = glm::vec4(
position.x,
position.y,
position.x + info.size.x,
position.y + info.size.y
);
if(flipY) {
QuadMesh::buffer(
mesh,
quad,
glm::vec4(
info.quad.x,
info.quad.w,
info.quad.z,
info.quad.y
),
i * QUAD_VERTICE_COUNT,
i * QUAD_INDICE_COUNT
);
} else {
QuadMesh::buffer(
mesh,
quad,
info.quad,
i * QUAD_VERTICE_COUNT,
i * QUAD_INDICE_COUNT
);
}
position += info.advance;
size += info.advance;
i++;
}
return size;
}
TrueTypeTexture::~TrueTypeTexture() {
}

View File

@ -1,66 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/Texture.hpp"
#include "TrueTypeCharacter.hpp"
#include "display/mesh/Mesh.hpp"
#include <ft2build.h>
#include FT_FREETYPE_H
namespace Dawn {
class TrueTypeTexture final {
private:
FT_Face face;
public:
uint32_t fontSize;
std::shared_ptr<Texture> texture;
std::unordered_map<wchar_t, struct TrueTypeCharacter> characterData;
/**
* Construct a new New True Type Face Texture object
*
* @param fontSize Size of the font.
*/
TrueTypeTexture(const uint32_t fontSize);
/**
* Sets the face for this texture.
*
* @param face Face to set.
*/
void setFace(const FT_Face face);
/**
* Returns the character data for the given character.
*
* @param c Character to get data for.
* @return The Character data for the given character.
*/
struct TrueTypeCharacter getCharacterData(wchar_t c);
/**
* Buffers a string to the given mesh.
*
* @param mesh Mesh to buffer to.
* @param text Text to buffer.
* @param position Position to buffer to.
* @param flipY Whether or not to flip the Y axis.
* @return The size of the string.
*/
glm::vec2 bufferStringToMesh(
std::shared_ptr<Mesh> mesh,
const std::wstring text,
glm::vec2 &position,
bool_t flipY = false
);
/**
* Destroys this true type face texture.
*/
~TrueTypeTexture();
};
}

View File

@ -1,11 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
CubeMesh.cpp
QuadMesh.cpp
)

View File

@ -1,70 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "CubeMesh.hpp"
using namespace Dawn;
void CubeMesh::buffer(
const std::shared_ptr<Mesh> mesh,
const glm::vec3 pos,
const glm::vec3 size,
const int32_t verticeStart,
const int32_t indiceStart
) {
glm::vec3 positions[CUBE_VERTICE_COUNT] = {
pos,
glm::vec3(pos.x+size.x, pos.y, pos.z),
glm::vec3(pos.x, pos.y+size.y, pos.z),
glm::vec3(pos.x+size.x, pos.y+size.y, pos.z),
glm::vec3(pos.x, pos.y, pos.z+size.z),
glm::vec3(pos.x+size.x, pos.y, pos.z+size.z),
glm::vec3(pos.x, pos.y+size.y, pos.z+size.z),
pos + size
};
glm::vec2 coordinates[CUBE_VERTICE_COUNT] = {
glm::vec2(0, 0),
glm::vec2(1, 0),
glm::vec2(0, 1),
glm::vec2(1, 1),
glm::vec2(0, 0),
glm::vec2(1, 0),
glm::vec2(0, 1),
glm::vec2(1, 1)
};
int32_t indices[CUBE_INDICE_COUNT] = {
// Back
verticeStart, verticeStart + 1, verticeStart + 3,
verticeStart, verticeStart + 2, verticeStart + 3,
// Right
verticeStart + 1, verticeStart + 5, verticeStart + 7,
verticeStart + 1, verticeStart + 3, verticeStart + 7,
// Left
verticeStart + 4, verticeStart, verticeStart + 2,
verticeStart + 4, verticeStart + 6, verticeStart + 2,
// Front
verticeStart + 5, verticeStart + 4, verticeStart + 6,
verticeStart + 5, verticeStart + 7, verticeStart + 6,
// Top
verticeStart + 7, verticeStart + 2, verticeStart + 6,
verticeStart + 7, verticeStart + 3, verticeStart + 2,
// Bottom
verticeStart + 1, verticeStart, verticeStart + 4,
verticeStart + 1, verticeStart + 4, verticeStart + 5
};
mesh->bufferPositions(verticeStart, positions, CUBE_VERTICE_COUNT);
mesh->bufferCoordinates(verticeStart, coordinates, CUBE_VERTICE_COUNT);
mesh->bufferIndices(indiceStart, indices, CUBE_INDICE_COUNT);
}

View File

@ -1,30 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
#define CUBE_VERTICE_COUNT 8
#define CUBE_INDICE_COUNT 36
namespace Dawn {
class CubeMesh {
public:
/**
* Buffers cube mesh vertices and indices into the given mesh.
*
* @param size The size of the cube.
* @param verticeStart The starting index of the vertices.
* @param indiceStart The starting index of the indices.
*/
static void buffer(
const std::shared_ptr<Mesh> mesh,
const glm::vec3 pos,
const glm::vec3 size,
const int32_t verticeStart,
const int32_t indiceStart
);
};
}

View File

@ -1,102 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
enum MeshDrawMode {
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN,
LINES,
POINTS
// LINE_STRIP,
};
class IMesh {
protected:
/** How many vertices are in the mesh */
int32_t verticeCount = -1;
/** How many indices are in the mesh */
int32_t indiceCount = -1;
public:
/**
* Create a new set of buffers for the mesh to use.
*
* @param verticeCount How many Vertices will this buffer support.
* @param indiceCount How many Indices will this buffer support.
*/
virtual void createBuffers(
const int32_t verticeCount,
const int32_t indiceCount
) = 0;
/**
* Cleanup the buffers on a given mesh. This is useful if you intend to
* expand the count of vertices your mesh supports.
*/
virtual void disposeBuffers() = 0;
/**
* Write vertice positions to the mesh.
*
* @param pos Position, within the buffer, to write to.
* @param vertices Array of positions to write.
* @param len How many positions are in the array.
*/
virtual void bufferPositions(
const int32_t pos,
const glm::vec3 positions[],
const int32_t len
) = 0;
/**
* Write vertice coordinates to the mesh.
*
* @param pos Position, within the buffer, to write to.
* @param coordinates Array of coordinates to write.
* @param len How many coordinates are in the array.
*/
virtual void bufferCoordinates(
const int32_t pos,
const glm::vec2 coordinates[],
const int32_t len
) = 0;
/**
* Write indices to the mesh.
*
* @param pos Position, within the buffer, to write to.
* @param indices Array of indices to write.
* @param len How many indices are in the array.
*/
virtual void bufferIndices(
const int32_t pos,
const int32_t indices[],
const int32_t len
) = 0;
/**
* Draw a primitive. Primitives are drawn by their indices.
*
* @param drawMode Which drawing mode to use to draw the primitive.
* @param start Start indice (index) to draw.
* @param count Count of indices to draw. Use -1 to draw all.
*/
virtual void draw(
const enum MeshDrawMode drawMode,
const int32_t start,
const int32_t count
) = 0;
/**
* Cleanup a previously initiated mesh.
*/
virtual ~IMesh() {
}
};
}

View File

@ -1,72 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "QuadMesh.hpp"
using namespace Dawn;
void QuadMesh::buffer(
const std::shared_ptr<Mesh> mesh,
const glm::vec4 positions,
const glm::vec4 coordinates,
const int32_t verticeStart,
const int32_t indiceStart,
const float_t depth
) {
glm::vec3 vertices[QUAD_VERTICE_COUNT] = {
glm::vec3(positions.x, positions.y, depth),
glm::vec3(positions.z, positions.y, depth),
glm::vec3(positions.x, positions.w, depth),
glm::vec3(positions.z, positions.w, depth)
};
glm::vec2 coords[QUAD_VERTICE_COUNT] = {
glm::vec2(coordinates.x, coordinates.y),
glm::vec2(coordinates.z, coordinates.y),
glm::vec2(coordinates.x, coordinates.w),
glm::vec2(coordinates.z, coordinates.w)
};
int32_t indices[QUAD_INDICE_COUNT] = {
verticeStart, verticeStart + 1, verticeStart + 3,
verticeStart, verticeStart + 2, verticeStart + 3
};
mesh->bufferPositions(verticeStart, vertices, QUAD_VERTICE_COUNT);
mesh->bufferCoordinates(verticeStart, coords, QUAD_VERTICE_COUNT);
mesh->bufferIndices(indiceStart, indices, QUAD_INDICE_COUNT);
}
void QuadMesh::bufferWithIndex(
const std::shared_ptr<Mesh> mesh,
const glm::vec4 positions,
const glm::vec4 coordinates,
const int32_t verticeStart,
const int32_t indiceStart,
const int32_t indexOffset
) {
glm::vec3 vertices[QUAD_VERTICE_COUNT] = {
glm::vec3(positions.x, positions.y, indexOffset),
glm::vec3(positions.z, positions.y, indexOffset + 1),
glm::vec3(positions.x, positions.w, indexOffset + 2),
glm::vec3(positions.z, positions.w, indexOffset + 3)
};
glm::vec2 coords[QUAD_VERTICE_COUNT] = {
glm::vec2(coordinates.x, coordinates.y),
glm::vec2(coordinates.z, coordinates.y),
glm::vec2(coordinates.x, coordinates.w),
glm::vec2(coordinates.z, coordinates.w)
};
int32_t indices[QUAD_INDICE_COUNT] = {
verticeStart, verticeStart + 1, verticeStart + 3,
verticeStart, verticeStart + 2, verticeStart + 3
};
mesh->bufferPositions(verticeStart, vertices, QUAD_VERTICE_COUNT);
mesh->bufferCoordinates(verticeStart, coords, QUAD_VERTICE_COUNT);
mesh->bufferIndices(indiceStart, indices, QUAD_INDICE_COUNT);
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
#define QUAD_VERTICE_COUNT 4
#define QUAD_INDICE_COUNT 6
namespace Dawn {
class QuadMesh {
public:
/**
* Buffers quad mesh vertices and indices into the given mesh.
*
* @param mesh The mesh to buffer into.
* @param positions The positions of the vertices.
* @param coordinates The coordinates of the vertices.
* @param verticeStart The starting index of the vertices.
* @param indiceStart The starting index of the indices.
* @param depth The depth of the vertices (Z coordinate).
*/
static void buffer(
const std::shared_ptr<Mesh> mesh,
const glm::vec4 positions,
const glm::vec4 coordinates,
const int32_t verticeStart,
const int32_t indiceStart,
const float_t depth = 0.0f
);
/**
* Buffers quad mesh vertices and indices into the given mesh. This will
* store the index of the vertice in the Z component, allowing you to find
* which vertex ID you are rendering in your shader.
*
* @param mesh The mesh to buffer into.
* @param positions The positions of the vertices.
* @param coordinates The coordinates of the vertices.
* @param verticeStart The starting index of the vertices.
* @param indiceStart The starting index of the indices.
* @param indexOffset The offset to add to the index of each vertex.
*/
static void bufferWithIndex(
const std::shared_ptr<Mesh> mesh,
const glm::vec4 positions,
const glm::vec4 coordinates,
const int32_t verticeStart,
const int32_t indiceStart,
const int32_t indexOffset = 0
);
};
}

View File

@ -1,40 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
namespace Dawn {
class IRenderPass {
public:
std::shared_ptr<Mesh> mesh;
/**
* Binds the shader for this render pass.
*/
virtual void bind() = 0;
/**
* Sets the data for this render pass to the shader.
*/
virtual void setData() = 0;
/**
* Uploads the data to the GPU.
*/
virtual void upload() = 0;
/**
* Draws the mesh for this render pass.
*/
virtual void draw() = 0;
/**
* Cleans up the render pass.
*/
virtual ~IRenderPass() {
}
};
}

View File

@ -1,94 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "game/Game.hpp"
#include "display/pass/IRenderPass.hpp"
#include "display/shader/Shader.hpp"
#include "display/Texture.hpp"
#include "component/display/MeshRenderer.hpp"
namespace Dawn {
template<class S, typename D>
class RenderPass : public IRenderPass {
private:
std::shared_ptr<S> shader;
const std::unordered_map<
shadertexturebinding_t, std::shared_ptr<Texture>
> textures;
std::shared_ptr<Mesh> mesh;
const enum MeshDrawMode drawMode;
const int32_t indiceStart;
const int32_t indiceCount;
const D data;
public:
/**
* Constructs a new RenderPass.
*
* @param self Self component instance that is creating this render pass.
* @param d The data to use for this render pass.
* @param mesh The mesh to use for this render pass.
* @param drawMode The draw mode to use for this render pass.
* @param indiceStart The indice to start drawing from.
* @param indiceCount The number of indices to draw.
*/
RenderPass(
SceneComponent &self,
const D d,
const std::unordered_map<
shadertexturebinding_t, std::shared_ptr<Texture>
> textures,
const std::shared_ptr<Mesh> mesh,
const enum MeshDrawMode drawMode,
const int32_t indiceStart,
const int32_t indiceCount
) :
data(d),
textures(textures),
mesh(mesh),
drawMode(drawMode),
indiceStart(indiceStart),
indiceCount(indiceCount)
{
//Get the shader
shader = (
self.getGame()->renderHost.shaderManager.getShader<S>()
);
assertNotNull(shader, "Shader cannot be null!");
// Need mesh?
if(!this->mesh) {
auto meshRenderer = self.getItem()->getComponent<MeshRenderer>();
if(meshRenderer) this->mesh = meshRenderer->mesh;
}
}
void bind() override {
shader->bind();
}
void setData() override {
shader->setData(data);
}
void upload() override {
for(auto &pair : textures) {
if(!pair.second->isReady()) continue;
pair.second->bind(pair.first);
}
shader->upload();
}
void draw() override {
if(mesh) {
mesh->draw(drawMode, indiceStart, indiceCount);
}
}
~RenderPass() override {
}
};
}

View File

@ -1,18 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "game/Game.hpp"
#include "scene/Scene.hpp"
#include "component/display/Camera.hpp"
namespace Dawn {
struct RenderPassContext {
std::shared_ptr<Game> game;
std::shared_ptr<Scene> scene;
std::shared_ptr<Camera> camera;
std::shared_ptr<RenderTarget> renderTarget;
};
}

View File

@ -1,11 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
IShader.cpp
IShaderStage.cpp
)

View File

@ -1,46 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "display/shader/Shader.hpp"
#include "assert/assert.hpp"
#include "display/Color.hpp"
#include "display/Texture.hpp"
using namespace Dawn;
size_t shaderParameterTypeGetSize(const enum ShaderParameterType type) {
switch(type) {
case ShaderParameterType::VEC2:
return sizeof(glm::vec2);
case ShaderParameterType::VEC3:
return sizeof(glm::vec3);
case ShaderParameterType::VEC4:
return sizeof(glm::vec4);
case ShaderParameterType::MAT3:
return sizeof(glm::mat3);
case ShaderParameterType::MAT4:
return sizeof(glm::mat4);
case ShaderParameterType::COLOR:
return sizeof(struct Color);
case ShaderParameterType::FLOAT:
return sizeof(float);
case ShaderParameterType::INT:
return sizeof(int32_t);
case ShaderParameterType::TEXTURE:
return sizeof(shadertexturebinding_t);
default:
assertUnreachable("Unknown ShaderParameterType");
return 0;
}
}

View File

@ -1,86 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
enum class ShaderParameterType {
VEC2,
VEC3,
VEC4,
MAT3,
MAT4,
COLOR,
FLOAT,
INT,
TEXTURE,
BOOLEAN
};
class IShaderBase {
public:
virtual ~IShaderBase() {
}
};
template<typename T>
class IShader : public IShaderBase {
protected:
T data;
public:
/**
* Returns the currently uploaded data on the Shader.
*
* @return The uploaded data.
*/
T getData() {
return data;
}
/**
* Sets the entire data to be uploaded.
*
* @param data Data to be uploaded.
*/
void setData(const T data) {
this->data = data;
}
/**
* Initializes the shader, this needs to be called before the shader can
* be used.
*/
virtual void init() = 0;
/**
* Binds the shader as the current one, does not upload any data, somewhat
* relies on something else uploading the data.
*/
virtual void bind() = 0;
/**
* Uploads the data to the GPU.
*/
virtual void upload() = 0;
/**
* Disposes of the shader.
*/
virtual ~IShader() {
}
};
}
/**
* Returns the size of the ShaderParameterType.
*
* @param type The type to get the size of.
* @return Size of the type.
*/
size_t shaderParameterTypeGetSize(const enum Dawn::ShaderParameterType type);

View File

@ -1,18 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "IShaderStage.hpp"
using namespace Dawn;
IShaderStage::IShaderStage(const enum ShaderStageType type) :
type(type)
{
}
IShaderStage::~IShaderStage() {
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
enum class ShaderStageType {
VERTEX,
FRAGMENT,
// COMPUTE
};
class IShaderStage {
public:
const enum ShaderStageType type;
/**
* Constructs a new Shader Stage.
*
* @param type Type of shader stage.
*/
IShaderStage(const enum ShaderStageType type);
/**
* Destroy the IShaderStage object
*/
virtual ~IShaderStage();
};
}

View File

@ -1,45 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/Shader.hpp"
namespace Dawn {
class ShaderManager {
private:
std::vector<std::shared_ptr<IShaderBase>> shaders;
public:
/**
* Retreives an instance of the shader from the shader manager. If the
* shader does not exist it will be created.
*
* @tparam T Type of shader to retreive.
* @return Shader instance.
*/
template<class T>
std::shared_ptr<T> getShader() {
auto itShaders = shaders.begin();
while(itShaders != shaders.end()) {
// auto shader = itShaders->lock();
// if(!shader) {
// itShaders = shaders.erase(itShaders);
// continue;
// }
// std::shared_ptr<T> casted = std::dynamic_pointer_cast<T>(shader);
auto shader = *itShaders;
std::shared_ptr<T> casted = std::dynamic_pointer_cast<T>(shader);
if(casted) return casted;
itShaders++;
}
auto newShader = std::make_shared<T>();
shaders.push_back(newShader);
newShader->init();
return newShader;
}
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Environment.cpp
)

View File

@ -1,31 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Environment.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
Environment environment;
bool_t Environment::hasVariable(const std::string &key) {
assertTrue(key.length() > 0, "Key must be at least 1 character long.");
return this->variables.find(key) != this->variables.end();
}
void Environment::setVariable(
const std::string &key,
const std::string &value
) {
assertTrue(key.length() > 0, "Key must be at least 1 character long.");
this->variables[key] = value;
}
std::string Environment::getVariable(const std::string &key) {
assertTrue(key.length() > 0, "Key must be at least 1 character long.");
assertTrue(this->hasVariable(key), "Variable does not exist.");
return this->variables[key];
}

View File

@ -1,41 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class Environment {
private:
std::map<std::string, std::string> variables;
public:
/**
* Checks if the environment has a variable.
*
* @param key Variable key to check.
* @return True if the variable exists, false otherwise.
*/
bool_t hasVariable(const std::string &key);
/**
* Sets a variable in the environment.
*
* @param key Variable key to set.
* @param value Variable value to set.
*/
void setVariable(const std::string &key, const std::string &value);
/**
* Gets a variable from the environment.
*
* @param key Variable key to get.
* @return Variable value, or empty string if not found.
*/
std::string getVariable(const std::string &key);
};
}
extern Dawn::Environment environment;

View File

@ -1,141 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
enum class CustomEventResult {
NOTHING,
REMOVE,
INVOKE,
INVOKE_AND_REMOVE
};
template<
typename InternalData,
typename InternalListenerData,
typename ListenerArgument,
typename ...InvokeArgs
>
class CustomEvent {
private:
int32_t nextId = 0;
std::unordered_map<
int32_t,
std::pair<InternalListenerData, std::function<void(InvokeArgs...)>>
> listeners;
protected:
InternalData internalData;
/**
* Custom event filter. Decides whether or not the event should be emitted
* to the listener.
*
* @param listenerData Data for this listener.
* @param args The arguments to pass to the listeners.
* @return The result of the filter.
*/
virtual enum CustomEventResult shouldEmit(
const InternalListenerData &listenerData,
const InvokeArgs... args
) = 0;
/**
* Transform the arguments for listener data when the listener is first
* subscribed.
*
* @param argument The argument to transform.
* @return The transformed argument into an internal data format.
*/
virtual InternalListenerData transformData(
const ListenerArgument &argument
) = 0;
/**
* Transform the data for listener data after the event has been emitted.
*
* @param internalData The internal data to transform.
* @return Updated/Transformed internal data.
*/
virtual InternalListenerData transformDataAfterEmit(
const InternalListenerData &internalData
) {
return internalData;
}
public:
/**
* Emits the event.
* @param args The arguments to pass to the listeners.
*/
void emit(InvokeArgs... args) {
auto copy = listeners;
for(auto &pair : copy) {
// Check emit test.
auto result = this->shouldEmit(
pair.second.first,
args...
);
if(
result == CustomEventResult::INVOKE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
pair.second.second(args...);
}
if(
result == CustomEventResult::REMOVE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
listeners.erase(pair.first);
continue;
}
if(
result == CustomEventResult::INVOKE ||
result == CustomEventResult::INVOKE_AND_REMOVE
) {
// Update the internal data.
listeners[pair.first].first = transformDataAfterEmit(
pair.second.first
);
}
}
}
/**
* Listens to the event.
*
* @param data Listener data to use.
* @param listener The listener to add.
* @returns A function that can be called to remove the listener.
*/
std::function<void()> listen(
const ListenerArgument &data,
const std::function<void(InvokeArgs...)> listener
) {
int32_t id = nextId++;
auto pair = std::make_pair(
transformData(data),
listener
);
listeners[id] = pair;
return [this, id]() {
listeners.erase(id);
};
}
/**
* Destroys the custom event.
*/
virtual ~CustomEvent() {
listeners.clear();
}
};
}

View File

@ -1,57 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
template<typename ...A>
class Event {
private:
int32_t nextId = 0;
std::map<int32_t, std::function<void(A...)>> listeners;
public:
/**
* Constructs a new Event.
*/
Event() {
}
/**
* Emits the event.
* @param args The arguments to pass to the listeners.
*/
void emit(A ...args) {
auto copy = listeners;
for(auto &pair : copy) {
pair.second(args...);
}
}
/**
* Listens to the event.
* @param listener The listener to add.
* @returns A function that can be called to remove the listener.
*/
std::function<void()> listen(
const std::function<void(A...)> listener
) {
int32_t id = nextId++;
listeners[id] = listener;
return [this, id]() {
listeners.erase(id);
};
}
/**
* Destroys the event.
*/
virtual ~Event() {
listeners.clear();
}
};
}

48
src/dawn/game.c Normal file
View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game.h"
#include "time.h"
#include "input.h"
#include "display/display.h"
#include "rpg/world/maps/testmap.h"
map_t MAP;
game_t GAME;
void gameInit() {
memset(&GAME, 0, sizeof(game_t));
timeInit();
inputInit();
displayInit();
testMapInit(&MAP);
gameSetMap(&MAP);
}
uint8_t gameUpdate(const float_t delta) {
timeUpdate(delta);
inputUpdate();
if(GAME.currentMap) {
mapUpdate(GAME.currentMap);
}
displayUpdate();
if(inputIsDown(INPUT_BIND_CANCEL)) return GAME_UPDATE_RESULT_EXIT;
return GAME_UPDATE_RESULT_CONTINUE;
}
void gameSetMap(map_t *map) {
GAME.currentMap = map;
}
void gameDispose() {
displayDispose();
}

43
src/dawn/game.h Normal file
View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/world/map.h"
#define GAME_UPDATE_RESULT_CONTINUE 0
#define GAME_UPDATE_RESULT_EXIT 1
typedef struct {
map_t *currentMap;
} game_t;
extern game_t GAME;
/**
* Initializes the game state.
*/
void gameInit();
/**
* Updates the game state.
*
* @param delta Time since last update.
* @return Game update result, 0 for continue, 1 for exit, else for failure.
*/
uint8_t gameUpdate(const float_t delta);
/**
* Sets the current map, does not take ownership.
*
* @param map Map to set.
*/
void gameSetMap(map_t *map);
/**
* Cleans up the game state.
*/
void gameDispose();

View File

@ -1,9 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Game.cpp
)

View File

@ -1,56 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "game/Game.hpp"
#include "game/GameInit.hpp"
#include "scene/Scene.hpp"
using namespace Dawn;
Game::Game() {
}
void Game::init() {
renderHost.init(shared_from_this());
inputManager.init(shared_from_this());
saveManager.init(shared_from_this());
auto initialScene = Scene::getInitialScene();
this->nextFrameScene = std::make_shared<Scene>(shared_from_this(), initialScene);
}
void Game::update() {
this->assetManager.update();
if(nextFrameScene) {
nextFrameScene->stage();
currentScene = nextFrameScene;
nextFrameScene = nullptr;
}
timeManager.update();
inputManager.update();
if(currentScene) currentScene->update();
renderHost.update(shared_from_this());
}
bool_t Game::isCloseRequested() {
return (
renderHost.isCloseRequested()
);
}
std::shared_ptr<Scene> Game::getCurrentScene() {
return currentScene;
}
Game::~Game() {
currentScene = nullptr;
nextFrameScene = nullptr;
assertTrue(SCENE_ITEMS_ACTIVE == 0, "Some scene items are still active?");
assertTrue(SCENE_COMPONENTS_ACTIVE == 0, "Some scene components are still active?");
}

View File

@ -1,67 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "display/RenderHost.hpp"
#include "input/InputManager.hpp"
#include "time/TimeManager.hpp"
#include "asset/AssetManager.hpp"
#include "locale/LocaleManager.hpp"
#include "save/SaveManager.hpp"
namespace Dawn {
class Scene;
class Game : public std::enable_shared_from_this<Game> {
private:
std::shared_ptr<Scene> currentScene = nullptr;
std::shared_ptr<Scene> nextFrameScene = nullptr;
public:
RenderHost renderHost;
InputManager inputManager;
TimeManager timeManager;
AssetManager assetManager;
LocaleManager localeManager;
SaveManager saveManager;
/**
* Constructs the game instance, does not initialize anything.
*/
Game();
/**
* Initialize the game and all of its components.
*/
void init();
/**
* Performs a single update tick on the game engine, and in turn all of
* the game's sub systems.
*/
void update();
/**
* Returns whether the game has been requested to gracefully close at the
* next available opportunity.
*
* @return True if the game should close.
*/
bool_t isCloseRequested();
/**
* Returns the current scene that is active.
*
* @return The current scene.
*/
std::shared_ptr<Scene> getCurrentScene();
/**
* Deconstructs the game instance, does not deinitialize anything.
*/
virtual ~Game();
};
}

View File

@ -1,15 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "game/Game.hpp"
#include "scene/Scene.hpp"
namespace Dawn {
class GameInit {
public:
static std::function<void(Scene&)> getInitialScene();
};
}

40
src/dawn/input.c Normal file
View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
input_t INPUT;
void inputInit() {
memset(&INPUT, 0, sizeof(input_t));
}
void inputUpdate() {
// Update the previous state
memcpy(
INPUT.previousState,
INPUT.currentState,
sizeof(INPUT.previousState)
);
// Get the new state
for(inputbind_t i = 0; i < INPUT_BIND_COUNT; i++) {
INPUT.currentState[i] = inputGetState(i);
}
}
inputstate_t inputIsDown(const inputbind_t bind) {
return INPUT.currentState[bind];
}
inputstate_t inputWasPressed(const inputbind_t bind) {
return INPUT.currentState[bind] && !INPUT.previousState[bind];
}
inputstate_t inputWasReleased(const inputbind_t bind) {
return !INPUT.currentState[bind] && INPUT.previousState[bind];
}

66
src/dawn/input.h Normal file
View File

@ -0,0 +1,66 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawn.h"
typedef uint8_t inputbind_t;
typedef bool_t inputstate_t;
#define INPUT_BIND_UP 0x00
#define INPUT_BIND_DOWN 0x01
#define INPUT_BIND_LEFT 0x02
#define INPUT_BIND_RIGHT 0x03
#define INPUT_BIND_ACCEPT 0x04
#define INPUT_BIND_CANCEL 0x05
#define INPUT_BIND_COUNT (INPUT_BIND_CANCEL + 1)
typedef struct {
inputstate_t currentState[INPUT_BIND_COUNT];
inputstate_t previousState[INPUT_BIND_COUNT];
} input_t;
extern input_t INPUT;
/**
* Initializes the input system.
*/
void inputInit();
/**
* Updates the input system.
*/
void inputUpdate();
/**
* Returns whether or not the given input is down.
*
* @param bind The input to check.
*/
inputstate_t inputIsDown(const inputbind_t bind);
/**
* Returns whether or not the given input was pressed this update tick.
*
* @param bind The input to check.
*/
inputstate_t inputWasPressed(const inputbind_t bind);
/**
* Returns whether or not the given input was released this update tick.
*
* @param bind The input to check.
*/
inputstate_t inputWasReleased(const inputbind_t bind);
/**
* Returns the current state of the given input. Overridden per platform.
*
* @param bind The input to check.
*/
inputstate_t inputGetState(const inputbind_t bind);

View File

@ -1,208 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/Math.hpp"
#include "input/InputBinds.hpp"
namespace Dawn {
class DawnGame;
template<typename T>
class IInputManager {
protected:
std::unordered_map<enum InputBind, std::vector<T>> binds;
std::unordered_map<enum InputBind, float_t> valuesLeft;
std::unordered_map<enum InputBind, float_t> valuesRight;
bool_t currentIsLeft = true;
/**
* Method to be overwritten by the host, reads a RAW input value from
* the pad/input device directly.
*
* @param axis Axis to get the value of.
* @return The current input value (between 0 and 1).
*/
virtual float_t getInputValue(const T axis) = 0;
public:
/**
* Binds an axis to a bind.
*
* @param bind Bind to bind the axis to.
* @param axis Axis to use for this bind.
*/
void bind(const enum InputBind bind, const T axis) {
this->binds[bind].push_back(axis);
}
/**
* Unbind a previously bound axis from a bind.
*
* @param bind Bind to remove all binds from.
*/
void unbind(const enum InputBind bind) {
this->binds[bind].clear();
}
/**
* Unbind all values, all of them.
*/
void unbindAll() {
this->binds.clear();
this->values.clear();
}
/**
* Return the current bind value.
*
* @param bind Bind to get the value of.
* @return The current input state (between 0 and 1).
*/
float_t getValue(const enum InputBind bind) {
if(this->currentIsLeft) {
auto exist = this->valuesLeft.find(bind);
return exist == this->valuesLeft.end() ? 0.0f : exist->second;
} else {
auto exist = this->valuesRight.find(bind);
return exist == this->valuesRight.end() ? 0.0f : exist->second;
}
}
/**
* Return the bind value from the previous frame.
*
* @param bind Bind to get the value of.
* @return The value of the bind, last frame.
*/
float_t getValueLastUpdate(const enum InputBind bind) {
if(this->currentIsLeft) {
auto exist = this->valuesRight.find(bind);
return exist == this->valuesRight.end() ? 0.0f : exist->second;
} else {
auto exist = this->valuesLeft.find(bind);
return exist == this->valuesLeft.end() ? 0.0f : exist->second;
}
}
/**
* Returns an axis input for a given negative and positive bind. This will
* combine and clamp the values to be between -1 and 1. For example, if
* the negative bind is 100% actuated and positive is not at all actuated,
* then the value will be -1. If instead positive is 50% actuated, then
* the value will be -0.5. If both are equally actuacted, then the value
* will be 0.
*
* @param negative Bind to use for the negative axis.
* @param positive Bind to use for the positive axis.
* @return A value between -1 and 1.
*/
float_t getAxis(const enum InputBind negative, const enum InputBind positive) {
return -getValue(negative) + getValue(positive);
}
glm::vec2 getAxis2D(
const enum InputBind negativeX,
const enum InputBind positiveX,
const enum InputBind negativeY,
const enum InputBind positiveY
) {
return glm::vec2(
getAxis(negativeX, positiveX),
getAxis(negativeY, positiveY)
);
}
/**
* Returns the 2D Axis for the given binds.
*
* @param x X Axis bind.
* @param y Y Axis bind.
* @return 2D vector of the two given input binds.
*/
glm::vec2 getAxis2D(const enum InputBind x, const enum InputBind y) {
return glm::vec2(getValue(x), getValue(y));
}
/**
* Returns true if the given bind is currently being pressed (a non-zero
* value).
*
* @param bind Bind to check if pressed.
* @return True if value is non-zero, or false for zero.
*/
bool_t isDown(const enum InputBind bind) {
return this->getValue(bind) != 0.0f;
}
/**
* Returns true on the first frame an input was pressed (when the state
* had changed from 0 to non-zero).
*
* @param bind Bind to check if pressed.
* @return True if down this frame and not down last frame.
*/
bool_t isPressed(const enum InputBind bind) {
return this->getValue(bind) != 0 && this->getValueLastUpdate(bind) == 0;
}
/**
* Returns true on the first frame an input was released (when the state
* had changed from non-zero to 0).
*
* @param bind Bind to check if released.
* @return True if up this frame, and down last frame.
*/
bool_t wasReleased(const enum InputBind bind) {
return this->getValue(bind) == 0 && this->getValueLastUpdate(bind) != 0;
}
/**
* Internal method to update the input state, checks current input raws
* and decides what values are set for the inputs.
*/
void update() {
auto it = this->binds.begin();
this->currentIsLeft = !this->currentIsLeft;
// For each bind...
while(it != this->binds.end()) {
float_t value = 0.0f, valCurrent;
// For each input axis...
auto bindIt = it->second.begin();
while(bindIt != it->second.end()) {
// Get value and make the new max.
float_t inputValue = this->getInputValue(*bindIt);
value = Math::max<float_t>(value, inputValue);
++bindIt;
}
// Set into current values
if(this->currentIsLeft) {
valCurrent = this->valuesRight[it->first];
this->valuesLeft[it->first] = value;
} else {
valCurrent = this->valuesLeft[it->first];
this->valuesRight[it->first] = value;
}
// Fire events when necessary.
if(value == 0 && valCurrent == 1) {
// eventBindReleased.invoke(it->first);
} else if(valCurrent == 0 && value == 1) {
// eventBindPressed.invoke(it->first);
}
++it;
}
// TODO: trigger events
}
virtual ~IInputManager() {
}
};
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
LocaleManager.cpp
)

View File

@ -1,8 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "LocaleManager.hpp"
using namespace Dawn;

View File

@ -1,15 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class LocaleManager final {
private:
public:
};
}

View File

@ -1,15 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Card.cpp
PokerPot.cpp
PokerPlayer.cpp
PokerGame.cpp
PokerWinning.cpp
PokerTurn.cpp
)

View File

@ -1,94 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Card.hpp"
#include "util/Random.hpp"
using namespace Dawn;
void Card::shuffle(std::vector<struct Card> &deck) {
for(uint8_t i = 0; i < deck.size(); i++) {
uint8_t swap = Random::random<uint8_t>(0, deck.size() - 1);
struct Card tmp = deck[i];
deck[i] = deck[swap];
deck[swap] = tmp;
}
}
void Card::fillDeck(std::vector<struct Card> &deck) {
deck.clear();
for(uint8_t i = 0; i < CARD_DECK_SIZE; i++) {
deck.push_back(Card(i));
}
}
int32_t Card::contains(
const std::vector<struct Card> &deck,
const struct Card c
) {
if(deck.size() == 0) return -1;
return std::distance(
deck.begin(),
std::find_if(
deck.begin(),
deck.end(),
[c](struct Card card) {
return card.cardValue == c.cardValue;
}
)
);
}
int32_t Card::containsValue(
const std::vector<struct Card> &deck,
const enum CardValue number
) {
if(deck.size() == 0) return -1;
return std::distance(
deck.begin(),
std::find_if(
deck.begin(),
deck.end(),
[number](struct Card c) {
return c.getValue() == number;
}
)
);
}
std::vector<struct Card> Card::countPairs(
const std::vector<struct Card> &deck,
const enum CardValue val
) {
std::vector<struct Card> pairs;
std::for_each(
deck.begin(),
deck.end(),
[&pairs, val](struct Card c) {
if(c.getValue() == val) pairs.push_back(c);
}
);
return pairs;
}
bool_t Card::cardSorter(struct Card left, struct Card right) {
return left.cardValue < right.cardValue;
}
void Card::sort(std::vector<struct Card> &deck) {
std::sort(deck.begin(), deck.end(), &Card::cardSorter);
}
enum CardValue Card::getValue() {
return (enum CardValue)(cardValue % CARD_COUNT_PER_SUIT);
}
enum CardSuit Card::getSuit() {
return (enum CardSuit)(cardValue / CARD_COUNT_PER_SUIT);
}

View File

@ -1,151 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "assert/assert.hpp"
namespace Dawn {
enum class CardSuit : uint8_t {
Clubs = 0,
Diamonds = 1,
Hearts = 2,
Spades = 3,
};
enum class CardValue : uint8_t {
Two = 0,
Three = 1,
Four = 2,
Five = 3,
Six = 4,
Seven = 5,
Eight = 6,
Nine = 7,
Ten = 8,
Jack = 9,
Queen = 10,
King = 11,
Ace = 12,
Invalid = 0xFF
};
/** Count of cards in each suit */
#define CARD_COUNT_PER_SUIT 13
/** Count of suits */
#define CARD_SUIT_COUNT 4
/** Standard Card Deck Size */
#define CARD_DECK_SIZE CARD_COUNT_PER_SUIT*CARD_SUIT_COUNT
struct Card {
public:
uint8_t cardValue;
/**
* Shuffles a hand / deck
*
* @param deck Array of cards to shuffle.
*/
static void shuffle(std::vector<struct Card> &deck);
/**
* Fills a vector with all of the cards in a deck, in order.
*
* @param deck Deck to fill.
*/
static void fillDeck(std::vector<struct Card> &deck);
/**
* Check if an array of cards contains a specific card.
*
* @param deck Deck/Hand/Array of cards to check.
* @param card Card to look for
* @returns The index within the array that the card is. -1 if not found.
*/
static int32_t contains(
const std::vector<struct Card> &deck,
const struct Card card
);
/**
* Check if the array of cards contains a specific number.
*
* @param deck Array of cards to check
* @param number The number to look for.
* @returns The index that the first card is. -1 if not found.
*/
static int32_t containsValue(
const std::vector<struct Card> &deck,
const enum CardValue number
);
/**
* Counts the amount of times a card's number appears within the given
* hand.
*
* @param deck The hand to check
* @param val Value of pairs to find.
* @return Card pairs in the deck.
*/
static std::vector<struct Card> countPairs(
const std::vector<struct Card> &deck,
const enum CardValue val
);
/**
* Sorter for the cardSorter function.
*
* @param left Left card to compare.
* @param right Right card to compare.
* @returns True if left is less than right.
*/
static bool_t cardSorter(struct Card left, struct Card right);
/**
* Sort a hand of cards. Cards are ordered in descending weight, aces are
* high. Cards will be grouped by their suits, e.g. CARD_CLUBS_TWO will
* appear before CARD_DIAMONDS_KING.
*
* @param deck Hand of cards to sort.
*/
static void sort(std::vector<struct Card> &deck);
/**
* Constructor for the Card class.
*
* @param suit Suit of the card.
* @param num Number of the card.
*/
Card(const CardSuit suit, const CardValue num) :
Card(
((uint8_t)suit * CARD_COUNT_PER_SUIT) + (uint8_t)num
)
{
}
/**
* Constructor for the Card class.
*
* @param cv Card value.
*/
Card(const uint8_t cv) : cardValue(cv) {
}
/**
* Returns the number of a given card.
* @returns The card number.
*/
enum CardValue getValue();
/**
* Returns the suit of a given card.
* @returns The suit.
*/
enum CardSuit getSuit();
};
}

View File

@ -1,197 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerGame.hpp"
using namespace Dawn;
std::shared_ptr<PokerPlayer> PokerGame::addNewPlayer() {
auto player = std::make_shared<PokerPlayer>(
weak_from_this(), this->players.size()
);
assertFalse(this->players.size() == PLAYER_COUNT_MAX, "Too many players.");
this->players.push_back(player);
return player;
}
std::shared_ptr<PokerPlayer> PokerGame::getCurrentBetter() {
auto nextIndex = this->getNextBetterIndex();
if(nextIndex == 0xFF) return nullptr;
return this->players[nextIndex];
}
void PokerGame::newGame() {
assertTrue(this->players.size() >= PLAYER_COUNT_MIN, "Not enough players.");
this->newRound();
this->smallBlind = POKER_BLIND_SMALL_DEFAULT;
this->bigBlind = POKER_BLIND_BIG_DEFAULT;
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->setChips(POKER_PLAYER_CHIPS_DEFAULT);
player->isOut = false;
++it;
}
this->setDealer(0x00);
}
void PokerGame::newRound() {
this->deck.clear();
Card::fillDeck(this->deck);
this->grave.clear();
this->community.clear();
this->pots.clear();
this->pots.push_back(PokerPot());
this->hasFlopped = false;
this->hasTurned = false;
this->hasRivered = false;
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->hand.clear();
player->currentBet = 0;
player->isFolded = false;
player->isShowingHand = false;
player->hasBetThisRound = false;
player->timesRaised = 0;
player->currentBet = 0;
++it;
}
this->setDealer(this->dealerIndex + 0x01);
}
void PokerGame::newBettingRound() {
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->hasBetThisRound = false;
player->timesRaised = 0;
++it;
}
this->betterIndex = this->bigBlindIndex;
this->betterIndex = this->getNextBetterIndex();
}
uint8_t PokerGame::getNextBetterIndex() {
uint8_t j, i;
for(i = 0; i < this->players.size(); i++) {
j = (i + this->betterIndex) % this->players.size();
auto player = this->players[j];
if(player->needsToBetThisRound()) return j;
}
return 0xFF;
}
void PokerGame::takeBlinds() {
auto playerSmallBlind = this->players[this->smallBlindIndex];
auto playerBigBlind = this->players[this->bigBlindIndex];
playerSmallBlind->bet(this->smallBlind);
playerBigBlind->bet(this->bigBlind);
playerSmallBlind->hasBetThisRound = false;
playerBigBlind->hasBetThisRound = false;
}
void PokerGame::setDealer(const uint8_t dealer) {
uint8_t i, k;
std::shared_ptr<PokerPlayer> player;
bool_t foundDealer;
bool_t foundSmall;
foundDealer = false;
foundSmall = false;
this->dealerIndex = dealer;
for(i = 0; i < this->players.size(); i++) {
k = (dealer + i) % this->players.size();
player = this->players[k];
if(player->isOut) continue;
if(!foundDealer) {
this->dealerIndex = k;
foundDealer = true;
} else if(!foundSmall) {
this->smallBlindIndex = k;
foundSmall = true;
} else {
this->bigBlindIndex = k;
break;
}
}
}
uint8_t PokerGame::getRemainingBettersCount() {
uint8_t count = 0;
auto it = this->players.begin();
while(it != this->players.end()) {
if((*it)->needsToBetThisRound()) count++;
++it;
}
return count;
}
uint8_t PokerGame::getRemainingPlayersCount() {
uint8_t count = 0;
auto it = this->players.begin();
while(it != this->players.end()) {
if(!(*it)->isFolded && !(*it)->isOut) count++;
++it;
}
return count;
}
int32_t PokerGame::getCurrentCallValue() {
assertTrue(this->pots.size() > 0, "No pots?");
return this->pots.back().call;
}
void PokerGame::burnCard() {
assertTrue(this->deck.size() > 0, "No cards to burn.");
auto card = this->deck.back();
this->deck.pop_back();
this->grave.push_back(card);
}
void PokerGame::dealCard(PokerPlayer &player) {
assertTrue(this->deck.size() > 0, "No cards to deal.");
auto card = this->deck.back();
this->deck.pop_back();
player.hand.push_back(card);
}
void PokerGame::dealToEveryone(const uint8_t count) {
for(uint8_t i = 0; i < count; i++) {
auto it = this->players.begin();
while(it != this->players.end()) {
this->dealCard(*(*it));
++it;
}
}
}
void PokerGame::turn(const uint8_t count) {
uint8_t c = count;
if(c == 0xFF) c = this->getCountOfCardsToTurn();
assertTrue(this->deck.size() >= c, "Not enough cards to turn.");
for(uint8_t i = 0; i < c; i++) {
auto card = this->deck.back();
this->deck.pop_back();
this->community.push_back(card);
}
}
uint8_t PokerGame::getCountOfCardsToTurn() {
if(!this->hasFlopped) return 3;
if(!this->hasTurned) return 1;
if(!this->hasRivered) return 1;
assertUnreachable("No more cards to turn.");
}

View File

@ -1,151 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerPlayer.hpp"
/** The default blind cost for the big blind. */
#define POKER_BLIND_BIG_DEFAULT 600
/** The default blind cost for the small blind. (Defaults half big blind) */
#define POKER_BLIND_SMALL_DEFAULT (POKER_BLIND_BIG_DEFAULT/2)
/** How many cards are dealt for the flop, turn and river */
#define POKER_FLOP_CARD_COUNT 3
#define POKER_TURN_CARD_COUNT 1
#define POKER_RIVER_CARD_COUNT 1
#define PLAYER_COUNT_MAX 8
#define PLAYER_COUNT_MIN 2
namespace Dawn {
class PokerGame : public std::enable_shared_from_this<PokerGame> {
protected:
std::vector<struct Card> deck;
std::vector<struct Card> grave;
uint8_t dealerIndex;
uint8_t smallBlindIndex;
uint8_t bigBlindIndex;
int32_t smallBlind = POKER_BLIND_SMALL_DEFAULT;
int32_t bigBlind = POKER_BLIND_BIG_DEFAULT;
bool_t hasFlopped = false;
bool_t hasTurned = false;
bool_t hasRivered = false;
public:
std::vector<std::shared_ptr<PokerPlayer>> players;
std::vector<struct PokerPot> pots;
std::vector<struct Card> community;
uint8_t betterIndex;
/**
* Creates and adds a new player to the game.
*/
std::shared_ptr<PokerPlayer> addNewPlayer();
/**
* Returns the player that is currently the better.
*
* @return The player that is currently the better.
*/
std::shared_ptr<PokerPlayer> getCurrentBetter();
/**
* Starts a new game of poker.
*/
void newGame();
/**
* Starts a new round of poker.
*/
void newRound();
/**
* Starts a new betting round.
*/
void newBettingRound();
/**
* Takes the blinds from the players.
*/
void takeBlinds();
/**
* Sets the blinds for the game.
*
* @param small The cost of the small blind.
* @param big The cost of the big blind.
*/
void setBlinds(const int32_t small, const int32_t big);
/**
* Returns the count of players that still need to bet this round.
*
* @return The count of players that still need to bet this round.
*/
uint8_t getRemainingBettersCount();
/**
* Returns the current call value for the game.
*
* @return The current call value for the game.
*/
int32_t getCurrentCallValue();
/**
* Returns the next better index.
*
* @return The next better index.
*/
uint8_t getNextBetterIndex();
/**
* Sets the dealer for the game.
*
* @param dealer The index of the dealer.
*/
void setDealer(const uint8_t dealer);
/**
* Sends a card to the burn pile.
*/
void burnCard();
/**
* Deals a card to a player.
*
* @param player The player to deal the card to.
*/
void dealCard(PokerPlayer &player);
/**
* Deals a card to each player.
*
* @param count The count of cards to deal.
*/
void dealToEveryone(const uint8_t count);
/**
* Deals a card to the community.
*
* @param count The count of cards to turn.
*/
void turn(const uint8_t count = 0xFF);
/**
* Returns the count of cards that need to be turned.
*
* @return The count of cards that need to be turned.
*/
uint8_t getCountOfCardsToTurn();
/**
* Returns the count of players that are still in the game.
*
* @return The count of players that are still in the game.
*/
uint8_t getRemainingPlayersCount();
};
}

View File

@ -1,481 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerPlayer.hpp"
#include "PokerGame.hpp"
#include "util/Math.hpp"
#include "util/Random.hpp"
#include "util/Easing.hpp"
using namespace Dawn;
PokerPlayer::PokerPlayer(
const std::weak_ptr<PokerGame> pokerGame,
const uint8_t playerIndex
) {
this->pokerGame = pokerGame;
this->playerIndex = playerIndex;
this->chips = POKER_PLAYER_CHIPS_DEFAULT;
}
void PokerPlayer::addChips(const int32_t chips) {
assertTrue(chips > 0, "Must add a positive amount of chips.");
this->chips += chips;
if(this->chips > 0) this->isOut = false;
this->eventChipsChanged.emit();
}
void PokerPlayer::setChips(const int32_t chips) {
this->chips = 0;
this->addChips(chips);
}
bool_t PokerPlayer::needsToBetThisRound() {
if(this->isFolded) return false;
if(this->chips <= 0) return false;
if(!this->hasBetThisRound) return true;
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
if(this->currentBet < pg->getCurrentCallValue()) return true;
return false;
}
void PokerPlayer::bet(
struct PokerPot &pot,
const int32_t chips
) {
assertTrue(chips >= 0, "Chips must be a positive value.");
assertTrue(!this->isFolded, "Cannot bet if player is folded.");
assertTrue(!this->isOut, "Cannot bet if player is out.");
this->setChips(this->chips - chips);
this->currentBet += chips;
this->hasBetThisRound = true;
if(chips > 0) {
this->timesRaised++;
} else {
this->timesRaised = 0;
}
pot.chips += chips;
pot.call = Math::max<int32_t>(pot.call, this ->currentBet);
auto existing = std::find(pot.players.begin(), pot.players.end(), shared_from_this());
if(existing == pot.players.end()) pot.players.push_back(shared_from_this());
}
void PokerPlayer::bet(const int32_t chips) {
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
assertTrue(pg->pots.size() > 0, "PokerGame has no pots?");
this->bet(pg->pots.back(), chips);
}
void PokerPlayer::fold() {
this->isFolded = true;
this->hasBetThisRound = true;
this->timesRaised = 0;
}
bool_t PokerPlayer::canCheck() {
if(this->isFolded) return false;
if(this->isOut) return false;
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
return pg->getCurrentCallValue() == this->currentBet;
}
struct PokerTurn PokerPlayer::getAITurn() {
struct PokerTurn turn;
float_t confidence;
int32_t callBet;
float_t potOdds;
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
// Can the player do anything?
if(this->isFolded || this->isOut) {
turn.type = PokerTurnType::Out;
return turn;
}
// The following logic is heavily inspired by;
// https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c
// But with some changes and smarts added by me. The original source code will
// essentially just run a crap tun of simulated games and get the times that
// they are expected to win from those games, but I'm just going to use the
// odds of the winning hand.
// Is this preflop?
if(pg->community.size() == 0) {
assertTrue(
this->hand.size() == POKER_PLAYER_HAND_SIZE_MAX,
"Invalid hand size."
);
// Get the hand weight
auto cardNumber0 = this->hand[0].getValue();
auto suitNumber0 = this->hand[0].getSuit();
auto cardNumber1 = this->hand[1].getValue();
auto suitNumber1 = this->hand[1].getSuit();
// Get delta between cards
auto i = (uint8_t)Math::abs<int8_t>(
(int8_t)cardNumber0 - (int8_t)cardNumber1
);
// Get card weight
confidence = (float_t)cardNumber0 + (float_t)cardNumber1;
if(cardNumber0 == cardNumber1) {// Pairs
confidence += 6;
} else if(suitNumber0 == suitNumber1) {// Same suit
confidence += 4;
}
// Get difference from cards for guessing flush
if(i > 4) {
confidence -= 4;
} else if(i > 2) {
confidence -= i;
}
// Get the confidence delta 0-1
confidence = confidence / 30.0f;
// This may change in future, but I was finding the AI did not want to bet
// during the preflop enough, this curves the AI to want to preflop call
// often.
confidence = Easing::easeOutCubic(confidence);
} else {
// Simulate my hand being the winning hand, use that as the confidence
auto winning = this->getWinning();
confidence = PokerWinning::getWinningTypeConfidence(winning.type);
}
// Now we know how confident the AI is, let's put a chip value to that weight
// How many chips to call?
callBet = this->getCallBet();
// Do they need chips to call, or is it possible to check?
if(callBet > 0) {
potOdds = (float_t)callBet / (
(float_t)callBet +
(float_t)this->getSumOfChips()
);
} else {
potOdds = 1.0f / (float_t)pg->getRemainingBettersCount();
}
// Now determine the expected ROI
auto expectedGain = confidence / potOdds;
// Now get a random 0-100
auto random = Random::random<int32_t>() % 100;
// Determine the max bet that the AI is willing to make
auto maxBet = (int32_t)((float_t)this->chips / 1.75f) - (random / 2);
maxBet -= callBet;
// Determine what's a good bluff bet.
auto bluffBet = random * maxBet / 100 / 2;
// Now prep the output
auto isBluff = false;
auto amount = 0;
// Now the actual AI can happen. This is basically a weight to confidence
// ratio. The higher the gains and the confidence then the more likely the AI
// is to betting. There are also bluff chances within here.
if(expectedGain < 0.8f && confidence < 0.8f) {
if(random < 85) {
amount = 0;
} else {
amount = bluffBet;
isBluff = true;
}
} else if((expectedGain < 1.0f && confidence < 0.85f) || confidence < 0.1f) {
if(random < 80) {
amount = 0;
} else if(random < 5) {
amount = callBet;
isBluff = true;
} else {
amount = bluffBet;
isBluff = true;
}
} else if((expectedGain < 1.3f && confidence < 0.9f) || confidence < 0.5f) {
if(random < 60 || confidence < 0.5f) {
amount = callBet;
} else {
amount = maxBet;
}
} else if(confidence < 0.95f || pg->community.size() < 4) {
if(random < 20) {
amount = callBet;
} else {
amount = maxBet;
}
} else {
amount = (this->chips - callBet) * 9 / 10;
}
// TODO: We can nicely round the amounts here to get us to a more "human"
// number.
// If this is the first round... make it a lot less likely I'll bet
if(pg->community.size() == 0 && amount > callBet) {
if(random > 5) amount = callBet;
}
// Did we actually bet?
if(amount > 0) {
std::cout << "AI is betting " << amount << " chips, bluff:" << isBluff << std::endl;
// Let's not get caught in a raising loop with AI.
if(this->timesRaised >= POKER_PLAYER_MAX_RAISES) {
amount = callBet;
}
amount = Math::max<int32_t>(amount, callBet);
turn = PokerTurn::bet(shared_from_this(), amount);
turn.confidence = confidence;
} else if(this->canCheck()) {
turn = PokerTurn::bet(shared_from_this(), 0);
turn.confidence = 1;
} else {
turn = PokerTurn::fold(shared_from_this());
turn.confidence = 1 - confidence;
}
return turn;
}
int32_t PokerPlayer::getCallBet() {
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
return pg->getCurrentCallValue() - this->currentBet;
}
int32_t PokerPlayer::getSumOfChips() {
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
int32_t count = 0;
auto it = pg->pots.begin();
while(it != pg->pots.end()) {
if(std::find(
it->players.begin(), it->players.end(), shared_from_this()
) != it->players.end()) {
count += it->chips;
}
++it;
}
return count;
}
struct PokerWinning PokerPlayer::getWinning() {
struct PokerWinning winning;
struct Card card(0x00);
uint8_t i, j;
int32_t index;
enum CardValue number, look;
enum CardSuit suit;
std::vector<struct Card> pairs;
auto pg = this->pokerGame.lock();
assertNotNull(pg, "PokerGame has become invalid.");
winning.player = shared_from_this();
// Get the full poker hand (should be a 7 card hand, but MAY not be)
for(i = 0; i < pg->community.size(); i++) {
winning.full.push_back(pg->community[i]);
}
for(i = 0; i < this->hand.size(); i++) {
winning.full.push_back(this->hand[i]);
}
Card::sort(winning.full);
//////////////////////// Now look for the winning set ////////////////////////
// Royal / Straight Flush
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
if(number < CardValue::Five) continue;
suit = card.getSuit();
winning.set.clear();
winning.set.push_back(card);
// Now look for the matching cards (Reverse order to order from A to 10)
for(j = 1; j <= 4; j++) {
// Ace low.
look = (
number == CardValue::Five && j == 4 ?
(enum CardValue)CardValue::Ace :
(enum CardValue)((uint8_t)number - j)
);
index = Card::contains(winning.full, Card(suit, look));
if(index == -1) break;
winning.set.push_back(winning.full[index]);
}
// Check if has all necessary cards.
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
// Add self to array
winning.type = (
number == CardValue::Ace ? PokerWinningType::RoyalFlush :
PokerWinningType::StraightFlush
);
winning.fillRemaining();
return winning;
}
// Four of a kind.
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
pairs = Card::countPairs(winning.full, number);
if(pairs.size() < CARD_SUIT_COUNT) continue;
winning.set = pairs;
winning.type = PokerWinningType::FourOfAKind;
winning.fillRemaining();
return winning;
}
// Full House
winning.set.clear();
for(i = 0; i < winning.full.size(); i++) {
// Check we haven't already added this card.
card = winning.full[i];
if(Card::contains(winning.set, card) != -1) {
continue;
}
number = card.getValue();
pairs = Card::countPairs(winning.full, number);
// Did we find either two pair or three pair?
if(pairs.size() != 2 && pairs.size() != 3) continue;
if(winning.set.size() == 3) {//Clamp to 5 max.
pairs.pop_back();
}
// Copy found pairs.
for(j = 0; j < pairs.size(); j++) {
winning.set.push_back(pairs[j]);
}
// Winned?
if(winning.set.size() != POKER_WINNING_SET_SIZE) continue;
winning.type = PokerWinningType::FullHouse;
winning.fillRemaining();
return winning;
}
// Flush (5 same suit)
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
suit = card.getSuit();
winning.set.clear();
winning.set.push_back(card);
for(j = i+1; j < winning.full.size(); j++) {
if(winning.full[j].getSuit() != suit) continue;
winning.set.push_back(winning.full[j]);
if(winning.set.size() == POKER_WINNING_SET_SIZE) break;
}
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
winning.type = PokerWinningType::Flush;
winning.fillRemaining();
return winning;
}
// Straight (sequence any suit)
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
if(number < CardValue::Five) continue;
winning.set.clear();
winning.set.push_back(card);
for(j = 1; j <= 4; j++) {
// Ace low.
look = (
number == CardValue::Five && j == 4 ?
(enum CardValue)CardValue::Ace :
(enum CardValue)((uint8_t)number - j)
);
index = Card::containsValue(winning.full, look);
if(index == -1) break;
winning.set.push_back(winning.full[index]);
}
// Check if has all necessary cards.
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
winning.type = PokerWinningType::Straight;
winning.fillRemaining();
return winning;
}
// Three of a kind
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
pairs = Card::countPairs(winning.full, number);
if(pairs.size() != 3) continue;
winning.set = pairs;
winning.type = PokerWinningType::ThreeOfAKind;
winning.fillRemaining();
return winning;
}
// Two Pair
winning.set.clear();
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];// Check we haven't already added this card.
if(
winning.set.size() > 0 &&
Card::contains(winning.set, card) != -1
) {
continue;
}
number = card.getValue();
pairs = Card::countPairs(winning.full, number);
if(pairs.size() != 2) continue;
for(j = 0; j < pairs.size(); j++) {
winning.set.push_back(pairs[j]);
}
if(winning.set.size() != 4) continue;
winning.type = PokerWinningType::TwoPair;
winning.fillRemaining();
return winning;
}
// Pair
if(winning.set.size() == 2) {
winning.type = PokerWinningType::Pair;
winning.fillRemaining();
return winning;
}
// High card
winning.set.clear();
winning.fillRemaining();
winning.type = PokerWinningType::HighCard;
return winning;
}

View File

@ -1,131 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "Card.hpp"
#include "PokerPot.hpp"
#include "PokerWinning.hpp"
#include "PokerTurn.hpp"
#include "event/Event.hpp"
#define POKER_PLAYER_CHIPS_DEFAULT 10000
/** Maximum cards a players' hand can hold */
#define POKER_PLAYER_HAND_SIZE_MAX 2
#define POKER_PLAYER_MAX_RAISES 0x02
namespace Dawn {
class PokerGame;
class PokerPlayer : public std::enable_shared_from_this<PokerPlayer> {
public:
std::weak_ptr<PokerGame> pokerGame;
uint8_t playerIndex;
int32_t chips = 0;
int32_t currentBet = 0;
uint8_t timesRaised = 0;
bool_t isFolded = false;
bool_t isOut = false;
bool_t hasBetThisRound = false;
bool_t isShowingHand = false;
bool_t isHuman = false;
std::vector<struct Card> hand;
Event<> eventChipsChanged;
/**
* Constructor for the PokerPlayer class.
*
* @param pokerGame Poker game this player is a part of.
* @param playerIndex Index of the player in the game.
*/
PokerPlayer(
const std::weak_ptr<PokerGame> pokerGame,
const uint8_t playerIndex
);
/**
* Adds chips to the player. This will also update the players' state.
*
* @param chips Count of chips to add.
*/
void addChips(const int32_t chips);
/**
* Sets the chips a player has.
*
* @param chips Chips to set to the player.
*/
void setChips(const int32_t chips);
/**
* Returns true if the player still needs to bet this betting round.
*
* @return True if betting is still required by this player.
*/
bool_t needsToBetThisRound();
/**
* Let a player bet chips into the pot.
*
* @param pot Poker pot to bet in to.
* @param amount The amount of chips the player is betting.
*/
void bet(struct PokerPot &pot, const int32_t amount);
/**
* Let a player bet chips into the current pot.
*
* @param amount The amount of chips the player is betting.
*/
void bet(const int32_t amount);
/**
* Player folds.
*/
void fold();
/**
* Returns the AI result for a turn done by a non human player.
*
* @return Some information about the move the player is trying to perform
*/
struct PokerTurn getAITurn();
/**
* Calculates and returns the winning state for a given player
*
* @return The winning state for this current players hand.
*/
struct PokerWinning getWinning();
/**
* Returns the sum of chips in the pot(s) that the specified player is in.
* This does not consider the pot, player or hand, just the pure sum of
* chips.
*
* @return The sum of chips from the pots the player is within.
*/
int32_t getSumOfChips();
/**
* Get the bet necessary for a specific player to make a call. This takes
* the players current bet and the bet necessary to call into the pot and
* will return the difference.
*
* @return The count of chips needed to call into the current active pot.
*/
int32_t getCallBet();
/**
* Returns whether or not the player can check, or if they need to either
* fold, call or bet.
*
* @return True if they can check.
*/
bool_t canCheck();
};
}

View File

@ -1,111 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerPot.hpp"
#include "PokerGame.hpp"
#include "PokerPlayer.hpp"
using namespace Dawn;
void PokerPotWinning::award() {
auto it = this->winners.begin();
while(it != this->winners.end()) {
if(it == this->winners.begin()) {
(*it)->addChips(this->chipsEach + this->chipsOverflow);
} else {
(*it)->addChips(this->chipsEach);
}
++it;
}
}
struct PokerPotWinning PokerPot::getWinners() {
struct PokerPotWinning winning;
winning.pot = this;
// Calculate the winnings first.
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
if(player->isOut || player->isFolded) {
++it;
continue;
}
winning.participants.push_back(player);
winning.winnings[player] = player->getWinning();
++it;
}
// Compare participating players
auto it2 = winning.participants.begin();
while(it2 != winning.participants.end()) {
auto playerLeft = *it2;
auto winnerLeft = &winning.winnings[playerLeft];
bool_t isWinner = true;
enum CardValue highNumber = CardValue::Invalid;
enum CardValue number = CardValue::Invalid;
struct Card highCard(0xFF);
struct Card card(0xFF);
auto it3 = winning.participants.begin();
while(it3 != winning.participants.end()) {
if(it2 == it3) {
++it3;
continue;
}
auto playerRight = *it3;
auto winnerRight = &winning.winnings[playerRight];
// Am I the better hand / Is it the better hand?
if(winnerLeft->type < winnerRight->type) {
++it3;
continue;
}
if(winnerLeft->type > winnerRight->type) {
isWinner = false;
break;
}
// Equal, compare hands.
card = PokerWinning::compare(*winnerLeft, *winnerRight);
if(card.cardValue == 0xFF) {
isWinner = false;
break;
}
// Determine high card.
number = card.getValue();
if(
highNumber == CardValue::Invalid ||
number == CardValue::Ace ||
number > highNumber
) {
highCard = card;
highNumber = number;
}
++it3;
}
if(!isWinner) {
++it2;
continue;
}
winnerLeft->kicker = highCard;
winning.winners.push_back(playerLeft);
++it2;
}
winning.chipsEach = this->chips / (int32_t)winning.winners.size();
winning.chipsOverflow = this->chips - (
winning.chipsEach * (int32_t)winning.winners.size()
);
return winning;
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerWinning.hpp"
namespace Dawn {
class PokerPlayer;
class PokerGame;
struct PokerPot;
struct PokerPotWinning {
public:
std::map<std::shared_ptr<PokerPlayer>, struct PokerWinning> winnings;
std::vector<std::shared_ptr<PokerPlayer>> winners;
std::vector<std::shared_ptr<PokerPlayer>> participants;
struct PokerPot *pot;
int32_t chipsEach;
int32_t chipsOverflow;
void award();
};
struct PokerPot {
public:
int32_t chips;
int32_t call;
std::vector<std::shared_ptr<PokerPlayer>> players;
/**
* Get the winners of the pot.
*
* @return The winning state of the pot.
*/
struct PokerPotWinning getWinners();
};
}

Some files were not shown because too many files have changed in this diff Show More