Update codebase

This commit is contained in:
2024-05-27 11:53:00 -05:00
parent 9adbc0565f
commit 7d8c104a93
43 changed files with 221 additions and 185 deletions

View File

@ -7,7 +7,6 @@
target_link_libraries(${DAWN_TARGET_NAME}
PUBLIC
glm
freetype
archive_static
)
@ -35,22 +34,4 @@ add_subdirectory(scene)
# add_subdirectory(state)
add_subdirectory(time)
add_subdirectory(util)
add_subdirectory(ui)
# Definitions
# target_compile_definitions(${DAWN_TARGET_NAME}
# PUBLIC
# DAWN_DEBUG_BUILD=${DAWN_DEBUG_BUILD}
# )
# Tests
target_link_libraries(dawntests
PUBLIC
glm
freetype
)
target_include_directories(dawntests
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
add_subdirectory(ui)

View File

@ -6,10 +6,4 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
assert.cpp
)
target_sources(dawntests
PRIVATE
assert.cpp
_test_.assert.cpp
)

View File

@ -1,77 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include <catch2/catch_test_macros.hpp>
#include "assert.hpp"
TEST_CASE("assertTrue", "[assert]") {
SECTION("true") {
assertTrue(true, "This should not fail");
}
SECTION("false") {
REQUIRE_THROWS(assertTrue(false, "This should fail"));
}
}
TEST_CASE("assertFalse", "[assert]") {
SECTION("true") {
REQUIRE_THROWS(assertFalse(true, "This should fail"));
}
SECTION("false") {
assertFalse(false, "This should not fail");
}
}
TEST_CASE("assertUnreachable", "[assert]") {
REQUIRE_THROWS(assertUnreachable("This should fail all the time."));
}
TEST_CASE("assertNotNull", "[assert]") {
SECTION("nullptr") {
REQUIRE_THROWS(assertNotNull(nullptr, "This should fail"));
}
SECTION("NULL") {
REQUIRE_THROWS(assertNotNull(NULL, "This should fail"));
}
SECTION("not nullptr or NULL") {
assertNotNull((void*)0x1, "This should not fail");
}
}
TEST_CASE("assertNull", "[assert]") {
SECTION("nullptr") {
assertNull(nullptr, "This should not fail");
}
SECTION("NULL") {
assertNull(NULL, "This should not fail");
}
SECTION("not nullptr or NULL") {
REQUIRE_THROWS(assertNull((void*)0x1, "This should fail"));
}
}
TEST_CASE("assertMapHasKey", "[assert]") {
SECTION("has key") {
std::map<std::string, std::string> map;
map["key"] = "value";
assertMapHasKey(map, "key", "This should not fail");
}
SECTION("does not have key") {
std::map<std::string, std::string> map;
map["different_key"] = "Some other value";
REQUIRE_THROWS(assertMapHasKey(map, "key", "This should fail"));
}
}
TEST_CASE("assertDeprecated", "[assert]") {
REQUIRE_THROWS(assertDeprecated("This should fail"));
}

View File

@ -5,7 +5,6 @@
#include "AssetManager.hpp"
#include "loaders/TextureLoader.hpp"
#include "loaders/TrueTypeLoader.hpp"
using namespace Dawn;
@ -60,45 +59,6 @@ bool_t AssetManager::isLoaded(const std::string filename) {
return false;
}
template<>
std::shared_ptr<Texture> AssetManager::get<Texture>(
const std::string filename
) {
auto existing = this->getExisting<TextureLoader>(filename);
if(existing) {
// Check pointer hasn't gone stale, if it has remove it and create new.
auto texture = existing->weakTexture.lock();
if(texture) return texture;
this->removeExisting(filename);
}
std::shared_ptr<TextureLoader> loader = std::make_shared<TextureLoader>(
filename
);
pendingAssetLoaders.push_back(std::static_pointer_cast<AssetLoader>(loader));
return loader->sharedTexture;
}
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);
}
AssetManager::~AssetManager() {
}

View File

@ -7,5 +7,4 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
TextureLoader.cpp
TrueTypeLoader.cpp
)

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

@ -39,11 +39,12 @@ extern "C" {
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtx/component_wise.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/gtx/intersect.hpp>
#include <glm/gtc/type_ptr.hpp>
#define GLM_ENABLE_EXPERIMENTAL 1
#include <glm/gtx/matrix_decompose.hpp>

View File

@ -12,6 +12,5 @@ target_sources(${DAWN_TARGET_NAME}
)
# Subdirs
add_subdirectory(font)
add_subdirectory(mesh)
add_subdirectory(shader)

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

@ -47,5 +47,4 @@ std::shared_ptr<Scene> Game::getCurrentScene() {
}
Game::~Game() {
std::cout << "Game successfully destructed" << std::endl;
}

View File

@ -6,5 +6,4 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
UIRectangle.cpp
UILabel.cpp
)

View File

@ -1,175 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "UILabel.hpp"
using namespace Dawn;
void UILabel::getSelfQuads(UICanvas &ctx) {
std::vector<struct UIShaderQuad> quads;
if(this->texture == nullptr || this->text.empty()) return;
glm::vec4 quad;
glm::vec2 pos = glm::vec2(0, this->texture->fontSize);
bool_t lastCharWasSpace = false;
for(size_t i = 0; i < text.size(); i++) {
wchar_t c = text[i];
auto info = texture->getCharacterData(c);
// Newline(s)
if(c == L'\n') {
pos.x = 0;
pos.y += this->texture->fontSize;
continue;
}
// Spaces
if(c == L' ') {
pos.x += info.advance.x;
lastCharWasSpace = true;
continue;
}
// Word Wrap
if(wordWrap) {
if(lastCharWasSpace) {
// Scan ahead to next space
float_t wordWidth = pos.x;// Start at current position and scan ahead.
for(size_t j = i; j < text.size(); j++) {
wchar_t c2 = text[j];
if(c2 == L' ' || c2 == L'\n') {
break;// If we hit another space, we are OK.
}
// Will this character fit on the row? If not the whole word will wrap.
auto info2 = texture->getCharacterData(c);
wordWidth += info.advance.x;
if(wordWidth > size.x) {
pos.x = 0;
pos.y += this->texture->fontSize;
break;
}
}
lastCharWasSpace = false;
}
// } else if(pos.x + info.size.x > subAlignedPosition.x + size.x) {
// // Not word wrap, but instead just overflow characters.
// pos.x = 0;
// pos.y += this->texture->fontSize;
}
ctx.addQuad(
{
subAlignedPosition.x + pos.x + info.offset.x,
subAlignedPosition.y + pos.y + info.offset.y,
subAlignedPosition.x + pos.x + info.size.x + info.offset.x,
subAlignedPosition.y + pos.y + info.size.y + info.offset.y
},
{
info.quad.x,
info.quad.y,
info.quad.z,
info.quad.w
},
this->color,
UIShaderQuadStyle::FONT,
texture->texture
);
pos += info.advance;
}
}
float_t UILabel::getContentWidth() {
if(this->texture == nullptr || this->text.empty()) return 0.0f;
float_t lineWidth = 0.0f;
float_t width = 0.0f;
for(wchar_t c : text) {
if(c == L'\n') {
width = Math::max<float_t>(width, lineWidth);
lineWidth = 0.0f;
continue;
}
auto info = texture->getCharacterData(c);
lineWidth += info.advance.x;
if(
this->hasExplicitWidth() &&
lineWidth >= size.x
) return size.x;
}
width = Math::max<float_t>(width, lineWidth);
return width;
}
float_t UILabel::getContentHeight() {
if(this->texture == nullptr || this->text.empty()) return 0.0f;
float_t height = this->texture->fontSize;
float_t lineWidth = 0.0f;
bool_t lastCharWasSpace = false;
for(wchar_t c : text) {
if(c == L'\n') {
height += this->texture->fontSize;
continue;
}
auto info = texture->getCharacterData(c);
if(c == L' ') {
lineWidth += info.advance.x;
lastCharWasSpace = true;
continue;
}
if(wordWrap) {
if(lastCharWasSpace) {
// Scan ahead to next space
float_t wordWidth = lineWidth;// Start at current position and scan ahead.
for(size_t j = 0; j < text.size(); j++) {
wchar_t c2 = text[j];
if(c2 == L' ' || c2 == L'\n') {
break;// If we hit another space, we are OK.
}
// Will this character fit on the row? If not the whole word will wrap.
auto info2 = texture->getCharacterData(c);
wordWidth += info.advance.x;
if(wordWidth > size.x) {
height += this->texture->fontSize;
lineWidth = 0.0f;
break;
}
}
lastCharWasSpace = false;
}
// } else if(lineWidth + info.size.x > size.x) {
// height += this->texture->fontSize;
// lineWidth = 0.0f;
}
}
return height;
}
std::shared_ptr<TrueTypeTexture> UILabel::getFont() {
return this->texture;
}
std::wstring UILabel::getText() {
return this->text;
}
void UILabel::setFont(std::shared_ptr<TrueTypeTexture> texture) {
this->texture = texture;
}
void UILabel::setText(const std::wstring &text) {
this->text = text;
}

View File

@ -1,54 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "ui/UISubAlignableElement.hpp"
#include "display/font/TrueTypeTexture.hpp"
namespace Dawn {
class UILabel final : public UISubAlignableElement {
private:
std::shared_ptr<TrueTypeTexture> texture = nullptr;
std::wstring text = L"Hello World";
protected:
void getSelfQuads(UICanvas &ctx) override;
public:
bool_t wordWrap = true;
struct Color color = COLOR_WHITE;
float_t getContentWidth() override;
float_t getContentHeight() override;
/**
* Returns the font used for this label.
*
* @return The font used for this label.
*/
std::shared_ptr<TrueTypeTexture> getFont();
/**
* Returns the text used for this label.
*
* @return The text used for this label.
*/
std::wstring getText();
/**
* Sets the font to use for this label.
*
* @param texture TrueType texture to use for this label.
*/
void setFont(std::shared_ptr<TrueTypeTexture> texture);
/**
* Sets the text to use for this label.
*
* @param text The text to use for this label.
*/
void setText(const std::wstring &text);
};
}