Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive

- name: Install Linux dependencies
if: runner.os == 'Linux'
Expand Down
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ target_include_directories(imgui PUBLIC
${CMAKE_SOURCE_DIR}/imgui
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/headers
${CMAKE_SOURCE_DIR}/quark-libs/freetype/include
)

target_link_libraries(imgui PUBLIC
Expand Down Expand Up @@ -93,10 +94,15 @@ if(BUILD_ENGINE)
src/plugins/plugin_manager.cpp
src/scene.cpp
src/tex.cpp
src/text_mesh.cpp
)

add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${EDITOR_FILES})

target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/quark-libs/freetype/include
)

if(WIN32)
target_compile_definitions(${PROJECT_NAME}
PRIVATE USE_LIBTYPE_SHARED
Expand Down Expand Up @@ -135,6 +141,10 @@ if(BUILD_ENGINE)
)

if(WIN32)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:QuarkCore>
Expand All @@ -147,6 +157,19 @@ if(BUILD_ENGINE)
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug
)
endif()

if(WIN32)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:freetype>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
)
endif()
endif()

if(BUILD_PLUGIN)
Expand Down
1 change: 1 addition & 0 deletions quark-libs/freetype
Submodule freetype added at 7e0e56
84 changes: 84 additions & 0 deletions src/editor/editor_components_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
#include "editor_ui.h"
#include "editor_viewers.h"
#include "imgui.h"
#include "headers/text_mesh.h"
#include "headers/models.h"
#include "headers/entity.h"
#include "editor/editor_ui.h"
#include "editor/editor.h"
#include <language_manager.h>
#include <filesystem>
#include <cstring>

#define lang LanguageManager::get()

Expand Down Expand Up @@ -47,6 +49,7 @@ void ComponentUIHelper::draw_entity_inspector(Editor& editor, Entity& entity, Sh
else if (auto light = std::dynamic_pointer_cast<LightComponent>(comp)) draw_light_component(editor, entity, light.get(), shader);
else if (auto material = std::dynamic_pointer_cast<MaterialComponent>(comp)) draw_material_component(editor, entity, material.get());
else if (auto collision = std::dynamic_pointer_cast<CollisionComponent>(comp)) draw_collision_component(editor, entity, collision.get());
else if (auto text = std::dynamic_pointer_cast<Text3DComponent>(comp)) draw_3d_text_component(editor, entity, text.get());

ImGui::Spacing();

Expand Down Expand Up @@ -136,6 +139,25 @@ void ComponentUIHelper::draw_entity_inspector(Editor& editor, Entity& entity, Sh
components_manager->add_component(light);
} else { editor.undo(); }
}

if (ImGui::MenuItem(lang.word("text"))) {
editor.save_state();
bool already_exists = false;

for (size_t j = 0; j < components_manager->get_component_count(); ++j) {
auto existing = components_manager->get_component(j);
if (existing && existing->get_type() == COMPONENT_CUSTOM) {
already_exists = true;
break;
}

}

if (!already_exists) {
auto text = std::make_shared<Text3DComponent>();
components_manager->add_component(text);
} else { editor.undo(); }
}
ImGui::EndPopup();
}

Expand Down Expand Up @@ -634,3 +656,65 @@ void ComponentUIHelper::draw_collision_component(Editor& editor, Entity& entity,
mark_entity_bounds_dirty(&entity);
}
}

void ComponentUIHelper::draw_3d_text_component(Editor& editor, Entity& entity, Text3DComponent* text) {
if (!text) return;

char buf[256] = {};
std::strncpy(buf, text->text.c_str(), sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';

if (ImGui::InputText("Text", buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue)) {
editor.save_state();
text->text = buf;
update_model(&entity);
mark_entity_bounds_dirty(&entity);
}

auto drag = [&](const char* label, float& val, float spd, float mn, float mx) {
if (ImGui::DragFloat(label, &val, spd, mn, mx)) {
editor.save_state();
update_model(&entity);
mark_entity_bounds_dirty(&entity);
}
};

drag("Size", text->size, 0.01f, 0.05f, 10.f);
drag("Thickness", text->thickness, 0.01f, 0.01f, 5.f);
drag("Letter Spacing", text->letter_spacing, 0.005f, 0.f, 100);

ImGui::Separator();

static std::vector<std::pair<std::string,std::string>> s_fonts;
static std::vector<const char*> s_font_names;
static int s_selected = -1;
static bool s_scanned = false;

if (!s_scanned) {
s_fonts = get_system_fonts();
s_font_names.clear();
for (auto& f : s_fonts) s_font_names.push_back(f.first.c_str());

for (int i = 0; i < (int)s_fonts.size(); i++) {
if (s_fonts[i].second == text->font_path) { s_selected = i; break; }
}
s_scanned = true;
}

ImGui::Text("Font");
if (!s_font_names.empty()) {
if (ImGui::Combo("##font", &s_selected, s_font_names.data(), (int)s_font_names.size())) {
editor.save_state();
text->font_path = s_fonts[s_selected].second;
update_model(&entity);
mark_entity_bounds_dirty(&entity);
}
}

else {
ImGui::TextDisabled("No fonts found");
}

if (!text->font_path.empty() && ImGui::IsItemHovered())
ImGui::SetTooltip("%s", text->font_path.c_str());
}
1 change: 1 addition & 0 deletions src/editor/editor_components_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ComponentUIHelper {
static void draw_light_component(Editor& editor, Entity& entity, LightComponent* light, Shader shader);
static void draw_material_component(Editor& editor, Entity& entity, MaterialComponent* material);
static void draw_collision_component(Editor& editor, Entity& entity, CollisionComponent* collision);
static void draw_3d_text_component(Editor& editor, Entity& entity, Text3DComponent* text);

private:
static bool should_show_component_menu;
Expand Down
6 changes: 6 additions & 0 deletions src/editor/editor_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ Entity make_entity_from_asset(Scene& scene, ModelAsset& asset) {
store_material_textures(&entity);
mat->texture_source = TEXTURE_NONE;
mat->texture_name.clear();

if (asset.name == "Text") {
auto text_comp = std::make_shared<Text3DComponent>();
entity.get_components()->add_component(text_comp);
}
}

else {
if (!load_model_instance(asset, mesh->model)) {
mesh->asset = nullptr;
Expand Down
31 changes: 31 additions & 0 deletions src/headers/component.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,34 @@ class ComponentManager {

void deserialize(const nlohmann::json& json);
};

class Text3DComponent : public Component {
public:
std::string text = "3D Text";
float size = 1.0f;
float thickness = 0.2f;
float letter_spacing = 0.1f;
std::string font_path;


Text3DComponent() { name = "3D Text"; type = COMPONENT_CUSTOM; }

std::string get_type_name() const override { return "3D Text"; }
ComponentType get_type() const override { return COMPONENT_CUSTOM; }

void serialize(nlohmann::json& j) const override {
j["text"] = text;
j["size"] = size;
j["thickness"] = thickness;
j["letter_spacing"] = letter_spacing;
j["font_path"] = font_path;
}

void deserialize(const nlohmann::json& j) override {
if (j.contains("text")) text = j["text"];
if (j.contains("size")) size = j["size"];
if (j.contains("thickness")) thickness = j["thickness"];
if (j.contains("letter_spacing")) letter_spacing = j["letter_spacing"];
if (j.contains("font_path")) font_path = j["font_path"];
}
};
2 changes: 1 addition & 1 deletion src/headers/models.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ bool entity_has_mesh_overrides(const Entity& entity);
void capture_mesh_overrides_from_model(Entity& entity);
bool apply_mesh_overrides(Entity& entity);
bool get_mesh_triangle_vertex_indices(const Mesh& mesh, int triangle_index, int out_indices[3]);
bool detach_mesh_triangles(Entity& entity);
bool detach_mesh_triangles(Entity& entity);
10 changes: 10 additions & 0 deletions src/headers/text_mesh.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <string>
#include <vector>
#include <raylib.h>

void init_freetype();
void shutdown_freetype();

std::vector<std::pair<std::string, std::string>> get_system_fonts();
std::string get_default_font_path();
Model generate_text_mesh(const std::string& text, float size, float thickness, float letter_spacing, const std::string& font_path);
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "plugins/plugin_manager.h"
#include "headers/lighting.h"
#include "headers/language_manager.h"
#include "headers/text_mesh.h"
#include "editor/editor.h"
#include "editor/editor_entity.h"
#include "headers/camera.h"
Expand Down Expand Up @@ -387,6 +388,7 @@ int main(int argc, char* argv[]) {

InitWindow(1280, 720, "Quark Engine", RendererType::OpenGL);
SetTargetFPS(GetCurrentMonitorRefreshRate());
init_freetype();
SetExitKey(KEY_NULL);
qcImGuiSetup(false);
reload_editor_fonts(LanguageManager::get().current);
Expand Down Expand Up @@ -529,6 +531,7 @@ int main(int argc, char* argv[]) {
UnloadShader(lighting_shader);
g_plugin_manager->unload_all();
qcImGuiShutdown();
shutdown_freetype();
CloseWindow();
return 0;
}
22 changes: 21 additions & 1 deletion src/models.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "headers/models.h"
#include "headers/scene.h"
#include "headers/text_mesh.h"
#include "editor/editor_utils.h"
#include <algorithm>
#include <cctype>
Expand Down Expand Up @@ -426,6 +427,17 @@ void load_models() {
torus_asset.is_procedural = true;
torus_asset.generator = [](int seg) { return LoadModelFromMesh(GenMeshTorus(1.0f, 1.0f, seg, seg)); };
assets.push_back(torus_asset);

ModelAsset text_asset;
text_asset.name = "3D Text";
text_asset.type = CUBE;
text_asset.is_procedural = true;
text_asset.generator = [](int) {
std::string fp = get_default_font_path();
return generate_text_mesh("Text", 0.3f, 0.15f, 0.2f, fp);
};

assets.push_back(text_asset);
}

void update_model(Entity* e)
Expand All @@ -437,6 +449,14 @@ void update_model(Entity* e)
if (!mesh) return;

if (mesh->model.meshCount > 0 && entity_owns_model(*e)) UnloadModel(mesh->model);

if (auto* tc = e->get_components()->get_component_of_type<Text3DComponent>().get()) {
std::string fp = tc->font_path.empty() ? get_default_font_path() : tc->font_path;
mesh->model = generate_text_mesh(tc->text, tc->size, tc->thickness, tc->letter_spacing, fp);
mesh->owns_model_instance = true;
return;
}

if (mesh->is_editable_mesh)
{
mesh->model = {};
Expand Down Expand Up @@ -576,4 +596,4 @@ void refresh_models(std::string project_path, Scene& scene) {
}
}
}
}
}
Loading
Loading