rename engine to src
This commit is contained in:
58
src/action.cpp
Normal file
58
src/action.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "action.h"
|
||||
#include "app.h"
|
||||
|
||||
ActionManager ActionManager::I;
|
||||
|
||||
void ActionManager::add(Action *action)
|
||||
{
|
||||
I.m_redos = std::stack<std::unique_ptr<Action>>();
|
||||
I.m_actions.emplace(action);
|
||||
I.m_memory += action->memory();
|
||||
//LOG("History: %.2f KB", I.m_memory / 1024.f);
|
||||
App::I.update_memory_usage(I.m_memory);
|
||||
}
|
||||
|
||||
void ActionManager::undo()
|
||||
{
|
||||
if (I.m_actions.empty())
|
||||
return;
|
||||
|
||||
I.m_redos.emplace(I.m_actions.top()->get_redo());
|
||||
I.m_redos.top()->was_saved = !ui::Canvas::I->m_unsaved;
|
||||
|
||||
I.m_actions.top()->undo();
|
||||
I.m_memory -= I.m_actions.top()->memory();
|
||||
ui::Canvas::I->m_unsaved = !I.m_actions.top()->was_saved;
|
||||
I.m_actions.pop();
|
||||
//LOG("History: %.2f KB", I.m_memory / 1024.f);
|
||||
App::I.update_memory_usage(I.m_memory);
|
||||
App::I.title_update();
|
||||
}
|
||||
|
||||
void ActionManager::redo()
|
||||
{
|
||||
if (I.m_redos.empty())
|
||||
return;
|
||||
|
||||
I.m_actions.emplace(I.m_redos.top()->get_redo());
|
||||
I.m_actions.top()->was_saved = !ui::Canvas::I->m_unsaved;
|
||||
I.m_memory += I.m_actions.top()->memory();
|
||||
|
||||
I.m_redos.top()->undo();
|
||||
ui::Canvas::I->m_unsaved = !I.m_redos.top()->was_saved;
|
||||
I.m_redos.pop();
|
||||
//LOG("History: %.2f KB", I.m_memory / 1024.f);
|
||||
App::I.update_memory_usage(I.m_memory);
|
||||
App::I.title_update();
|
||||
}
|
||||
|
||||
void ActionManager::clear()
|
||||
{
|
||||
while (!I.m_actions.empty())
|
||||
I.m_actions.pop();
|
||||
I.m_memory = 0;
|
||||
//LOG("History: %.2f KB", I.m_memory / 1024.f);
|
||||
App::I.update_memory_usage(I.m_memory);
|
||||
}
|
||||
29
src/action.h
Normal file
29
src/action.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
bool was_saved = false;
|
||||
virtual void run() = 0;
|
||||
virtual void undo() = 0;
|
||||
virtual Action* get_redo() = 0;
|
||||
virtual size_t memory() = 0;
|
||||
virtual ~Action(){};
|
||||
};
|
||||
|
||||
class ActionManager
|
||||
{
|
||||
public:
|
||||
static ActionManager I;
|
||||
std::stack<std::unique_ptr<Action>> m_actions;
|
||||
std::stack<std::unique_ptr<Action>> m_redos;
|
||||
size_t m_memory = 0;
|
||||
static void add(Action* action);
|
||||
static void undo();
|
||||
static void redo();
|
||||
static void clear();
|
||||
static bool empty()
|
||||
{
|
||||
return I.m_actions.empty();
|
||||
}
|
||||
};
|
||||
764
src/app.cpp
Normal file
764
src/app.cpp
Normal file
@@ -0,0 +1,764 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "app.h"
|
||||
#include "node_icon.h"
|
||||
#include "node_dialog_open.h"
|
||||
#include "node_progress_bar.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
void android_async_lock(struct engine* engine);
|
||||
void android_async_swap(struct engine* engine);
|
||||
void android_async_unlock(struct engine* engine);
|
||||
#elif _WIN32
|
||||
bool async_lock_try();
|
||||
void async_lock();
|
||||
void async_swap();
|
||||
void async_unlock();
|
||||
void destroy_window();
|
||||
#endif
|
||||
|
||||
using namespace ui;
|
||||
|
||||
App App::I; // singleton
|
||||
|
||||
void App::create()
|
||||
{
|
||||
width = 1920/2;
|
||||
height = 1080/2;
|
||||
}
|
||||
|
||||
bool App::request_close()
|
||||
{
|
||||
static bool dialog_already_opened = false;
|
||||
if (!ui::Canvas::I->m_unsaved)
|
||||
return true;
|
||||
if (!dialog_already_opened)
|
||||
{
|
||||
async_start();
|
||||
auto* m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Do you want to close without saving?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_ok->on_click = [this](Node*) {
|
||||
#ifdef _WIN32
|
||||
destroy_window();
|
||||
PostQuitMessage(0);
|
||||
#endif
|
||||
#ifdef __OSX__
|
||||
[osx_view close];
|
||||
#endif
|
||||
ui::Canvas::I->m_unsaved = false;
|
||||
};
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_cancel->on_click = [this,m](Node*) {
|
||||
m->destroy();
|
||||
dialog_already_opened = false;
|
||||
};
|
||||
async_redraw();
|
||||
async_end();
|
||||
dialog_already_opened = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void App::clear()
|
||||
{
|
||||
glClearColor(.1f, .1f, .1f, 1.f);
|
||||
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void App::initAssets()
|
||||
{
|
||||
LOG("initializing assets");
|
||||
FontManager::init();
|
||||
LOG("initializing assets loading fonts");
|
||||
FontManager::load(kFont::Arial_11, "data/arial.ttf", 15);
|
||||
FontManager::load(kFont::Arial_30, "data/arial.ttf", 30);
|
||||
|
||||
LOG("initializing assets create sampler");
|
||||
sampler.create(GL_NEAREST);
|
||||
LOG("initializing assets load uvs texture");
|
||||
if (!tex.load("data/uvs.jpg"))
|
||||
LOG("error loading image");
|
||||
LOG("initializing assets completed");
|
||||
}
|
||||
|
||||
void App::initLog()
|
||||
{
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString* docpath = [paths objectAtIndex:0];
|
||||
data_path = [docpath cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
NSString* recpath = [docpath stringByAppendingString:@"/rec"];
|
||||
rec_path = [recpath cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
NSError* recerr = nil;
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:recpath withIntermediateDirectories:YES attributes:nil error:&recerr])
|
||||
{
|
||||
LOG("error creating rec path: %s", [[recerr localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
}
|
||||
#elif _WIN32
|
||||
//CHAR my_documents[MAX_PATH];
|
||||
//HRESULT result = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, my_documents);
|
||||
|
||||
//HMODULE hModule = GetModuleHandle(NULL);
|
||||
//CHAR path[MAX_PATH];
|
||||
//GetModuleFileNameA(hModule, path, MAX_PATH);
|
||||
//CHAR out_drive[MAX_PATH];
|
||||
//CHAR out_path[MAX_PATH];
|
||||
//_splitpath(path, out_drive, out_path, nullptr, nullptr);
|
||||
//sprintf_s(path, "%s%s", out_drive, out_path);
|
||||
//data_path = path;
|
||||
|
||||
CHAR path[MAX_PATH];
|
||||
GetCurrentDirectoryA(sizeof(path), path);
|
||||
data_path = path;
|
||||
rec_path = data_path + "\\frames";
|
||||
#endif
|
||||
|
||||
//LogRemote::I.start();
|
||||
LogRemote::I.file_init();
|
||||
}
|
||||
|
||||
int progress_callback_download(void *clientp, curl_off_t dltotal,
|
||||
curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
std::function<void(float)> progress = *(std::function<void(float)>*)clientp;
|
||||
progress((float)dlnow / (float)dltotal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int progress_callback_upload(void *clientp, curl_off_t dltotal,
|
||||
curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
std::function<void(float)> progress = *(std::function<void(float)>*)clientp;
|
||||
progress((float)ulnow / (float)ultotal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void App::download(std::string filename, std::function<void(float)> progress)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
auto dest = data_path + "/" + filename;
|
||||
FILE* fp = fopen(dest.c_str(), "wb");
|
||||
std::string url = "http://omigamedev.com/panopainter/cloud/cloud-dwl.php?file=" + filename;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_write);
|
||||
if (progress)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_download);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
}
|
||||
auto err = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
bool App::check_license()
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
std::string url = "http://omigamedev.com/panopainter/79516B99-8E67-40AD-B12F-149A5A9C2E15";
|
||||
//std::string url = "https://panopainter.com/license/E8EDC2FE-E1BD-4AB1-91BD-FCCD926739BD.php"; // wacom
|
||||
//std::string url = "https://panopainter.com/license/A744FBA9-BC6C-43C8-BD24-0CCE24B3D985.php"; // others
|
||||
|
||||
std::string ret;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
|
||||
//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2L);
|
||||
|
||||
auto err = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
LOG("License check: %s", ret.c_str());
|
||||
if (err == CURLcode::CURLE_OK)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void App::upload(std::string filename, std::string name, std::function<void(float)> progress)
|
||||
{
|
||||
CURL *curl;
|
||||
|
||||
struct curl_httppost *formpost = NULL;
|
||||
struct curl_httppost *lastptr = NULL;
|
||||
|
||||
//curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "fileToUpload",
|
||||
CURLFORM_FILE, filename.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
curl = curl_easy_init();
|
||||
std::string res;
|
||||
|
||||
if (curl)
|
||||
{
|
||||
std::string url = "http://omigamedev.com/panopainter/cloud/cloud-upl.php?name=" + name;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
|
||||
if (progress)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_upload);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
}
|
||||
auto err = curl_easy_perform(curl);
|
||||
std::cout << "\n\nUPLOAD RESULT\n" << res << "\n\n\n";
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
void App::init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
|
||||
// colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c
|
||||
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
|
||||
{
|
||||
static std::map<GLenum, int> colors = {
|
||||
{ GL_DEBUG_SEVERITY_NOTIFICATION, 8 },
|
||||
{ GL_DEBUG_SEVERITY_LOW, 8 },
|
||||
{ GL_DEBUG_SEVERITY_MEDIUM, FOREGROUND_GREEN | FOREGROUND_INTENSITY },
|
||||
{ GL_DEBUG_SEVERITY_HIGH, FOREGROUND_RED | FOREGROUND_INTENSITY },
|
||||
};
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM || severity == GL_DEBUG_SEVERITY_LOW)
|
||||
{
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors[severity]);
|
||||
LOG("OPENGL: %.*s", length, message);
|
||||
FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes);
|
||||
//__debugbreak();
|
||||
}
|
||||
}, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
#endif
|
||||
|
||||
LOG("GL version: %s", glGetString(GL_VERSION));
|
||||
LOG("GL vendor: %s", glGetString(GL_VENDOR));
|
||||
LOG("GL renderer: %s", glGetString(GL_RENDERER));
|
||||
|
||||
// GLint n_exts;
|
||||
// glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts);
|
||||
// for (int i = 0; i < n_exts; i++)
|
||||
// {
|
||||
// LOG("%s", glGetStringi(GL_EXTENSIONS, i));
|
||||
// }
|
||||
|
||||
LOG("Screen Resolution: %dx%d", (int)width, (int)height);
|
||||
|
||||
//zoom = ceilf(width / 2000.f);
|
||||
//zoom = 2;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
#if defined(_WIN32) || defined(__OSX__)
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
|
||||
initShaders();
|
||||
initAssets();
|
||||
initLayout();
|
||||
title_update();
|
||||
|
||||
GLfloat width_range[2];
|
||||
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, width_range);
|
||||
LOG("GL line range: %f - %f", width_range[0], width_range[1]);
|
||||
LOG("Screen Size: %f %f", width, height);
|
||||
|
||||
GLint fb0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fb0);
|
||||
LOG("Default Framebuffer %d", fb0);
|
||||
}
|
||||
|
||||
void App::async_start()
|
||||
{
|
||||
#if __OSX__
|
||||
[osx_view async_lock];
|
||||
#elif __IOS__
|
||||
[ios_view async_lock];
|
||||
#elif __ANDROID__
|
||||
android_async_lock(and_engine);
|
||||
#elif _WIN32
|
||||
async_lock();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::async_update()
|
||||
{
|
||||
#if __IOS__
|
||||
[ios_view->glview bindDrawable];
|
||||
#elif _WIN32
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
#endif
|
||||
redraw = true;
|
||||
clear();
|
||||
update(0);
|
||||
#if __OSX__
|
||||
[osx_view async_swap];
|
||||
#elif __IOS__
|
||||
[ios_view->glview bindDrawable];
|
||||
[ios_view async_swap];
|
||||
#elif __ANDROID__
|
||||
android_async_swap(and_engine);
|
||||
#elif _WIN32
|
||||
async_swap();
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::async_redraw()
|
||||
{
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
void App::async_end()
|
||||
{
|
||||
#if __OSX__
|
||||
[osx_view async_unlock];
|
||||
#elif __IOS__
|
||||
[ios_view async_unlock];
|
||||
#elif __ANDROID__
|
||||
android_async_unlock(and_engine);
|
||||
#elif _WIN32
|
||||
async_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::update(float dt)
|
||||
{
|
||||
static float rec_timer = 0.f;
|
||||
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> _lock(m);
|
||||
|
||||
// update offscreen stuff
|
||||
if (canvas && canvas->m_canvas)
|
||||
canvas->m_canvas->stroke_draw();
|
||||
|
||||
if (!(redraw || animate))
|
||||
return;
|
||||
|
||||
//glClearColor(.1f, .1f, .1f, 1.f);
|
||||
//glViewport(0, 0, (GLsizei)width, (GLsizei)height);
|
||||
//glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
//if (!canvas->m_mouse_captured)
|
||||
{
|
||||
#if _WIN32 || __OSX__
|
||||
layout.reload();
|
||||
#endif
|
||||
if (auto* main = layout[main_id])
|
||||
{
|
||||
main->update(width, height, zoom);
|
||||
stroke->update_controls();
|
||||
}
|
||||
}
|
||||
|
||||
static glm::vec4 color_button_normal{.1, .1, .1, 1};
|
||||
static glm::vec4 color_button_hlight{ 1, .0, .0, 1};
|
||||
CanvasModePen* mode = (CanvasModePen*)canvas->m_canvas->modes[(int)Canvas::kCanvasMode::Draw][0];
|
||||
layout[main_id]->find<NodeButton>("btn-pick")->set_color(
|
||||
mode->m_picking ? color_button_hlight : color_button_normal);
|
||||
layout[main_id]->find<NodeButton>("btn-pen")->set_color(
|
||||
canvas->m_canvas->m_current_mode == Canvas::kCanvasMode::Draw ? color_button_hlight : color_button_normal);
|
||||
layout[main_id]->find<NodeButton>("btn-erase")->set_color(
|
||||
canvas->m_canvas->m_current_mode == Canvas::kCanvasMode::Erase ? color_button_hlight : color_button_normal);
|
||||
layout[main_id]->find<NodeButton>("btn-touchlock")->set_color(
|
||||
canvas->m_canvas->m_touch_lock ? color_button_hlight : color_button_normal);
|
||||
|
||||
|
||||
auto observer = [this](Node* n)
|
||||
{
|
||||
if (n && n->m_display)
|
||||
{
|
||||
auto box = n->m_clip;
|
||||
//glm::ivec4 c = glm::vec4((int)box.x - 1, (int)(height / zoom - box.y - box.w) - 1, (int)box.z + 2, (int)box.w + 2) * zoom;
|
||||
glm::ivec4 c = glm::vec4((int)box.x, (int)(height / zoom - box.y - box.w), (int)box.z, (int)box.w) * zoom;
|
||||
glScissor(c.x, c.y, c.z, c.w);
|
||||
n->draw();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#if __IOS__
|
||||
[ios_view->glview bindDrawable];
|
||||
#else
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
#endif
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
if (auto* main = layout[main_id])
|
||||
main->watch(observer);
|
||||
//msgbox->watch(observer);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
if (rec_running)
|
||||
{
|
||||
rec_timer += dt;
|
||||
if (rec_timer > 1.f && canvas->m_canvas->m_dirty_stroke)
|
||||
{
|
||||
canvas->m_canvas->m_dirty_stroke = false;
|
||||
|
||||
LOG("rec tick");
|
||||
rec_timer = 0.f;
|
||||
|
||||
auto data = new uint8_t[width * height * 4];
|
||||
#if __IOS__
|
||||
[ios_view->glview bindDrawable];
|
||||
#else
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
#endif
|
||||
|
||||
GLint dfbo, rfbo;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &rfbo);
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &dfbo);
|
||||
if (dfbo != rfbo)
|
||||
LOG("DIFFERENT FB");
|
||||
|
||||
glReadBuffer(GL_FRONT);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(rec_mutex);
|
||||
rec_frames.emplace_back(data);
|
||||
rec_cv.notify_all();
|
||||
}
|
||||
update_rec_frames();
|
||||
}
|
||||
}
|
||||
|
||||
redraw = false;
|
||||
}
|
||||
|
||||
|
||||
void App::terminate()
|
||||
{
|
||||
LOG("App::terminate");
|
||||
TextureManager::invalidate();
|
||||
ShaderManager::invalidate();
|
||||
layout.clear_context();
|
||||
brushes->clear_context();
|
||||
layers->clear_context();
|
||||
color->clear_context();
|
||||
stroke->clear_context();
|
||||
rec_stop();
|
||||
}
|
||||
|
||||
void App::update_memory_usage(size_t bytes)
|
||||
{
|
||||
if (auto txt = layout[main_id]->find<NodeText>("txt-memory"))
|
||||
{
|
||||
static char buffer[128];
|
||||
sprintf(buffer, "History memory: %.2f Mb", bytes / 1024.f / 1024.f);
|
||||
txt->set_text(buffer);
|
||||
layout[main_id]->update();
|
||||
}
|
||||
}
|
||||
|
||||
void App::update_rec_frames()
|
||||
{
|
||||
if (auto txt = layout[main_id]->find<NodeText>("txt-rec"))
|
||||
{
|
||||
if (rec_running)
|
||||
{
|
||||
static char buffer[128];
|
||||
sprintf(buffer, "Recorder %d frames", rec_count);
|
||||
txt->set_text(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
txt->set_text("");
|
||||
}
|
||||
layout[main_id]->update();
|
||||
}
|
||||
}
|
||||
|
||||
void App::rec_clear()
|
||||
{
|
||||
rec_stop();
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
NSString *path = [NSString stringWithUTF8String:rec_path.c_str()];
|
||||
NSDirectoryEnumerator* en = [[NSFileManager defaultManager] enumeratorAtPath:path];
|
||||
NSError* err = nil;
|
||||
BOOL res;
|
||||
NSString* file;
|
||||
while (file = [en nextObject])
|
||||
{
|
||||
NSString* file_path = [path stringByAppendingPathComponent:file];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:file_path error:nil];
|
||||
NSLog(@"delete: %@", file_path);
|
||||
}
|
||||
#endif
|
||||
rec_count = 0;
|
||||
update_rec_frames();
|
||||
}
|
||||
|
||||
void App::rec_start()
|
||||
{
|
||||
if (!rec_running)
|
||||
{
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
NSString* path = [NSString stringWithUTF8String:rec_path.c_str()];
|
||||
NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
||||
NSArray *jpgFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self ENDSWITH '.jpg'"]];
|
||||
rec_count = (int)[jpgFiles count];
|
||||
update_rec_frames();
|
||||
rec_thread = std::thread(&App::rec_loop, this);
|
||||
#else
|
||||
rec_count = Asset::list_files(rec_path, false, ".*\\.jpg").size();
|
||||
update_rec_frames();
|
||||
rec_thread = std::thread(&App::rec_loop, this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void App::rec_stop()
|
||||
{
|
||||
if (rec_running)
|
||||
{
|
||||
rec_running = false;
|
||||
rec_cv.notify_all();
|
||||
if (rec_thread.joinable())
|
||||
rec_thread.join();
|
||||
update_rec_frames();
|
||||
}
|
||||
}
|
||||
|
||||
void stillImageDataReleaseCallback(void *releaseRefCon, const void *baseAddress)
|
||||
{
|
||||
free((void *)baseAddress);
|
||||
}
|
||||
|
||||
void App::rec_export(std::string path)
|
||||
{
|
||||
int progress = 0;
|
||||
int tot = rec_count;
|
||||
auto pb = layout[main_id]->add_child<NodeProgressBar>();
|
||||
pb->m_progress->SetWidthP(0);
|
||||
pb->m_title->set_text("Exporting MP4 movie");
|
||||
async_update();
|
||||
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
NSString* mov_path = [NSString stringWithFormat:@"%s/out.mp4", rec_path.c_str()];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:mov_path])
|
||||
{
|
||||
NSLog(@"remove existing mp4");
|
||||
[[NSFileManager defaultManager] removeItemAtPath:mov_path error:nil];
|
||||
}
|
||||
NSURL* url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%s/out.mp4", rec_path.c_str()]];
|
||||
AVAssetWriter* writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:nil];
|
||||
writer.shouldOptimizeForNetworkUse = NO;
|
||||
NSDictionary *videoCompressionSettings = @{
|
||||
AVVideoCodecKey : AVVideoCodecH264,
|
||||
AVVideoWidthKey : @(width),
|
||||
AVVideoHeightKey : @(height),
|
||||
AVVideoCompressionPropertiesKey : @{ AVVideoAverageBitRateKey : @(8<<20) }
|
||||
};
|
||||
if (![writer canApplyOutputSettings:videoCompressionSettings forMediaType:AVMediaTypeVideo])
|
||||
{
|
||||
NSLog(@"Couldn't add asset writer video input.");
|
||||
return;
|
||||
}
|
||||
AVAssetWriterInput* input = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
|
||||
outputSettings:videoCompressionSettings
|
||||
sourceFormatHint:nil];
|
||||
input.expectsMediaDataInRealTime = YES;
|
||||
|
||||
NSDictionary *adaptorDict = @{
|
||||
(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
|
||||
(id)kCVPixelBufferWidthKey : @(width),
|
||||
(id)kCVPixelBufferHeightKey : @(height)
|
||||
};
|
||||
|
||||
AVAssetWriterInputPixelBufferAdaptor* _pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc]
|
||||
initWithAssetWriterInput:input
|
||||
sourcePixelBufferAttributes:adaptorDict];
|
||||
|
||||
|
||||
// Add asset writer input to asset writer
|
||||
if (![writer canAddInput:input]) {
|
||||
NSLog(@"Couldn't add input to writer.");
|
||||
return;
|
||||
}
|
||||
|
||||
[writer addInput:input];
|
||||
|
||||
CMTime t;
|
||||
t.timescale = 30;
|
||||
t.flags = kCMTimeFlags_Valid;
|
||||
t.epoch = 0;
|
||||
t.value = 0;
|
||||
//[writer startSessionAtSourceTime:t];
|
||||
|
||||
CVPixelBufferRef buff = NULL;
|
||||
uint8_t* data = (uint8_t*)calloc(1, width * height * 4);
|
||||
CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, kCVPixelFormatType_32RGBA, data,
|
||||
width * 4, stillImageDataReleaseCallback, nil, nil, &buff);
|
||||
OSStatus err = CVPixelBufferPoolCreatePixelBuffer(nil, _pixelBufferAdaptor.pixelBufferPool, &buff);
|
||||
|
||||
|
||||
if (writer.status == AVAssetWriterStatusUnknown)
|
||||
{
|
||||
// If the asset writer status is unknown, implies writing hasn't started yet, hence start writing with start time as the buffer's presentation timestamp
|
||||
if ([writer startWriting])
|
||||
{
|
||||
[writer startSessionAtSourceTime:t];
|
||||
}
|
||||
}
|
||||
|
||||
if (writer.status == AVAssetWriterStatusWriting)
|
||||
{
|
||||
for (int i = 0; i < tot; i++)
|
||||
{
|
||||
// If the asset writer status is writing, append sample buffer to its corresponding asset writer input
|
||||
if (input.readyForMoreMediaData)
|
||||
{
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/%04d.jpg", rec_path.c_str(), i);
|
||||
NSString* img_path = [NSString stringWithUTF8String:path];
|
||||
NSLog(@"frame: %@", img_path);
|
||||
#if __OSX__
|
||||
NSImage *image = [[NSImage alloc] initWithContentsOfFile:img_path];
|
||||
if (!image)
|
||||
break;
|
||||
NSRect imageRect = NSMakeRect(0, 0, image.size.width, image.size.height);
|
||||
CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:NULL hints:nil];
|
||||
#elif __IOS__
|
||||
UIImage* image = [UIImage imageNamed:img_path];
|
||||
if (!image)
|
||||
break;
|
||||
CGImageRef cgImage = image.CGImage;
|
||||
#endif
|
||||
|
||||
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
|
||||
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
|
||||
CGSize sz = image.size;
|
||||
CVPixelBufferRef pxbuffer = NULL;
|
||||
{
|
||||
CGImageRef image = cgImage;
|
||||
CGSize size = sz;
|
||||
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, &pxbuffer);
|
||||
// CVReturn status = CVPixelBufferPoolCreatePixelBuffer(NULL, adaptor.pixelBufferPool, &pxbuffer);
|
||||
|
||||
//NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
|
||||
|
||||
CVPixelBufferLockBaseAddress(pxbuffer, 0);
|
||||
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
|
||||
//NSParameterAssert(pxdata != NULL);
|
||||
|
||||
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst);
|
||||
//NSParameterAssert(context);
|
||||
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
|
||||
|
||||
CGColorSpaceRelease(rgbColorSpace);
|
||||
CGContextRelease(context);
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
|
||||
}
|
||||
|
||||
t.value = i;
|
||||
if (![_pixelBufferAdaptor appendPixelBuffer:pxbuffer withPresentationTime:t])
|
||||
{
|
||||
NSLog(@"error %@", [writer.error localizedFailureReason]);
|
||||
}
|
||||
CFRelease(pxbuffer);
|
||||
|
||||
progress++;
|
||||
pb->m_progress->SetWidthP((float)progress / tot * 100.f);
|
||||
async_update();
|
||||
}
|
||||
}
|
||||
[input markAsFinished];
|
||||
[writer finishWritingWithCompletionHandler:^{
|
||||
NSString* path = [NSString stringWithFormat:@"%s/out.mp4", rec_path.c_str()];
|
||||
NSLog(@"saved video %@", path);
|
||||
#if __IOS__
|
||||
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path))
|
||||
{
|
||||
NSLog(@"saving to camera roll");
|
||||
UISaveVideoAtPathToSavedPhotosAlbum(path, nil, nil, nil);
|
||||
}
|
||||
#endif
|
||||
}];
|
||||
}
|
||||
|
||||
if (writer.status == AVAssetWriterStatusFailed)
|
||||
{
|
||||
NSLog(@"failed");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
pb->destroy();
|
||||
async_update();
|
||||
|
||||
}
|
||||
|
||||
void App::rec_loop()
|
||||
{
|
||||
rec_running = true;
|
||||
while(rec_running)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(rec_mutex);
|
||||
rec_cv.wait(lock);
|
||||
if (!rec_running)
|
||||
break;
|
||||
if (!rec_frames.empty())
|
||||
{
|
||||
if (rec_frames.front())
|
||||
{
|
||||
auto inverted = std::make_unique<uint8_t[]>(width*height*4);
|
||||
for (int y = height - 1, y1 = 0; y >= 0; y--, y1++)
|
||||
{
|
||||
uint8_t* dst = &inverted[y * width * 4];
|
||||
uint8_t* src = &rec_frames.front()[y1 * width * 4];
|
||||
std::copy_n(src, (int)width * 4, dst);
|
||||
}
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/%04d.jpg", rec_path.c_str(), rec_count);
|
||||
LOG("writing %s", path);
|
||||
jpge::params params;
|
||||
params.m_quality = 75;
|
||||
bool saved = jpge::compress_image_to_jpeg_file(path, width, height, 4, inverted.get(), params);
|
||||
if (!saved)
|
||||
{
|
||||
LOG("error writing the frame");
|
||||
rec_running = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rec_count++;
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
rec_frames.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
165
src/app.h
Normal file
165
src/app.h
Normal file
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include "log.h"
|
||||
#include "shader.h"
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
#include "layout.h"
|
||||
#include "font.h"
|
||||
#include "node_message_box.h"
|
||||
#include "node_settings.h"
|
||||
#include "node_popup_menu.h"
|
||||
#include "node_panel_brush.h"
|
||||
#include "node_panel_layer.h"
|
||||
#include "node_panel_color.h"
|
||||
#include "node_panel_stroke.h"
|
||||
#include "node_scroll.h"
|
||||
#include "node_canvas.h"
|
||||
#include "node_dialog_layer_rename.h"
|
||||
#include "node_progress_bar.h"
|
||||
|
||||
#if defined(__OBJC__) && defined(__IOS__)
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GameViewController.h"
|
||||
#endif
|
||||
|
||||
#if defined(__OBJC__) && defined(__OSX__)
|
||||
#import "main.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "main.h"
|
||||
#endif
|
||||
#include "node_panel_grid.h"
|
||||
|
||||
class App
|
||||
{
|
||||
public:
|
||||
static App I;
|
||||
std::string data_path{ "." };
|
||||
|
||||
std::string rec_path{ "." };
|
||||
std::thread rec_thread;
|
||||
bool rec_running = false;
|
||||
int rec_count = 0;
|
||||
std::mutex rec_mutex;
|
||||
std::condition_variable rec_cv;
|
||||
std::deque<std::unique_ptr<uint8_t[]>> rec_frames;
|
||||
|
||||
Sampler sampler;
|
||||
Texture2D tex;
|
||||
LayoutManager layout;
|
||||
NodeMessageBox* msgbox;
|
||||
NodeSettings* settings;
|
||||
NodePopupMenu* popup = nullptr;
|
||||
NodePopupMenu* menu_file = nullptr;
|
||||
NodePopupMenu* menu_edit = nullptr;
|
||||
NodePopupMenu* menu_layers = nullptr;
|
||||
NodeBorder* sidebar = nullptr;
|
||||
std::shared_ptr<NodePanelBrush> brushes;
|
||||
std::shared_ptr<NodePanelLayer> layers;
|
||||
std::shared_ptr<NodePanelColor> color;
|
||||
std::shared_ptr<NodePanelStroke> stroke;
|
||||
std::shared_ptr<NodePanelGrid> grid;
|
||||
std::shared_ptr<NodePanelBrushPreset> presets;
|
||||
NodeCanvas* canvas;
|
||||
Node* current_panel = nullptr;
|
||||
NodeScroll* panels;
|
||||
const uint16_t main_id = const_hash("main");
|
||||
std::string doc_name = "no-name";
|
||||
float width;
|
||||
float height;
|
||||
bool keys[256];
|
||||
bool redraw = true;
|
||||
bool animate = false;
|
||||
glm::vec2 gesture_p0;
|
||||
glm::vec2 gesture_p1;
|
||||
#ifdef __ANDROID__
|
||||
float zoom = 3.0;
|
||||
#elif __IOS__
|
||||
float zoom = 2.0;
|
||||
#else
|
||||
float zoom = 1.0;
|
||||
#endif // __ANDROID__
|
||||
|
||||
#if defined(__IOS__) && defined(__OBJC__)
|
||||
GameViewController* ios_view;
|
||||
#endif
|
||||
|
||||
#if defined(__OSX__) && defined(__OBJC__)
|
||||
View* osx_view;
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
struct android_app* and_app;
|
||||
struct engine* and_engine;
|
||||
#endif
|
||||
void pick_image(std::function<void(std::string path)> callback);
|
||||
void showKeyboard();
|
||||
void hideKeyboard();
|
||||
void initLog();
|
||||
void init();
|
||||
void initShaders();
|
||||
void initAssets();
|
||||
void initLayout();
|
||||
void create();
|
||||
bool request_close();
|
||||
void terminate();
|
||||
void clear();
|
||||
void update(float dt);
|
||||
void async_start();
|
||||
void async_update();
|
||||
void async_redraw();
|
||||
void async_end();
|
||||
void resize(float w, float h);
|
||||
bool mouse_down(int button, float x, float y, float pressure, kEventSource source);
|
||||
bool mouse_move(float x, float y, float pressure, kEventSource source);
|
||||
bool mouse_up(int button, float x, float y, kEventSource source);
|
||||
bool mouse_scroll(float x, float y, float delta);
|
||||
bool mouse_cancel(int button);
|
||||
bool gesture_start(const glm::vec2& p0, const glm::vec2& p1);
|
||||
bool gesture_move(const glm::vec2& p0, const glm::vec2& p1);
|
||||
bool gesture_end();
|
||||
bool key_down(kKey key);
|
||||
bool key_up(kKey key);
|
||||
bool key_char(char key);
|
||||
void toggle_ui();
|
||||
|
||||
void rec_clear();
|
||||
void rec_loop();
|
||||
void rec_start();
|
||||
void rec_stop();
|
||||
void rec_export(std::string path);
|
||||
|
||||
void init_toolbar_main();
|
||||
void init_toolbar_draw();
|
||||
void init_sidebar();
|
||||
void init_menu_file();
|
||||
void init_menu_edit();
|
||||
void init_menu_layer();
|
||||
void init_menu_timelapse();
|
||||
void dialog_newdoc();
|
||||
void dialog_save();
|
||||
void dialog_save_ver();
|
||||
void dialog_open();
|
||||
void dialog_browse();
|
||||
void dialog_export();
|
||||
void dialog_export_cubes();
|
||||
void dialog_layer_rename();
|
||||
|
||||
void cloud_upload();
|
||||
void cloud_upload_all();
|
||||
void cloud_browse();
|
||||
void upload(std::string filename, std::string name = "", std::function<void(float)> progress = nullptr);
|
||||
void download(std::string filename, std::function<void(float)> progress = nullptr);
|
||||
bool check_license();
|
||||
|
||||
std::shared_ptr<NodeProgressBar> show_progress(const std::string& title);
|
||||
|
||||
void brush_update();
|
||||
void title_update();
|
||||
void update_memory_usage(size_t bytes);
|
||||
void update_rec_frames();
|
||||
|
||||
void cmd_convert(std::string pano_path, std::string out_path);
|
||||
};
|
||||
161
src/app_cloud.cpp
Normal file
161
src/app_cloud.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "util.h"
|
||||
#include "node_progress_bar.h"
|
||||
#include "node_dialog_cloud.h"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
void App::cloud_upload()
|
||||
{
|
||||
if (!canvas)
|
||||
return;
|
||||
if (ui::Canvas::I->m_newdoc)
|
||||
{
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Warning");
|
||||
msgbox->m_message->set_text("This document needs to be saved before upload.");
|
||||
layout[main_id]->add_child(msgbox);
|
||||
layout[main_id]->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::thread([this] {
|
||||
std::string path = data_path + "/" + doc_name + ".pano";
|
||||
if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
Canvas::I->project_save_thread(path);
|
||||
}
|
||||
|
||||
async_start();
|
||||
auto pb = show_progress("Uploading");
|
||||
async_redraw();
|
||||
async_end();
|
||||
|
||||
upload(path, doc_name + ".pano", [this,pb](float p){
|
||||
async_start();
|
||||
pb->m_progress->SetWidthP(p * 100.f);
|
||||
async_redraw();
|
||||
async_end();
|
||||
});
|
||||
|
||||
async_start();
|
||||
pb->destroy();
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Success");
|
||||
msgbox->m_message->set_text("This document has been succesfully uploaded.");
|
||||
layout[main_id]->add_child(msgbox);
|
||||
layout[main_id]->update();
|
||||
async_redraw();
|
||||
async_end();
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
void App::cloud_upload_all()
|
||||
{
|
||||
std::thread([this] {
|
||||
auto names = Asset::list_files(data_path, false, ".*\\.pano");
|
||||
|
||||
gl_state gl;
|
||||
std::shared_ptr<NodeProgressBar> pb;
|
||||
if (layout.m_loaded)
|
||||
{
|
||||
async_start();
|
||||
pb = show_progress("Export Pano Image");
|
||||
async_redraw();
|
||||
async_end();
|
||||
}
|
||||
|
||||
int progress = 0;
|
||||
int total = names.size();
|
||||
|
||||
for (const auto& n : names)
|
||||
{
|
||||
std::string path = data_path + "/" + n;
|
||||
upload(path);
|
||||
|
||||
progress++;
|
||||
float p = (float)progress / total * 100.f;
|
||||
LOG("progress: %f", p);
|
||||
|
||||
if (layout.m_loaded)
|
||||
{
|
||||
async_start();
|
||||
pb->m_progress->SetWidthP(p);
|
||||
gl.save();
|
||||
async_redraw();
|
||||
gl.restore();
|
||||
async_end();
|
||||
}
|
||||
}
|
||||
|
||||
if (layout.m_loaded)
|
||||
{
|
||||
async_start();
|
||||
pb->destroy();
|
||||
async_redraw();
|
||||
async_end();
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void App::cloud_browse()
|
||||
{
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
// load thumbnail test
|
||||
auto dialog = std::make_shared<NodeDialogCloud>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->data_path = data_path;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
dialog->destroy();
|
||||
std::thread([this, dialog] {
|
||||
async_start();
|
||||
auto* m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Downloading");
|
||||
m->m_message->set_text("Download in progress");
|
||||
async_redraw();
|
||||
async_end();
|
||||
|
||||
download(dialog->selected_file, [this,m](float p){
|
||||
static char progress[256];
|
||||
sprintf(progress, "Download in progress %.2f%%", p * 100.f);
|
||||
async_start();
|
||||
m->m_message->set_text(progress);
|
||||
async_redraw();
|
||||
async_end();
|
||||
});
|
||||
|
||||
async_start();
|
||||
canvas->reset_camera();
|
||||
layers->clear();
|
||||
async_end();
|
||||
|
||||
canvas->m_canvas->project_open_thread(dialog->selected_path);
|
||||
|
||||
async_start();
|
||||
doc_name = dialog->selected_name;
|
||||
title_update();
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
ActionManager::clear();
|
||||
m->destroy();
|
||||
async_redraw();
|
||||
async_end();
|
||||
}).detach();
|
||||
};
|
||||
}
|
||||
16
src/app_commands.cpp
Normal file
16
src/app_commands.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "canvas.h"
|
||||
|
||||
void App::cmd_convert(std::string pano_path, std::string out_path)
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
|
||||
ui::Canvas* canvas = new ui::Canvas;
|
||||
canvas->create(CANVAS_RES, CANVAS_RES);
|
||||
canvas->project_open_thread(pano_path);
|
||||
canvas->export_equirectangular_thread(out_path);
|
||||
}
|
||||
381
src/app_dialogs.cpp
Normal file
381
src/app_dialogs.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "node_dialog_open.h"
|
||||
#include "node_dialog_browse.h"
|
||||
#include "node_dialog_cloud.h"
|
||||
|
||||
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title)
|
||||
{
|
||||
auto pb = std::make_shared<NodeProgressBar>();
|
||||
pb->m_manager = &layout;
|
||||
pb->init();
|
||||
pb->create();
|
||||
pb->loaded();
|
||||
pb->m_progress->SetWidthP(0);
|
||||
pb->m_title->set_text(title.c_str());
|
||||
layout[main_id]->add_child(pb);
|
||||
return pb;
|
||||
}
|
||||
|
||||
void App::dialog_newdoc()
|
||||
{
|
||||
auto show_dialog = [this] {
|
||||
async_start();
|
||||
auto dialog = std::make_shared<NodeDialogNewDoc>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
dialog->input->set_text("name");
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
|
||||
App::I.showKeyboard();
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
std::string name = dialog->input->m_string;
|
||||
std::string path = data_path + "/" + name + ".pano";
|
||||
|
||||
auto action = [this, dialog, name] {
|
||||
std::array<int, 4> resolutions{ 512, 1024, 1536, 2048 };
|
||||
int res = resolutions[dialog->m_resolution->m_current_index];
|
||||
doc_name = name;
|
||||
|
||||
layers->clear();
|
||||
canvas->m_canvas->m_layers.clear();
|
||||
canvas->m_canvas->m_order.clear();
|
||||
canvas->m_canvas->resize(res, res);
|
||||
canvas->reset_camera();
|
||||
ActionManager::clear();
|
||||
|
||||
canvas->m_canvas->layer_add("Default");
|
||||
layers->add_layer("Default");
|
||||
|
||||
title_update();
|
||||
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
|
||||
if (Asset::exist(path, false))
|
||||
{
|
||||
// ask confirm is file already exist
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Warning");
|
||||
msgbox->m_message->set_text("A document with this name already exists, continue?");
|
||||
msgbox->btn_ok->on_click = [this, msgbox, action](Node*) {
|
||||
action();
|
||||
msgbox->destroy();
|
||||
};
|
||||
layout[main_id]->add_child(msgbox);
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
};
|
||||
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
||||
{
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
async_end();
|
||||
};
|
||||
|
||||
if (canvas)
|
||||
{
|
||||
if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Would you like to save this document before closing?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_ok->on_click = [this, m, show_dialog](Node*) {
|
||||
ui::Canvas::I->project_save([this, m, show_dialog] {
|
||||
show_dialog();
|
||||
});
|
||||
m->destroy();
|
||||
};
|
||||
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
||||
show_dialog();
|
||||
m->destroy();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
show_dialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_open()
|
||||
{
|
||||
auto show_dialog = [this] {
|
||||
async_start();
|
||||
// load thumbnail test
|
||||
auto dialog = std::make_shared<NodeDialogOpen>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->data_path = data_path;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
canvas->reset_camera();
|
||||
layers->clear();
|
||||
doc_name = dialog->selected_name;
|
||||
canvas->m_canvas->project_open(dialog->selected_path, [this] {
|
||||
// on complete
|
||||
async_start();
|
||||
title_update();
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
async_end();
|
||||
});
|
||||
dialog->destroy();
|
||||
ActionManager::clear();
|
||||
};
|
||||
async_end();
|
||||
};
|
||||
|
||||
if (canvas)
|
||||
{
|
||||
if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Would you like to save this document before closing?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_ok->on_click = [this,m,show_dialog](Node*){
|
||||
ui::Canvas::I->project_save([this,m,show_dialog] {
|
||||
show_dialog();
|
||||
});
|
||||
m->destroy();
|
||||
};
|
||||
m->btn_cancel->on_click = [this,m,show_dialog](Node*) {
|
||||
show_dialog();
|
||||
m->destroy();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
show_dialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_browse()
|
||||
{
|
||||
auto show_dialog = [this] {
|
||||
async_start();
|
||||
// load thumbnail test
|
||||
auto dialog = std::make_shared<NodeDialogBrowse>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->data_path = data_path;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
canvas->reset_camera();
|
||||
layers->clear();
|
||||
doc_name = dialog->selected_name;
|
||||
canvas->m_canvas->project_open(dialog->selected_path, [this] {
|
||||
// on complete
|
||||
async_start();
|
||||
title_update();
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
async_end();
|
||||
});
|
||||
dialog->destroy();
|
||||
ActionManager::clear();
|
||||
};
|
||||
async_end();
|
||||
};
|
||||
|
||||
if (canvas)
|
||||
{
|
||||
if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Would you like to save this document before closing?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_ok->on_click = [this, m, show_dialog](Node*) {
|
||||
ui::Canvas::I->project_save([this, m, show_dialog] {
|
||||
show_dialog();
|
||||
});
|
||||
m->destroy();
|
||||
};
|
||||
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
||||
show_dialog();
|
||||
m->destroy();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
show_dialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_save_ver()
|
||||
{
|
||||
int current = 0;
|
||||
std::string next = doc_name + ".01";
|
||||
std::string base = doc_name;
|
||||
|
||||
std::regex r(R"((.*)\.(\w{2})$)");
|
||||
std::smatch m;
|
||||
if (std::regex_search(doc_name, m, r))
|
||||
{
|
||||
base = m[1].str();
|
||||
current = atoi(m[2].str().c_str());
|
||||
}
|
||||
|
||||
for (int i = current + 1; i < 99; i++)
|
||||
{
|
||||
static char tmp_name[256];
|
||||
sprintf(tmp_name, "%s.%02d", base.c_str(), i);
|
||||
next = tmp_name;
|
||||
if (Asset::exist(data_path + "/" + next + ".pano", false))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
doc_name = next;
|
||||
title_update();
|
||||
canvas->m_canvas->project_save(data_path + "/" + next + ".pano");
|
||||
}
|
||||
|
||||
void App::dialog_save()
|
||||
{
|
||||
if (canvas)
|
||||
{
|
||||
auto dialog = std::make_shared<NodeDialogSave>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
dialog->input->set_text(doc_name);
|
||||
|
||||
App::I.showKeyboard();
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
std::string name = dialog->input->m_string;
|
||||
std::string path = data_path + "/" + name + ".pano";
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Warning");
|
||||
msgbox->m_message->set_text("You need to specify a name to file.");
|
||||
layout[main_id]->add_child(msgbox);
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = [this, dialog, name, path] {
|
||||
doc_name = name;
|
||||
title_update();
|
||||
canvas->m_canvas->project_save(path);
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
|
||||
if (Asset::exist(path, false))
|
||||
{
|
||||
// ask confirm is file already exist
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Warning");
|
||||
msgbox->m_message->set_text(("Are you sure you want to overwrite " + name + "?").c_str());
|
||||
msgbox->btn_ok->on_click = [this, msgbox, action](Node*) {
|
||||
action();
|
||||
msgbox->destroy();
|
||||
};
|
||||
layout[main_id]->add_child(msgbox);
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
};
|
||||
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
||||
{
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_export()
|
||||
{
|
||||
if (canvas)
|
||||
{
|
||||
canvas->m_canvas->export_equirectangular(data_path + "/" + doc_name + ".jpg");
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_export_cubes()
|
||||
{
|
||||
if (canvas)
|
||||
{
|
||||
canvas->m_canvas->export_cubes(data_path + "/" + doc_name);
|
||||
}
|
||||
}
|
||||
|
||||
void App::dialog_layer_rename()
|
||||
{
|
||||
auto dialog = std::make_shared<NodeDialogLayerRename>();
|
||||
dialog->m_manager = &layout;
|
||||
dialog->data_path = data_path;
|
||||
dialog->init();
|
||||
dialog->create();
|
||||
dialog->loaded();
|
||||
dialog->input->set_text(layers->m_current_layer->m_label_text);
|
||||
|
||||
App::I.showKeyboard();
|
||||
|
||||
layout[main_id]->add_child(dialog);
|
||||
layout[main_id]->update();
|
||||
|
||||
dialog->btn_ok->on_click = [this,dialog](Node*)
|
||||
{
|
||||
layers->m_current_layer->set_name(dialog->get_name().c_str());
|
||||
canvas->m_canvas->m_layers[canvas->m_canvas->m_current_layer_idx].m_name = dialog->get_name();
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
||||
{
|
||||
dialog->destroy();
|
||||
App::I.hideKeyboard();
|
||||
};
|
||||
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
}
|
||||
199
src/app_events.cpp
Normal file
199
src/app_events.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
void displayKeyboard(android_app* mApplication, bool pShow);
|
||||
#elif _WIN32
|
||||
std::string win32_open_file();
|
||||
#endif
|
||||
|
||||
|
||||
using namespace ui;
|
||||
|
||||
void App::resize(float w, float h)
|
||||
{
|
||||
redraw = true;
|
||||
width = w;
|
||||
height = h;
|
||||
if (auto* main = layout[main_id])
|
||||
main->update(w , h, zoom);
|
||||
}
|
||||
|
||||
void App::showKeyboard()
|
||||
{
|
||||
LOG("show keyboard");
|
||||
redraw = true;
|
||||
#ifdef __IOS__
|
||||
[ios_view registerForKeyboardNotifications];
|
||||
[ios_view becomeFirstResponder];
|
||||
#elif __ANDROID__
|
||||
displayKeyboard(and_app, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::hideKeyboard()
|
||||
{
|
||||
LOG("hide keyboard");
|
||||
redraw = true;
|
||||
#ifdef __IOS__
|
||||
[ios_view resignFirstResponder];
|
||||
// [ios_view unregisterForKeyboardNotifications];
|
||||
#elif __ANDROID__
|
||||
displayKeyboard(and_app, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::pick_image(std::function<void(std::string path)> callback)
|
||||
{
|
||||
redraw = true;
|
||||
#ifdef __IOS__
|
||||
[ios_view pick_photo:callback];
|
||||
#elif __OSX__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
std::string path = [osx_view pick_file];
|
||||
if (!path.empty())
|
||||
callback(path);
|
||||
});
|
||||
#elif __ANDROID__
|
||||
//displayKeyboard(and_app, false);
|
||||
#elif _WIN32
|
||||
std::string path = win32_open_file();
|
||||
if (!path.empty())
|
||||
callback(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source)
|
||||
{
|
||||
redraw = true;
|
||||
MouseEvent e;
|
||||
e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pressure = pressure;
|
||||
e.m_source = source;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_move(float x, float y, float pressure, kEventSource source)
|
||||
{
|
||||
redraw = true;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseMove;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_pressure = pressure;
|
||||
e.m_source = source;
|
||||
kEventResult ret = kEventResult::Available;
|
||||
if (auto* main = layout[main_id])
|
||||
ret = main->on_event(&e);
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_up(int button, float x, float y, kEventSource source)
|
||||
{
|
||||
redraw = true;
|
||||
MouseEvent e;
|
||||
e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_source = source;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_scroll(float x, float y, float delta)
|
||||
{
|
||||
redraw = true;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseScroll;
|
||||
e.m_pos = { x / zoom, y / zoom };
|
||||
e.m_scroll_delta = delta;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::mouse_cancel(int button)
|
||||
{
|
||||
redraw = true;
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseCancel;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1)
|
||||
{
|
||||
redraw = true;
|
||||
GestureEvent e;
|
||||
glm::vec2 p = glm::lerp(p0, p1, 0.5f);
|
||||
e.m_type = kEventType::GestureStart;
|
||||
e.m_pos = p / glm::vec2(zoom);
|
||||
e.m_distance = glm::distance(p0, p1);
|
||||
gesture_p0 = p0;
|
||||
gesture_p1 = p1;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1)
|
||||
{
|
||||
redraw = true;
|
||||
GestureEvent e;
|
||||
glm::vec2 p = glm::lerp(p0, p1, 0.5f);
|
||||
e.m_type = kEventType::GestureMove;
|
||||
e.m_pos = p / glm::vec2(zoom);
|
||||
e.m_distance = glm::distance(p0, p1);
|
||||
e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1);
|
||||
e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f);
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::gesture_end()
|
||||
{
|
||||
redraw = true;
|
||||
GestureEvent e;
|
||||
e.m_type = kEventType::GestureEnd;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_down(kKey key)
|
||||
{
|
||||
redraw = true;
|
||||
keys[(int)key] = true;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyDown;
|
||||
e.m_key = key;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_up(kKey key)
|
||||
{
|
||||
redraw = true;
|
||||
keys[(int)key] = false;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyUp;
|
||||
e.m_key = key;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
bool App::key_char(char key)
|
||||
{
|
||||
redraw = true;
|
||||
KeyEvent e;
|
||||
e.m_type = kEventType::KeyChar;
|
||||
e.m_char = key;
|
||||
auto ret = layout[main_id]->on_event(&e);
|
||||
layout[main_id]->update();
|
||||
return ret == kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void App::toggle_ui()
|
||||
{
|
||||
static bool fullscreen = false;
|
||||
auto m = layout[main_id]->m_children[0];
|
||||
for (int i = 2; i < m->m_children.size(); i++)
|
||||
m->m_children[i]->m_display = fullscreen;
|
||||
fullscreen = !fullscreen;
|
||||
}
|
||||
743
src/app_layout.cpp
Normal file
743
src/app_layout.cpp
Normal file
@@ -0,0 +1,743 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "node_icon.h"
|
||||
#include "node_dialog_open.h"
|
||||
#include "node_text.h"
|
||||
#include "node_progress_bar.h"
|
||||
#include "node_dialog_picker.h"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
static glm::vec4 color_button_normal{ .1, .1, .1, 1 };
|
||||
static glm::vec4 color_button_hlight{ 1, .0, .0, 1 };
|
||||
|
||||
void App::title_update()
|
||||
{
|
||||
static char str[256];
|
||||
snprintf(str, 256, "Panodoc: %s%s (%dpx)", doc_name.c_str(), canvas->m_canvas->m_unsaved ? "*" : "", canvas->m_canvas->m_width);
|
||||
if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
|
||||
docname->set_text(str);
|
||||
}
|
||||
|
||||
void App::init_toolbar_main()
|
||||
{
|
||||
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-export"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
if (canvas)
|
||||
{
|
||||
canvas->m_canvas->export_equirectangular(data_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-anim"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
if (canvas)
|
||||
{
|
||||
canvas->m_canvas->export_anim(data_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-open"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
dialog_open();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-save"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
dialog_save();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-undo"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
ActionManager::undo();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-redo"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
ActionManager::redo();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-clean-memory"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
ActionManager::clear();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-clear"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
//exit(0);
|
||||
if (canvas)
|
||||
canvas->m_canvas->clear({ 0, 0, 0, 0 });
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-popup"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = &layout;
|
||||
msgbox->init();
|
||||
layout[main_id]->add_child(msgbox);
|
||||
layout[main_id]->update();
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-settings"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
settings = new NodeSettings();
|
||||
settings->m_manager = &layout;
|
||||
settings->init();
|
||||
layout[main_id]->add_child(settings);
|
||||
layout[main_id]->update();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::init_sidebar()
|
||||
{
|
||||
sidebar = layout[main_id]->find<NodeBorder>("sidebar");
|
||||
panels = layout[main_id]->find<NodeScroll>("panels");
|
||||
canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");
|
||||
canvas->data_path = data_path;
|
||||
|
||||
//brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
|
||||
//layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
|
||||
//color = layout[main_id]->find<NodePanelColor>("panel-color");
|
||||
//stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");
|
||||
|
||||
brushes = std::make_shared<NodePanelBrush>();
|
||||
brushes->m_manager = &layout;
|
||||
brushes->init();
|
||||
brushes->create();
|
||||
brushes->loaded();
|
||||
|
||||
layers = std::make_shared<NodePanelLayer>();
|
||||
layers->m_manager = &layout;
|
||||
layers->init();
|
||||
layers->create();
|
||||
layers->loaded();
|
||||
|
||||
color = std::make_shared<NodePanelColor>();
|
||||
color->m_manager = &layout;
|
||||
color->init();
|
||||
color->create();
|
||||
color->loaded();
|
||||
|
||||
stroke = std::make_shared<NodePanelStroke>();
|
||||
stroke->m_manager = &layout;
|
||||
stroke->init();
|
||||
stroke->create();
|
||||
stroke->loaded();
|
||||
|
||||
grid = std::make_shared<NodePanelGrid>();
|
||||
grid->m_manager = &layout;
|
||||
grid->init();
|
||||
grid->create();
|
||||
grid->loaded();
|
||||
|
||||
presets = std::make_shared<NodePanelBrushPreset>();
|
||||
presets->m_manager = &layout;
|
||||
presets->init();
|
||||
presets->create();
|
||||
presets->loaded();
|
||||
|
||||
// if (canvas)
|
||||
// {
|
||||
// ui::Canvas::I->m_current_brush.m_tip_color = color->m_color;
|
||||
// stroke->m_canvas->draw_stroke();
|
||||
// }
|
||||
|
||||
brushes->on_brush_changed = [this](Node* target, int index) {
|
||||
ui::Canvas::I->m_current_brush.m_tex_id = brushes->get_texture_id(index);
|
||||
ui::Canvas::I->m_current_brush.id = brushes->get_brush_id(index);
|
||||
stroke->m_preview->draw_stroke();
|
||||
};
|
||||
presets->on_brush_changed = [this](Node* target, int index) {
|
||||
auto b = presets->get_brush(index);
|
||||
// don't change some params
|
||||
b.m_tip_size = ui::Canvas::I->m_current_brush.m_tip_size;
|
||||
b.m_tip_color = ui::Canvas::I->m_current_brush.m_tip_color;
|
||||
ui::Canvas::I->m_current_brush = b;
|
||||
stroke->m_preview->draw_stroke();
|
||||
};
|
||||
|
||||
color->on_color_changed = [this](Node* target, glm::vec4 color) {
|
||||
ui::Canvas::I->m_current_brush.m_tip_color = color;
|
||||
};
|
||||
//
|
||||
// stroke->on_stroke_change = [this](Node*target) {
|
||||
// if (canvas)
|
||||
// canvas->m_brush = stroke->m_canvas->m_brush;
|
||||
// };
|
||||
|
||||
layers->on_layer_add = [this](Node*) {
|
||||
canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str());
|
||||
};
|
||||
|
||||
layers->on_layer_change = [this](Node*, int old_idx, int new_idx) {
|
||||
canvas->m_canvas->m_current_layer_idx = canvas->m_canvas->m_order[new_idx];
|
||||
};
|
||||
|
||||
layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
|
||||
canvas->m_canvas->layer_order(old_idx, new_idx);
|
||||
};
|
||||
|
||||
layers->on_layer_delete = [this](Node*, int idx) {
|
||||
canvas->m_canvas->layer_remove(canvas->m_canvas->m_order[idx]);
|
||||
};
|
||||
|
||||
layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
|
||||
canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_opacity = value;
|
||||
};
|
||||
|
||||
layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
|
||||
canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = visible;
|
||||
};
|
||||
|
||||
layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
|
||||
canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_hightlight = highlight;
|
||||
};
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-stroke"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(stroke.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-brush"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-brush-preset"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-color"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(color.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
// auto pick = layout[main_id]->add_child<NodeColorPicker>();
|
||||
// pick->m_color_cur->m_color = ui::Canvas::I->m_current_brush.m_tip_color;
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-layer"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(layers.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-grids-panel"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
panels->get_child_index(grid.get()) == -1 ? panels->add_child(grid) : panels->remove_child(grid.get());
|
||||
panels->fix_scroll();
|
||||
button->set_color(panels->get_child_index(grid.get()) == -1 ? color_button_normal : color_button_hlight);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::init_toolbar_draw()
|
||||
{
|
||||
static auto select_button = [] (Node* main, NodeButton* button) {
|
||||
main->find<NodeButton>("btn-pen")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-erase")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-line")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-cam")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-grid")->set_color(color_button_normal);
|
||||
//main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-mask-free")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-mask-line")->set_color(color_button_normal);
|
||||
button->set_color(color_button_hlight);
|
||||
};
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-pen"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Draw);
|
||||
};
|
||||
layout[main_id]->find<NodeButton>("btn-pen")->set_color(color_button_hlight);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Draw);
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-pick"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
CanvasModePen* mode = (CanvasModePen*)canvas->m_canvas->modes[(int)Canvas::kCanvasMode::Draw][0];
|
||||
if (mode && canvas->m_canvas->m_current_mode == Canvas::kCanvasMode::Draw)
|
||||
{
|
||||
mode->m_picking = !mode->m_picking;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-touchlock"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
canvas->m_canvas->m_touch_lock = !canvas->m_canvas->m_touch_lock;
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-erase"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Erase);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-line"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Line);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-cam"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Camera);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-grid"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Grid);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-fill"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Fill);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-mask-free"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::MaskFree);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-mask-line"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
Canvas::set_mode(Canvas::kCanvasMode::MaskLine);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-bucket"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
canvas->m_canvas->clear(ui::Canvas::I->m_current_brush.m_tip_color);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::init_menu_file()
|
||||
{
|
||||
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-file"))
|
||||
{
|
||||
menu_file->on_click = [=](Node*) {
|
||||
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
|
||||
popup = (NodePopupMenu*)layout[const_hash("file-menu")]->m_children[0]->clone();
|
||||
popup->update();
|
||||
if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
|
||||
pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(pos.x, pos.y);
|
||||
layout[main_id]->add_child(popup);
|
||||
layout[main_id]->update();
|
||||
popup->mouse_capture();
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
|
||||
popup->find<NodeButtonCustom>("file-newdoc")->on_click = [this](Node*) {
|
||||
dialog_newdoc();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-import")->on_click = [this](Node*) {
|
||||
App::I.pick_image([](std::string path){
|
||||
Canvas::I->import_equirectangular(path);
|
||||
});
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-open")->on_click = [this](Node*) {
|
||||
dialog_open();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-browse")->on_click = [this](Node*) {
|
||||
dialog_browse();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-save")->on_click = [this](Node*) {
|
||||
if (ui::Canvas::I->m_newdoc)
|
||||
{
|
||||
dialog_save();
|
||||
}
|
||||
else if(ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
canvas->m_canvas->project_save();
|
||||
}
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-save-as")->on_click = [this](Node*) {
|
||||
dialog_save();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-save-ver")->on_click = [this](Node*) {
|
||||
ui::Canvas::I->m_newdoc ? dialog_save() : dialog_save_ver();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-export")->on_click = [this](Node*) {
|
||||
dialog_export();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-export-cubes")->on_click = [this](Node*) {
|
||||
dialog_export_cubes();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-cloud-upload")->on_click = [this](Node*) {
|
||||
cloud_upload();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
popup->find<NodeButtonCustom>("file-cloud-browse")->on_click = [this](Node*) {
|
||||
cloud_browse();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::init_menu_edit()
|
||||
{
|
||||
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-edit"))
|
||||
{
|
||||
menu_file->on_click = [=](Node*) {
|
||||
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
|
||||
popup = (NodePopupMenu*)layout[const_hash("edit-menu")]->m_children[0]->clone();
|
||||
popup->update();
|
||||
if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
|
||||
pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(pos.x, pos.y);
|
||||
layout[main_id]->add_child(popup);
|
||||
layout[main_id]->update();
|
||||
popup->mouse_capture();
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::init_menu_timelapse()
|
||||
{
|
||||
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-timelapse"))
|
||||
{
|
||||
menu_file->on_click = [=](Node*) {
|
||||
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
|
||||
popup = (NodePopupMenu*)layout[const_hash("timelapse-menu")]->m_children[0]->clone();
|
||||
popup->update();
|
||||
if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
|
||||
pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(pos.x, pos.y);
|
||||
layout[main_id]->add_child(popup);
|
||||
layout[main_id]->update();
|
||||
popup->mouse_capture();
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
|
||||
if (auto item = popup->find<NodeButtonCustom>("timelapse-start"))
|
||||
{
|
||||
if (auto text = popup->find<NodeText>("menu-label"))
|
||||
{
|
||||
text->set_text(App::I.rec_running ? "Stop Recording" : "Start Recording");
|
||||
}
|
||||
}
|
||||
|
||||
popup->find<NodeButtonCustom>("timelapse-start")->on_click = [this](Node*) {
|
||||
App::I.rec_running ? App::I.rec_stop() : App::I.rec_start();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
|
||||
popup->find<NodeButtonCustom>("timelapse-clear")->on_click = [this](Node*) {
|
||||
App::I.rec_clear();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
|
||||
popup->find<NodeButtonCustom>("timelapse-export")->on_click = [this](Node*) {
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
App::I.rec_export("");
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::brush_update()
|
||||
{
|
||||
// brushes->select_brush(canvas->m_brush.id);
|
||||
// stroke->set_params(canvas->m_brush);
|
||||
}
|
||||
|
||||
void App::init_menu_layer()
|
||||
{
|
||||
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-layers"))
|
||||
{
|
||||
menu_file->on_click = [=](Node*) {
|
||||
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
|
||||
popup = (NodePopupMenu*)layout[const_hash("layers-menu")]->m_children[0]->clone();
|
||||
popup->update();
|
||||
if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
|
||||
pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(pos.x, pos.y);
|
||||
layout[main_id]->add_child(popup);
|
||||
layout[main_id]->update();
|
||||
popup->mouse_capture();
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
popup->find<NodeButtonCustom>("clear-grids")->on_click = [this](Node*) {
|
||||
CanvasModeGrid* mode = (CanvasModeGrid*)ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid][0];
|
||||
mode->clear();
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
|
||||
popup->find<NodeButtonCustom>("layer-clear")->on_click = [this](Node*) {
|
||||
canvas->m_canvas->clear();
|
||||
};
|
||||
if (layers->m_current_layer)
|
||||
popup->find<NodeButtonCustom>("layer-clear")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text(("Clear Layer " + layers->m_current_layer->m_label_text).c_str());
|
||||
|
||||
popup->find<NodeButtonCustom>("layer-rename")->on_click = [this](Node*) {
|
||||
dialog_layer_rename();
|
||||
};
|
||||
if (layers->m_current_layer)
|
||||
popup->find<NodeButtonCustom>("layer-rename")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text(("Rename Layer " + layers->m_current_layer->m_label_text).c_str());
|
||||
else
|
||||
popup->find<NodeButtonCustom>("layer-rename")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text("Rename Layer (Select a layer)");
|
||||
|
||||
popup->find<NodeButtonCustom>("layer-merge")->on_click = [this](Node*) {
|
||||
const auto& order = canvas->m_canvas->m_order;
|
||||
//layers->get_child_index(layers->)
|
||||
int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx));
|
||||
if (current_idx_order > 0)
|
||||
{
|
||||
int dest_layer_idx = order[current_idx_order - 1];
|
||||
canvas->m_canvas->layer_merge(canvas->m_canvas->m_current_layer_idx, dest_layer_idx);
|
||||
canvas->m_canvas->layer_remove(current_idx_order);
|
||||
layers->clear();
|
||||
for (auto& i : canvas->m_canvas->m_order)
|
||||
layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
|
||||
layers->m_current_layer->m_selected = false;
|
||||
layers->m_current_layer = layers->m_layers[current_idx_order - 1];
|
||||
layers->m_current_layer->m_selected = true;
|
||||
layers->m_current_layer->on_selected(layers->m_current_layer);
|
||||
}
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
};
|
||||
if (layers->m_current_layer)
|
||||
{
|
||||
const auto& order = canvas->m_canvas->m_order;
|
||||
int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx));
|
||||
if (current_idx_order > 0)
|
||||
{
|
||||
int down_layer_idx = order[current_idx_order - 1];
|
||||
popup->find<NodeButtonCustom>("layer-merge")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text(("Merge with " + canvas->m_canvas->m_layers[down_layer_idx].m_name).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
popup->find<NodeButtonCustom>("layer-merge")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text("Merge Layer (Select upper layers)");
|
||||
}
|
||||
}
|
||||
else
|
||||
popup->find<NodeButtonCustom>("layer-merge")->
|
||||
find<NodeText>("menu-label")->
|
||||
set_text("Merge Layer (Select a layer)");
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void App::initLayout()
|
||||
{
|
||||
LOG("initializing layout statics");
|
||||
NodeBorder::static_init();
|
||||
NodeImage::static_init();
|
||||
NodeIcon::static_init();
|
||||
NodeStrokePreview::static_init();
|
||||
|
||||
layout.on_loaded = [&] {
|
||||
LOG("initializing layout updating after load");
|
||||
layout[main_id]->update(width, height, zoom);
|
||||
|
||||
LOG("initializing layout components");
|
||||
|
||||
init_sidebar();
|
||||
|
||||
canvas->m_canvas->layer_add("Default");
|
||||
layers->add_layer("Default");
|
||||
|
||||
init_toolbar_draw();
|
||||
init_toolbar_main();
|
||||
init_menu_file();
|
||||
init_menu_edit();
|
||||
init_menu_layer();
|
||||
init_menu_timelapse();
|
||||
|
||||
// set version string
|
||||
if (auto* version_label = layout[main_id]->find<NodeText>("version"))
|
||||
{
|
||||
version_label->set_text(g_version);
|
||||
}
|
||||
|
||||
|
||||
if (auto* menu_entry = layout[main_id]->find<NodeButtonCustom>("menu-about"))
|
||||
{
|
||||
menu_entry->on_click = [=](Node*) {
|
||||
// int x = 0;
|
||||
// sin(time(0) / x);
|
||||
};
|
||||
}
|
||||
|
||||
Brush b;
|
||||
int br_idx = brushes->find_brush("Round-Hard");
|
||||
b.m_tex_id = brushes->get_texture_id(br_idx);
|
||||
b.id = brushes->get_brush_id(br_idx);
|
||||
b.m_tip_size = .1f;
|
||||
b.m_tip_flow = .5f;
|
||||
b.m_tip_spacing = .1f;
|
||||
b.m_tip_opacity = 1.f;
|
||||
ui::Canvas::I->m_current_brush = b;
|
||||
|
||||
brush_update();
|
||||
|
||||
TextureManager::load("data/paper.jpg");
|
||||
|
||||
// hacky thing to make the toolbar buttons not steal events when moving cursor fast
|
||||
if (auto* toolbar = layout[main_id]->find<Node>("toolbar"))
|
||||
toolbar->m_flood_events = true;
|
||||
|
||||
NodeImage* n = new NodeImage;
|
||||
n->m_path = "data/ui/p-black.png";
|
||||
n->m_tex_id = const_hash("data/ui/p-black.png");
|
||||
n->SetSize(30, 45);
|
||||
n->create();
|
||||
|
||||
NodeButtonCustom* butt = new NodeButtonCustom;
|
||||
butt->create();
|
||||
butt->add_child(n);
|
||||
butt->SetPositioning(YGPositionTypeAbsolute);
|
||||
butt->set_color({ 0, 0, 0, 0 });
|
||||
//n->SetPosition(100, 100);
|
||||
YGNodeStyleSetPosition(butt->y_node, YGEdgeBottom, 8);
|
||||
YGNodeStyleSetPosition(butt->y_node, YGEdgeLeft, 15);
|
||||
//butt->SetSize(30, 45);
|
||||
layout[main_id]->add_child(butt);
|
||||
|
||||
butt->on_click = [this](Node*){
|
||||
toggle_ui();
|
||||
};
|
||||
|
||||
if (auto* slider = layout[main_id]->find<NodeSliderH>("frames-slider"))
|
||||
{
|
||||
auto frame_text = layout[main_id]->find<NodeText>("timeline-frame");
|
||||
slider->on_value_changed = [this, frame_text](Node*, float value)
|
||||
{
|
||||
auto& c = *ui::Canvas::I;
|
||||
|
||||
for (int i = 0; i < c.m_layers.size(); i++)
|
||||
{
|
||||
auto l = layers->get_layer_at(i);
|
||||
layers->handle_layer_opacity(l, .0f);
|
||||
}
|
||||
|
||||
int current_layer = (int)glm::clamp<int>(
|
||||
floor(value * c.m_layers.size()), 1, c.m_layers.size() - 1);
|
||||
auto l = layers->get_layer_at(current_layer);
|
||||
layers->handle_layer_selected(l);
|
||||
layers->handle_layer_opacity(l, 1.f);
|
||||
if (current_layer > 0)
|
||||
{
|
||||
auto l = layers->get_layer_at(current_layer - 1);
|
||||
layers->handle_layer_opacity(l, .25f);
|
||||
}
|
||||
|
||||
// First layer always visible
|
||||
{
|
||||
auto l = layers->get_layer_at(0);
|
||||
layers->handle_layer_opacity(l, 1.0f);
|
||||
}
|
||||
|
||||
if (frame_text)
|
||||
{
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%02d", current_layer);
|
||||
frame_text->set_text(str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
App::I.redraw = true;
|
||||
};
|
||||
LOG("initializing layout xml");
|
||||
if (layout.m_loaded)
|
||||
{
|
||||
LOG("restore layout");
|
||||
layout.restore_context();
|
||||
if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context();
|
||||
if (panels->get_child_index(layers.get()) == -1) layers->restore_context();
|
||||
if (panels->get_child_index(color.get()) == -1) color->restore_context();
|
||||
if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context();
|
||||
}
|
||||
else
|
||||
layout.load("data/layout.xml");
|
||||
LOG("initializing layout completed");
|
||||
}
|
||||
531
src/app_shaders.cpp
Normal file
531
src/app_shaders.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "shader.h"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
void App::initShaders()
|
||||
{
|
||||
static const char* shader_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec4 pos;"
|
||||
"in vec2 uvs;"
|
||||
"out vec3 uv;"
|
||||
"void main(){"
|
||||
" uv = vec3(uvs, pos.w);"
|
||||
" gl_Position = mvp * vec4(pos.xyz, 1.0);"
|
||||
"}";
|
||||
static const char* shader_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;"
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
//" frag = texture(tex, uv.xy/uv.z);"
|
||||
" frag = texture(tex, uv.xy);"
|
||||
"}";
|
||||
static const char* shader_uv_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;"
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" frag = vec4(uv.xy, 0.0, 1.0);"
|
||||
"}";
|
||||
// TEXTURE ALPHA
|
||||
static const char* shader_alpha_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform bool highlight;\n"
|
||||
"in mediump vec3 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" mediump vec4 c = texture(tex, uv.xy);\n"
|
||||
" frag = highlight ? \n"
|
||||
" vec4(clamp(vec3(.3)+c.rgb, vec3(0), vec3(1)), c.a) : \n"
|
||||
" texture(tex, uv.xy) * vec4(1,1,1,alpha);\n"
|
||||
"}\n";
|
||||
// TEXTURE ALPHA SEPARATED
|
||||
static const char* shader_alpha_sep_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform sampler2D tex_alpha;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform bool highlight;\n"
|
||||
"in mediump vec3 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" mediump vec3 rgb = texture(tex, uv.xy).rgb;\n"
|
||||
" mediump float a = texture(tex_alpha, uv.xy).a;\n"
|
||||
" mediump vec4 c = vec4(rgb, a);\n"
|
||||
" frag = highlight ? \n"
|
||||
" vec4(clamp(vec3(.3)+c.rgb, vec3(0), vec3(1)), c.a) : \n"
|
||||
" texture(tex, uv.xy) * vec4(1,1,1,alpha);\n"
|
||||
"}\n";
|
||||
// STROKE PREVIEW
|
||||
static const char* shader_stroke_preview_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform mediump vec4 col;\n"
|
||||
"in mediump vec3 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" mediump float stroke = 1.0 - texture(tex, uv.xy).r;\n"
|
||||
" frag = vec4(col.rgb, stroke * alpha);\n"
|
||||
"}";
|
||||
// TEXTURE COMP ERASE
|
||||
static const char* shader_comp_erase_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform sampler2D tex_stroke;\n"
|
||||
"uniform sampler2D tex_mask;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform bool lock;\n"
|
||||
"in mediump vec3 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" mediump vec4 base = texture(tex, uv.xy);\n"
|
||||
" mediump vec4 stroke = texture(tex_stroke, uv.xy);\n"
|
||||
" mediump float a = base.a - (stroke.a * alpha);\n"
|
||||
" frag = vec4(base.rgb, clamp(a, 0.0, 1.0));\n"
|
||||
"}\n";
|
||||
// TEXTURE COMP DRAW
|
||||
static const char* shader_comp_draw_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform sampler2D tex_stroke;\n"
|
||||
"uniform sampler2D tex_mask;\n"
|
||||
"uniform sampler2D tex_stencil;\n"
|
||||
//"uniform image2D img_mixer;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform mediump int blend_mode;\n"
|
||||
"uniform bool lock;\n"
|
||||
"uniform bool mask;\n"
|
||||
"in mediump vec3 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"mediump vec4 blur(sampler2D t, mediump vec2 uv){\n"
|
||||
" mediump vec4 sum = texture(t, uv);\n"
|
||||
" sum += textureOffset(t, uv, ivec2(-1, -1));\n"
|
||||
" sum += textureOffset(t, uv, ivec2(-1, 0));\n"
|
||||
" sum += textureOffset(t, uv, ivec2(-1, 1));\n"
|
||||
" sum += textureOffset(t, uv, ivec2( 0, -1));\n"
|
||||
" sum += textureOffset(t, uv, ivec2( 0, 1));\n"
|
||||
" sum += textureOffset(t, uv, ivec2( 1, -1));\n"
|
||||
" sum += textureOffset(t, uv, ivec2( 1, 0));\n"
|
||||
" sum += textureOffset(t, uv, ivec2( 1, 1));\n"
|
||||
" return sum / vec4(9.0);\n"
|
||||
"}\n"
|
||||
|
||||
"mediump vec3 blend_normal(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"
|
||||
"{ return mix(base.rgb, stroke.rgb, stroke.a/alpha_tot); }\n"
|
||||
"mediump vec3 blend_multiply(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"
|
||||
"{ return mix(stroke.rgb, mix(base.rgb, base.rgb*stroke.rgb, stroke.a/alpha_tot), base.a/alpha_tot); }\n"
|
||||
"mediump vec3 blend_screen(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"
|
||||
"{ return mix(stroke.rgb, mix(base.rgb, 1.0-(1.0-base.rgb)*(1.0-stroke.rgb), stroke.a/alpha_tot), base.a/alpha_tot); }\n"
|
||||
"mediump vec3 blend_colorDodge(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"
|
||||
"{ return mix(stroke.rgb, mix(base.rgb, base.rgb/(1.0-stroke.rgb), stroke.a/alpha_tot), base.a/alpha_tot); }\n"
|
||||
"mediump vec3 blend_overlay(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"
|
||||
"{ return mix(stroke.rgb, mix(base.rgb, mix(2.0*base.rgb*stroke.rgb, 1.0-2.0*(1.0-base.rgb)*(1.0-stroke.rgb), floor(base.rgb*2.0)), stroke.a/alpha_tot), base.a/alpha_tot); }\n"
|
||||
|
||||
"mediump vec3 blend(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot, int mode) {\n"
|
||||
" if (mode == 0) return blend_normal(base, stroke, alpha_tot);\n"
|
||||
" else if (mode == 1) return blend_multiply(base, stroke, alpha_tot);\n"
|
||||
" else if (mode == 2) return blend_screen(base, stroke, alpha_tot);\n"
|
||||
" else if (mode == 3) return blend_colorDodge(base, stroke, alpha_tot);\n"
|
||||
" else if (mode == 4) return blend_overlay(base, stroke, alpha_tot);\n"
|
||||
" else return blend_multiply(base, stroke, alpha_tot);\n"
|
||||
"}\n"
|
||||
|
||||
"void main(){\n"
|
||||
" mediump vec4 base = texture(tex, uv.xy);\n"
|
||||
" mediump vec4 stroke = texture(tex_stroke, uv.xy);\n"
|
||||
" stroke.a = mask ? stroke.a * alpha * blur(tex_mask, uv.xy).r : stroke.a * alpha;\n"
|
||||
|
||||
" if (!lock && base.a == 0.0) { frag = stroke; return; }\n"
|
||||
" mediump float contribution = (1.0 - base.a) * stroke.a;\n"
|
||||
" mediump float alpha_tot = base.a + contribution;"
|
||||
" mediump vec3 rgb = blend(base, stroke, alpha_tot, blend_mode);\n"
|
||||
" frag = vec4(rgb, (lock ? base.a : alpha_tot));\n"
|
||||
"}\n";
|
||||
|
||||
// TEXTURE ATLAS
|
||||
static const char* shader_atlas_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"uniform vec2 tof;"
|
||||
"uniform vec2 tsz;"
|
||||
"in vec2 pos;"
|
||||
"in vec2 uvs;"
|
||||
"out vec2 uv;"
|
||||
"void main(){"
|
||||
" uv = tof + uvs * tsz;"
|
||||
" gl_Position = mvp * vec4(pos, 0.0, 1.0);"
|
||||
"}";
|
||||
static const char* shader_atlas_f =
|
||||
SHADER_VERSION
|
||||
"uniform sampler2D tex;"
|
||||
"in mediump vec2 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" frag = texture(tex, uv);"
|
||||
"}";
|
||||
|
||||
// SOLID COLOR
|
||||
static const char* shader_color_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec4 pos;"
|
||||
"void main(){"
|
||||
" gl_Position = mvp * pos;"
|
||||
" gl_PointSize = 5.0;"
|
||||
"}";
|
||||
static const char* shader_color_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump vec4 col;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" frag = col;"
|
||||
"}";
|
||||
|
||||
// COLOR QUAD
|
||||
static const char* shader_color_quad_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec4 pos;"
|
||||
"in vec2 uvs;"
|
||||
"out vec3 uv;"
|
||||
"void main(){"
|
||||
" gl_Position = mvp * pos;"
|
||||
" uv = vec3(uvs, pos.w);"
|
||||
"}";
|
||||
static const char* shader_color_quad_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump vec4 col; // HSV\n"
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"mediump vec3 rgb2hsv(mediump vec3 c) {"
|
||||
" mediump vec4 k = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);"
|
||||
" mediump vec4 p = mix(vec4(c.bg, k.wz), vec4(c.gb, k.xy), step(c.b, c.g));"
|
||||
" mediump vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));"
|
||||
" mediump float d = q.x - min(q.w, q.y);"
|
||||
" mediump float e = 1.0e-10;"
|
||||
" return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);"
|
||||
"}"
|
||||
"mediump vec3 hsv2rgb(mediump vec3 c) {"
|
||||
" mediump vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);"
|
||||
" mediump vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);"
|
||||
" return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);"
|
||||
"}"
|
||||
"void main() {"
|
||||
" frag = vec4(hsv2rgb(vec3(col.x, uv.x, 1.0 - uv.y)), 1.0);"
|
||||
"}";
|
||||
// COLOR TRI
|
||||
static const char* shader_color_tri_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump vec4 col;" // in HSV
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"mediump vec3 rgb2hsv(mediump vec3 c) {"
|
||||
" mediump vec4 k = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);"
|
||||
" mediump vec4 p = mix(vec4(c.bg, k.wz), vec4(c.gb, k.xy), step(c.b, c.g));"
|
||||
" mediump vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));"
|
||||
" mediump float d = q.x - min(q.w, q.y);"
|
||||
" mediump float e = 1.0e-10;"
|
||||
" return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);"
|
||||
"}"
|
||||
"mediump vec3 hsv2rgb(mediump vec3 c) {"
|
||||
" mediump vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);"
|
||||
" mediump vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);"
|
||||
" return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);"
|
||||
"}"
|
||||
"void main() {"
|
||||
" mediump float sat = tan(atan(uv.y, uv.x)) *.5 + .5;"
|
||||
" frag = vec4(hsv2rgb(vec3(col.r, sat, uv.x)), 1.0);"
|
||||
"}";
|
||||
|
||||
// HUE
|
||||
static const char* shader_color_hue_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec4 pos;"
|
||||
"in vec2 uvs;"
|
||||
"out vec3 uv;"
|
||||
"void main(){"
|
||||
" gl_Position = mvp * pos;"
|
||||
" uv = vec3(uvs, pos.w);"
|
||||
"}";
|
||||
static const char* shader_color_hue_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump vec4 col;"
|
||||
"uniform bool dir;" // 0:horizontal, 1:vertical
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"mediump vec3 rgb2hsv(mediump vec3 c) {"
|
||||
" mediump vec4 k = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);"
|
||||
" mediump vec4 p = mix(vec4(c.bg, k.wz), vec4(c.gb, k.xy), step(c.b, c.g));"
|
||||
" mediump vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));"
|
||||
" mediump float d = q.x - min(q.w, q.y);"
|
||||
" mediump float e = 1.0e-10;"
|
||||
" return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);"
|
||||
"}"
|
||||
"mediump vec3 hsv2rgb(mediump vec3 c) {"
|
||||
" mediump vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);"
|
||||
" mediump vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);"
|
||||
" return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);"
|
||||
"}"
|
||||
"void main(){"
|
||||
" frag = vec4(hsv2rgb(vec3(dir?uv.y:uv.x, 1.0, 1.0)), 1.0);"
|
||||
"}";
|
||||
|
||||
// FONT
|
||||
static const char* shader_font_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec2 pos;"
|
||||
"in vec2 uvs;"
|
||||
"out vec2 uv;"
|
||||
"void main(){"
|
||||
" uv = uvs;"
|
||||
" gl_Position = mvp * vec4(pos, 0.0, 1.0);"
|
||||
"}";
|
||||
static const char* shader_font_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump sampler2D tex;"
|
||||
"uniform mediump vec4 col;"
|
||||
"in mediump vec2 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" mediump float a = texture(tex, uv).r;"
|
||||
" frag = vec4(col.rgb, a);"
|
||||
"}";
|
||||
|
||||
// STROKE
|
||||
static const char* shader_stroke_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;\n"
|
||||
"in vec4 pos;\n"
|
||||
"in vec2 uvs;\n"
|
||||
"in vec2 uvs2;\n"
|
||||
"out vec2 uv;\n"
|
||||
"out vec2 uv_2;\n"
|
||||
"out float q;\n"
|
||||
"void main(){\n"
|
||||
" uv = uvs;\n"
|
||||
" uv_2 = uvs2;\n"
|
||||
" q = pos.z;\n"
|
||||
" gl_Position = mvp * vec4(pos.xy, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
static const char* shader_stroke_f =
|
||||
SHADER_VERSION
|
||||
#ifdef __IOS__
|
||||
"#extension GL_EXT_shader_framebuffer_fetch : enable\n"
|
||||
// "#extension GL_EXT_shader_image_load_store : enable\n"
|
||||
#endif
|
||||
"uniform mediump sampler2D tex;\n"
|
||||
"uniform mediump sampler2D tex_bg;\n"
|
||||
"uniform mediump sampler2D tex_stencil;\n"
|
||||
"uniform mediump sampler2D tex_mix;\n"
|
||||
"uniform mediump vec4 col;\n"
|
||||
"uniform mediump vec2 resolution;\n"
|
||||
"uniform mediump float alpha;\n"
|
||||
"uniform mediump float noise;\n"
|
||||
"uniform mediump vec2 stencil_offset;\n"
|
||||
"uniform mediump float stencil_alpha;\n"
|
||||
"uniform mediump float mix_alpha;\n"
|
||||
"uniform mediump float wet;\n"
|
||||
"in mediump vec2 uv;\n"
|
||||
"in mediump vec2 uv_2;\n"
|
||||
"in mediump float q;\n"
|
||||
#ifdef __IOS__
|
||||
"inout mediump vec4 frag;\n"
|
||||
#else
|
||||
"out mediump vec4 frag;\n"
|
||||
#endif
|
||||
// http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
|
||||
"highp float rand(mediump vec2 co)\n"
|
||||
"{\n"
|
||||
" highp float a = 12.9898;\n"
|
||||
" highp float b = 78.233;\n"
|
||||
" highp float c = 43758.5453;\n"
|
||||
" highp float dt= dot(co.xy ,vec2(a,b));\n"
|
||||
" highp float sn= mod(dt,3.14);\n"
|
||||
" return fract(sin(sn) * c);\n"
|
||||
"}\n"
|
||||
//"highp float rand(mediump vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); }\n"
|
||||
"void main(){\n"
|
||||
" mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
|
||||
" mediump float stencil = 1.0 - (texture(tex_stencil, (uv2+stencil_offset) * 5.0).r) * stencil_alpha;\n"
|
||||
" mediump float brush_alpha = ( 1.0 - texture(tex, uv/q).r ) * alpha;\n"
|
||||
" mediump vec4 fg = vec4(col.rgb, brush_alpha * stencil);\n"
|
||||
#ifdef __IOS__
|
||||
" mediump vec4 bg = frag;\n"
|
||||
#else
|
||||
" mediump vec4 bg = texture(tex_bg, uv2);\n"
|
||||
#endif
|
||||
" fg.a *= 1.0-rand(uv2+uv)*noise;\n"
|
||||
" if (fg.a == 0.0) discard;\n"
|
||||
" if (mix_alpha > 0.0){\n"
|
||||
" mediump vec2 uv_mix = uv_2 / q;\n"
|
||||
" if (uv_mix.x < 0.0 || uv_mix.x > 1.0 || uv_mix.y < 0.0 || uv_mix.y > 1.0) discard;\n"
|
||||
" mediump vec4 mbg = texture(tex_mix, uv_mix);\n"
|
||||
" fg.rgb = mix(fg.rgb, mbg.rgb, mix_alpha * mbg.a);\n"
|
||||
" }\n"
|
||||
" mediump float contribution = (1.0 - bg.a) * fg.a;\n"
|
||||
" mediump float alpha_tot = bg.a + contribution;"
|
||||
" mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
|
||||
" mediump vec4 frag_wet = vec4(rgb, max(bg.a, fg.a * 1.2));\n"
|
||||
" mediump vec4 frag_dry = vec4(rgb, alpha_tot);\n"
|
||||
" frag = mix(frag_dry, frag_wet, wet);\n"
|
||||
|
||||
// " mediump vec4 mbg = texture(tex_mix, uv_2 / q);\n"
|
||||
// " frag.rgb = mix(frag.rgb, mbg.rgb, mix_alpha * mbg.a);\n"
|
||||
"}\n";
|
||||
|
||||
static const char* shader_checkerboard_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;\n"
|
||||
"in vec4 pos;\n"
|
||||
"in vec2 uvs;\n"
|
||||
"out vec2 uv;\n"
|
||||
"void main(){\n"
|
||||
" uv = uvs;\n"
|
||||
" gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
|
||||
"}";
|
||||
static const char* shader_checkerboard_f =
|
||||
SHADER_VERSION
|
||||
"in mediump vec2 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" const mediump vec4 c1 = vec4(1.0, 1.0, 1.0, 1.0);\n"
|
||||
" const mediump vec4 c2 = vec4(0.9, 0.9, 0.9, 1.0);\n"
|
||||
" mediump vec2 c = floor(fract(uv * 10.0) * 2.0);\n"
|
||||
" mediump float alpha = mix(c.x, 1.0 - c.x, c.y);\n"
|
||||
" frag = mix(c1, c2, alpha);\n"
|
||||
"}";
|
||||
|
||||
static const char* shader_equirect_v =
|
||||
SHADER_VERSION
|
||||
"#define PI 3.1415926535897932384626433832795\n"
|
||||
"#define TWO_PI 6.283185307179586476925286766559\n"
|
||||
"uniform mat4 mvp;\n"
|
||||
"in vec4 pos;\n"
|
||||
"in vec2 uvs;\n"
|
||||
"out vec2 uv;\n"
|
||||
"void main(){\n"
|
||||
" uv = (vec2(1.0) - uvs + vec2(0.25,0.0)) * vec2(TWO_PI, PI);\n"
|
||||
" gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
|
||||
"}";
|
||||
static const char* shader_equirect_f =
|
||||
SHADER_VERSION
|
||||
"uniform samplerCube tex;\n"
|
||||
"in highp vec2 uv;\n"
|
||||
"out mediump vec4 frag;\n"
|
||||
"void main(){\n"
|
||||
" highp float anglex = uv.x;\n"
|
||||
" highp float angley = uv.y;\n"
|
||||
" highp float sx = sin(anglex);\n"
|
||||
" highp float cx = cos(anglex);\n"
|
||||
" highp vec3 dir = vec3(0.0, 0.0, 0.0);\n"
|
||||
" dir.x = sin(angley) * cx;\n"
|
||||
" dir.y = cos(angley);\n"
|
||||
" dir.z = sin(angley) * sx;\n"
|
||||
" frag = texture(tex, dir);\n"
|
||||
"}";
|
||||
|
||||
// STROKE - INSTANCED
|
||||
static const char* shader_stroke_inst_v =
|
||||
SHADER_VERSION
|
||||
"in vec4 pos;"
|
||||
"in vec2 uvs;"
|
||||
"in mat4 a_mvp;"
|
||||
"in float a_flow;"
|
||||
"out vec3 uv;"
|
||||
"out float alpha;"
|
||||
"void main(){"
|
||||
" uv = vec3(uvs, pos.w);"
|
||||
" alpha = a_flow;"
|
||||
" gl_Position = a_mvp * vec4(pos.xyz, 1.0);"
|
||||
"}";
|
||||
static const char* shader_stroke_inst_f =
|
||||
SHADER_VERSION
|
||||
"uniform mediump sampler2D tex;"
|
||||
"uniform mediump sampler2D tex_stencil;\n"
|
||||
"uniform mediump vec4 col;"
|
||||
"uniform mediump vec2 resolution;\n"
|
||||
"uniform mediump vec2 stencil_offset;\n"
|
||||
"uniform mediump float stencil_alpha;\n"
|
||||
"in mediump float alpha;"
|
||||
"in mediump vec3 uv;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
|
||||
" mediump float stencil = 1.0 - (texture(tex_stencil, (uv2+stencil_offset)).r * 0.9) * stencil_alpha;\n"
|
||||
" mediump float a = (1.0 - texture(tex, uv.xy).r) * alpha * stencil;"
|
||||
" frag = vec4(col.rgb, a);"
|
||||
"}";
|
||||
|
||||
// VERTEX COLOR
|
||||
static const char* shader_vertcol_v =
|
||||
SHADER_VERSION
|
||||
"uniform mat4 mvp;"
|
||||
"in vec4 pos;"
|
||||
"in vec4 col;"
|
||||
"out vec4 c;"
|
||||
"void main(){"
|
||||
" c = col;"
|
||||
" gl_Position = mvp * pos;"
|
||||
" gl_PointSize = 5.0;"
|
||||
"}";
|
||||
static const char* shader_vertcol_f =
|
||||
SHADER_VERSION
|
||||
"in mediump vec4 c;"
|
||||
"out mediump vec4 frag;"
|
||||
"void main(){"
|
||||
" frag = c;"
|
||||
"}";
|
||||
|
||||
|
||||
LOG("initializing shaders");
|
||||
if (!ShaderManager::create(kShader::Texture, shader_v, shader_f))
|
||||
LOG("Failed to create shader Texture");
|
||||
if (!ShaderManager::create(kShader::TextureAlpha, shader_v, shader_alpha_f))
|
||||
LOG("Failed to create shader TextureAlpha");
|
||||
if (!ShaderManager::create(kShader::TextureAlphaSep, shader_v, shader_alpha_sep_f))
|
||||
LOG("Failed to create shader TextureAlphaSep");
|
||||
if (!ShaderManager::create(kShader::StrokePreview, shader_v, shader_stroke_preview_f))
|
||||
LOG("Failed to create shader StrokePreview");
|
||||
if (!ShaderManager::create(kShader::CompErase, shader_v, shader_comp_erase_f))
|
||||
LOG("Failed to create shader CompErase");
|
||||
if (!ShaderManager::create(kShader::CompDraw, shader_v, shader_comp_draw_f))
|
||||
LOG("Failed to create shader CompDraw");
|
||||
if (!ShaderManager::create(kShader::Color, shader_color_v, shader_color_f))
|
||||
LOG("Failed to create shader Color");
|
||||
if (!ShaderManager::create(kShader::ColorQuad, shader_color_quad_v, shader_color_quad_f))
|
||||
LOG("Failed to create shader ColorQuad");
|
||||
if (!ShaderManager::create(kShader::ColorTri, shader_color_quad_v, shader_color_tri_f))
|
||||
LOG("Failed to create shader ColorTri");
|
||||
if (!ShaderManager::create(kShader::ColorHue, shader_color_hue_v, shader_color_hue_f))
|
||||
LOG("Failed to create shader ColorHue");
|
||||
if (!ShaderManager::create(kShader::UVs, shader_v, shader_uv_f))
|
||||
LOG("Failed to create shader UVs");
|
||||
if (!ShaderManager::create(kShader::Font, shader_font_v, shader_font_f))
|
||||
LOG("Failed to create shader Font");
|
||||
if (!ShaderManager::create(kShader::Atlas, shader_atlas_v, shader_atlas_f))
|
||||
LOG("Failed to create shader Atlas");
|
||||
if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f))
|
||||
LOG("Failed to create shader Stroke");
|
||||
if (!ShaderManager::create(kShader::Checkerboard, shader_checkerboard_v, shader_checkerboard_f))
|
||||
LOG("Failed to create shader Checkerboard");
|
||||
if (!ShaderManager::create(kShader::Equirect, shader_equirect_v, shader_equirect_f))
|
||||
LOG("Failed to create shader Equirect");
|
||||
if (!ShaderManager::create(kShader::BrushStroke, shader_stroke_inst_v, shader_stroke_inst_f))
|
||||
LOG("Failed to create shader BrushStroke");
|
||||
if (!ShaderManager::create(kShader::VertexColor, shader_vertcol_v, shader_vertcol_f))
|
||||
LOG("Failed to create shader VertexColor");
|
||||
LOG("shaders initialized");
|
||||
}
|
||||
|
||||
|
||||
193
src/asset.cpp
Normal file
193
src/asset.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "asset.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <dirent.h>
|
||||
AAssetManager* Asset::m_am;
|
||||
#endif
|
||||
|
||||
bool Asset::delete_file(const std::string& path)
|
||||
{
|
||||
LOG("delete project: %s", path.c_str());
|
||||
std::remove(path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Asset::exist(std::string path, bool is_asset)
|
||||
{
|
||||
if (is_asset)
|
||||
{
|
||||
Asset asset;
|
||||
if (asset.open(path.c_str()))
|
||||
{
|
||||
asset.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ifstream f(path);
|
||||
return f.is_open();
|
||||
}
|
||||
return false; // useless return for the stupid xcode
|
||||
}
|
||||
|
||||
std::vector<std::string> Asset::list_files(std::string folder, bool is_asset, const std::string& filter_regex)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &fd);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
// read all (real) files in current folder
|
||||
// , delete '!' read other 2 default folder . and ..
|
||||
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
names.push_back(fd.cFileName);
|
||||
}
|
||||
} while (::FindNextFileA(hFind, &fd));
|
||||
::FindClose(hFind);
|
||||
}
|
||||
#elif __ANDROID__
|
||||
if (is_asset)
|
||||
{
|
||||
AAssetDir* dir = AAssetManager_openDir(Asset::m_am, folder.c_str());
|
||||
while (const char* name = AAssetDir_getNextFileName(dir))
|
||||
{
|
||||
//LOG("asset: %s", name);
|
||||
names.push_back(name);
|
||||
}
|
||||
AAssetDir_close(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
dp = opendir(folder.c_str());
|
||||
|
||||
if (dp != NULL)
|
||||
{
|
||||
while ((ep = readdir(dp)))
|
||||
if (ep->d_type != DT_DIR)
|
||||
names.push_back(ep->d_name);
|
||||
closedir(dp);
|
||||
}
|
||||
else
|
||||
LOG("Couldn't open the directory: %s", folder.c_str());
|
||||
}
|
||||
#else
|
||||
std::string abs_path = folder;
|
||||
if (is_asset)
|
||||
{
|
||||
NSString* bundle_path = [[NSBundle mainBundle] resourcePath];
|
||||
std::string base = [bundle_path cStringUsingEncoding : 1];
|
||||
abs_path = base + "/" + folder;
|
||||
}
|
||||
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
dp = opendir(abs_path.c_str());
|
||||
|
||||
if (dp != NULL)
|
||||
{
|
||||
while ((ep = readdir(dp)))
|
||||
if (ep->d_type != DT_DIR)
|
||||
names.push_back(ep->d_name);
|
||||
closedir(dp);
|
||||
}
|
||||
else
|
||||
LOG("Couldn't open the directory: %s", folder.c_str());
|
||||
#endif
|
||||
|
||||
if (!filter_regex.empty())
|
||||
{
|
||||
std::regex r(filter_regex);
|
||||
names.erase(std::remove_if(names.begin(), names.end(),
|
||||
[&r](const std::string& s) { return !std::regex_match(s, r); }), names.end());
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
std::string Asset::absolute(const std::string& path)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
NSString* bundle_path = [[NSBundle mainBundle] resourcePath];
|
||||
std::string base = [bundle_path cStringUsingEncoding:1];
|
||||
return base + "/" + path;
|
||||
#else
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Asset::open(const char* path)
|
||||
{
|
||||
//LOG("Asset::open %s", path);
|
||||
m_current_path = path;
|
||||
std::string file_path = path;
|
||||
#ifdef __ANDROID__
|
||||
if (!(m_asset = AAssetManager_open(m_am, path, AASSET_MODE_RANDOM)))
|
||||
{
|
||||
LOG("AAssetManager_open failed");
|
||||
return false;
|
||||
}
|
||||
m_len = (int)AAsset_getLength(m_asset);
|
||||
m_data = (uint8_t*)AAsset_getBuffer(m_asset);
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
NSString* bundle_path = [[NSBundle mainBundle] resourcePath];
|
||||
std::string base = [bundle_path cStringUsingEncoding:1];
|
||||
file_path = base + "/" + path;
|
||||
#endif
|
||||
//LOG("asset file: %s", file_path.c_str());
|
||||
if (!(m_fp = fopen(file_path.c_str(), "rb")))
|
||||
{
|
||||
LOG("errno = %d", errno);
|
||||
return false;
|
||||
}
|
||||
fseek(m_fp, 0, SEEK_END);
|
||||
m_len = (int)ftell(m_fp);
|
||||
fseek(m_fp, 0, SEEK_SET);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::uint8_t* Asset::read_all()
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return m_data;
|
||||
#else
|
||||
if (!m_data)
|
||||
{
|
||||
m_data = new uint8_t[m_len];
|
||||
if (m_len != fread(m_data, 1, m_len, m_fp))
|
||||
{
|
||||
LOG("ASSET READ FAILED for %s", m_current_path.c_str());
|
||||
delete m_data;
|
||||
m_data = nullptr;
|
||||
}
|
||||
}
|
||||
return m_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Asset::close()
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
AAsset_close(m_asset);
|
||||
#else
|
||||
if (m_fp)
|
||||
fclose(m_fp);
|
||||
if (m_data)
|
||||
delete m_data;
|
||||
m_data = nullptr;
|
||||
#endif
|
||||
}
|
||||
23
src/asset.h
Normal file
23
src/asset.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
class Asset
|
||||
{
|
||||
public:
|
||||
#ifdef __ANDROID__
|
||||
static AAssetManager* m_am;
|
||||
AAsset* m_asset = nullptr;
|
||||
#endif
|
||||
static std::vector<std::string> list_files(std::string folder, bool is_asset, const std::string& filter_regex);
|
||||
static bool exist(std::string path, bool is_asset);
|
||||
static bool delete_file(const std::string& path);
|
||||
static std::string absolute(const std::string& path);
|
||||
|
||||
std::string m_current_path;
|
||||
FILE* m_fp = nullptr;
|
||||
int m_len = 0;
|
||||
uint8_t* m_data = nullptr;
|
||||
bool open(const char* path);
|
||||
uint8_t* read_all();
|
||||
void close();
|
||||
};
|
||||
|
||||
40
src/bezier.cpp
Normal file
40
src/bezier.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "bezier.h"
|
||||
|
||||
|
||||
double BezierCurve::FactorialLookup[] = {
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
6.0,
|
||||
24.0,
|
||||
120.0,
|
||||
720.0,
|
||||
5040.0,
|
||||
40320.0,
|
||||
362880.0,
|
||||
3628800.0,
|
||||
39916800.0,
|
||||
479001600.0,
|
||||
6227020800.0,
|
||||
87178291200.0,
|
||||
1307674368000.0,
|
||||
20922789888000.0,
|
||||
355687428096000.0,
|
||||
6402373705728000.0,
|
||||
121645100408832000.0,
|
||||
2432902008176640000.0,
|
||||
51090942171709440000.0,
|
||||
1124000727777607680000.0,
|
||||
25852016738884976640000.0,
|
||||
620448401733239439360000.0,
|
||||
15511210043330985984000000.0,
|
||||
403291461126605635584000000.0,
|
||||
10888869450418352160768000000.0,
|
||||
304888344611713860501504000000.0,
|
||||
8841761993739701954543616000000.0,
|
||||
265252859812191058636308480000000.0,
|
||||
8222838654177922817725562880000000.0,
|
||||
263130836933693530167218012160000000.0,
|
||||
};
|
||||
66
src/bezier.h
Normal file
66
src/bezier.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
class BezierCurve
|
||||
{
|
||||
static double FactorialLookup[33];
|
||||
public:
|
||||
// just check if n is appropriate, then return the result
|
||||
static double factorial(int n)
|
||||
{
|
||||
// if (n < 0) { throw new Exception("n is less than 0"); }
|
||||
// if (n > 32) { throw new Exception("n is greater than 32"); }
|
||||
return FactorialLookup[n]; /* returns the value n! as a SUMORealing point number */
|
||||
}
|
||||
|
||||
static double Ni(int n, int i)
|
||||
{
|
||||
double ni;
|
||||
double a1 = factorial(n);
|
||||
double a2 = factorial(i);
|
||||
double a3 = factorial(n - i);
|
||||
ni = a1 / (a2 * a3);
|
||||
return ni;
|
||||
}
|
||||
|
||||
// Calculate Bernstein basis
|
||||
static double Bernstein(int n, int i, double t)
|
||||
{
|
||||
double basis;
|
||||
double ti; /* t^i */
|
||||
double tni; /* (1 - t)^i */
|
||||
|
||||
/* Prevent problems with pow */
|
||||
|
||||
if (t == 0.0 && i == 0)
|
||||
ti = 1.0;
|
||||
else
|
||||
ti = pow(t, i);
|
||||
|
||||
if (n == i && t == 1.0)
|
||||
tni = 1.0;
|
||||
else
|
||||
tni = pow((1 - t), (n - i));
|
||||
|
||||
//Bernstein basis
|
||||
basis = Ni(n, i) * ti * tni;
|
||||
return basis;
|
||||
}
|
||||
|
||||
static glm::vec2 Bezier2D(const std::vector<glm::vec2>& b, double t)
|
||||
{
|
||||
// if ((1.0 - t) < 5e-6)
|
||||
// t = 1.0;
|
||||
|
||||
double px = 0.0;
|
||||
double py = 0.0;
|
||||
const int npts = (int)b.size();
|
||||
for (int i = 0; i < npts; i++)
|
||||
{
|
||||
double basis = Bernstein(npts - 1, i, t);
|
||||
px += basis * b[i].x;
|
||||
py += basis * b[i].y;
|
||||
}
|
||||
return { px, py };
|
||||
}
|
||||
};
|
||||
240
src/brush.cpp
Normal file
240
src/brush.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "brush.h"
|
||||
#include "canvas.h"
|
||||
|
||||
void ui::BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4& proj)
|
||||
{
|
||||
std::vector<instance_t> attributes;
|
||||
attributes.reserve(samples.size());
|
||||
for (const auto& s : samples)
|
||||
{
|
||||
auto mvp = proj *
|
||||
glm::translate(glm::vec3(s.pos, 0)) *
|
||||
glm::scale(glm::vec3(s.size, s.size, 1)) *
|
||||
glm::eulerAngleZ(s.angle);
|
||||
attributes.emplace_back(instance_t{ mvp, s.flow });
|
||||
}
|
||||
#ifdef USE_VBO
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size());
|
||||
glBindVertexArray(0);
|
||||
#else
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos));
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs));
|
||||
|
||||
// Likewise, we can do the same with the model matrix. Note that a
|
||||
// matrix input to the vertex shader consumes N consecutive input
|
||||
// locations, where N is the number of columns in the matrix. So...
|
||||
// we have four vertex attributes to set up.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (int)(sizeof(instance_t) * attributes.size()), attributes.data(), GL_STATIC_DRAW);
|
||||
// Loop over each column of the matrix...
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// Set up the vertex attribute
|
||||
glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t),
|
||||
(GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i));
|
||||
// Enable it
|
||||
glEnableVertexAttribArray(loc_mvp + i);
|
||||
// Make it instanced
|
||||
glVertexAttribDivisor(loc_mvp + i, 1);
|
||||
}
|
||||
glEnableVertexAttribArray(loc_flow);
|
||||
glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t),
|
||||
(GLvoid*)offsetof(instance_t, flow));
|
||||
glVertexAttribDivisor(loc_flow, 1);
|
||||
|
||||
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, (int)samples.size());
|
||||
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
|
||||
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
for (int i = 0; i < 4; i++)
|
||||
glDisableVertexAttribArray(loc_mvp + i);
|
||||
glDisableVertexAttribArray(loc_flow);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
#endif // USE_VBO
|
||||
}
|
||||
bool ui::BrushMesh::create()
|
||||
{
|
||||
static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 };
|
||||
static vertex_t vertices[4]{
|
||||
{ { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C
|
||||
{ { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | |
|
||||
{ { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | |
|
||||
{ { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D
|
||||
};
|
||||
glGenBuffers(3, buffers);
|
||||
if (!(buffers[0] && buffers[1] && buffers[2]))
|
||||
return false;
|
||||
|
||||
static instance_t inst{ glm::mat4(), .1f };
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
auto shader = ShaderManager::get(kShader::BrushStroke);
|
||||
|
||||
loc_flow = shader->GetAttribLocation("a_flow");
|
||||
loc_mvp = shader->GetAttribLocation("a_mvp");
|
||||
|
||||
#if USE_VBO
|
||||
glGenVertexArrays(1, &vao);
|
||||
if (!vao)
|
||||
return false;
|
||||
glBindVertexArray(vao);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos));
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
|
||||
// Loop over each column of the matrix...
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// Set up the vertex attribute
|
||||
glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t),
|
||||
(GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i));
|
||||
// Enable it
|
||||
glEnableVertexAttribArray(loc_mvp + i);
|
||||
// Make it instanced
|
||||
glVertexAttribDivisor(loc_mvp + i, 1);
|
||||
}
|
||||
glEnableVertexAttribArray(loc_flow);
|
||||
glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t),
|
||||
(GLvoid*)offsetof(instance_t, flow));
|
||||
glVertexAttribDivisor(loc_flow, 1);
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
ui::StrokeSample ui::Stroke::randomize_sample(const glm::vec2& pos, float pressure, float curve_angle)
|
||||
{
|
||||
auto rnd_nor = [&] { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
|
||||
//auto rnd_neg = [&] { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1]
|
||||
auto rnd_rad = [&] { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi]
|
||||
auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec2(cosf(rad), sinf(rad)); }; // normalized direction vector
|
||||
|
||||
float size_dyn = m_brush.m_tip_size_pressure ? pressure : 1.f;
|
||||
float flow_dyn = m_brush.m_tip_flow_pressure ? pressure : 1.f;
|
||||
|
||||
StrokeSample s;
|
||||
s.origin = pos;
|
||||
s.angle = -curve_angle + (m_brush.m_tip_angle + rnd_nor() * m_brush.m_jitter_angle) * (float)(M_PI * 2.0);
|
||||
s.pos = pos + (rnd_vec() * m_brush.m_jitter_spread * 100.f);
|
||||
s.size = 800.f * m_brush.m_tip_size * (1.f - rnd_nor() * m_brush.m_jitter_scale) * size_dyn;
|
||||
s.flow = m_brush.m_tip_flow * (1.f - rnd_nor() * m_brush.m_jitter_flow) * flow_dyn;
|
||||
auto hsv = convert_rgb2hsv(m_brush.m_tip_color);
|
||||
hsv.x = glm::clamp(glm::mix(hsv.x, (pressure - 0.5f) * 2.0f, m_brush.m_tip_hue * (float)m_brush.m_tip_hue_pressure) + (rnd_nor() - 0.5f) * m_brush.m_jitter_hue, 0.f, 1.f);
|
||||
hsv.y = glm::clamp(glm::mix(hsv.y, (1.f - pressure - 0.5f) * 2.0f, m_brush.m_tip_sat * (float)m_brush.m_tip_sat_pressure) + (rnd_nor() - 0.5f) * m_brush.m_jitter_sat, 0.f, 1.f);
|
||||
hsv.z = glm::clamp(glm::mix(hsv.z, (pressure - 0.5f) * 2.0f, m_brush.m_tip_val * (float)m_brush.m_tip_val_pressure) + (rnd_nor() - 0.5f) * m_brush.m_jitter_val, 0.f, 1.f);
|
||||
m_hsv_jitter.add(hsv);
|
||||
s.col = convert_hsv2rgb(m_hsv_jitter.average());
|
||||
return s;
|
||||
}
|
||||
std::vector<ui::StrokeSample> ui::Stroke::compute_samples()
|
||||
{
|
||||
if (m_keypoints.empty()) return {};
|
||||
int nsamples = (int)glm::floor((m_keypoints.back().dist - m_dist) / m_step);
|
||||
std::vector<StrokeSample> samples;
|
||||
samples.reserve(nsamples); // preallocate the estimate number of samples
|
||||
while (m_keypoints.back().dist > (m_dist + m_step))
|
||||
{
|
||||
bool is_first = m_last_kp == 0;
|
||||
m_dist += m_step;
|
||||
while (m_dist > m_keypoints[m_last_kp + 1].dist)
|
||||
m_last_kp++;
|
||||
const auto& A = m_keypoints[m_last_kp];
|
||||
const auto& B = m_keypoints[m_last_kp + 1]; // NOTE: this should be true when while is true
|
||||
float t = (m_dist - A.dist) / (B.dist - A.dist); // NOTE: must be A != B
|
||||
auto pos = glm::lerp(A.pos, B.pos, t);
|
||||
float pressure = glm::lerp(A.pressure, B.pressure, t);
|
||||
|
||||
auto s = randomize_sample(pos, pressure, 0);
|
||||
if (m_brush.m_tip_angle_follow)
|
||||
{
|
||||
auto& pre = m_prev_sample;
|
||||
glm::vec2 v = glm::normalize(s.origin - pre.origin);
|
||||
float curve_angle = -glm::orientedAngle(v, glm::vec2(1, 0));
|
||||
|
||||
// NOTE: average angles need correction for 0-360 discontinuity
|
||||
//m_curve_angles.add(curve_angle);
|
||||
//float avg = m_curve_angles.average();
|
||||
|
||||
s.angle += curve_angle;
|
||||
}
|
||||
m_prev_sample = s;
|
||||
if (!s.valid())
|
||||
LOG("Invalid sample");
|
||||
samples.push_back(s);
|
||||
}
|
||||
return std::move(samples);
|
||||
}
|
||||
bool ui::Stroke::has_sample()
|
||||
{
|
||||
return m_keypoints.empty() ? false : // no keypoints
|
||||
(m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing
|
||||
}
|
||||
void ui::Stroke::reset(bool clear_keypoints /*= false*/)
|
||||
{
|
||||
m_last_kp = 0;
|
||||
m_dist = 0.f;
|
||||
if (clear_keypoints)
|
||||
m_keypoints.clear();
|
||||
}
|
||||
void ui::Stroke::add_point(glm::vec2 pos, float pressure)
|
||||
{
|
||||
#ifdef __IOS__
|
||||
m_curve = glm::min(m_curve + 0.1f, 1.f);
|
||||
pressure = pressure * glm::pow(m_curve, 2.f);
|
||||
#endif // __IOS__
|
||||
m_pressure_buff.add(pressure);
|
||||
pressure = m_pressure_buff.average();
|
||||
|
||||
if (m_brush.m_tip_size_pressure)
|
||||
m_step = glm::max(m_brush.m_tip_spacing * m_brush.m_tip_size * pressure * 800.f, 1.f);
|
||||
|
||||
float dist = m_keypoints.empty() ? m_step :
|
||||
m_keypoints.back().dist + glm::distance(m_keypoints.back().pos, pos);
|
||||
if (m_keypoints.empty())
|
||||
m_prev_sample.origin = pos;
|
||||
else if (m_keypoints.back().pos == pos)
|
||||
return; // skip same point, leading to black samples (NaN values)
|
||||
Keypoint kp;
|
||||
kp.pos = pos;
|
||||
kp.pressure = pressure;
|
||||
kp.dist = dist;
|
||||
m_keypoints.push_back(kp);
|
||||
}
|
||||
void ui::Stroke::start(const ui::Brush& brush)
|
||||
{
|
||||
m_curve = 0.f;
|
||||
m_curve_angles.clear();
|
||||
m_pressure_buff.clear();
|
||||
m_hsv_jitter.clear();
|
||||
m_last_kp = 0;
|
||||
m_dist = 0.f;
|
||||
m_brush = brush;
|
||||
m_brush.m_tip_size *= 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f));
|
||||
m_step = glm::max(m_brush.m_tip_spacing * m_brush.m_tip_size * 800.f, 1.f);
|
||||
prng.seed(0);
|
||||
}
|
||||
110
src/brush.h
Normal file
110
src/brush.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include "rtt.h"
|
||||
#include "shader.h"
|
||||
|
||||
NS_START
|
||||
|
||||
class Brush
|
||||
{
|
||||
public:
|
||||
int id = 0;
|
||||
std::string m_name;
|
||||
uint16_t m_tex_id = 0;
|
||||
glm::vec4 m_tip_color{1, 0, 0, 1};
|
||||
float m_tip_size = 0;
|
||||
float m_tip_spacing = 0;
|
||||
float m_tip_flow = 0;
|
||||
float m_tip_opacity = 0;
|
||||
float m_tip_angle = 0;
|
||||
float m_tip_mix = 0;
|
||||
float m_tip_stencil = 0;
|
||||
float m_tip_wet = 0;
|
||||
float m_tip_noise = 0;
|
||||
float m_tip_hue = 0;
|
||||
float m_tip_sat = 0;
|
||||
float m_tip_val = 0;
|
||||
bool m_tip_angle_follow = false;
|
||||
bool m_tip_flow_pressure = false;
|
||||
bool m_tip_size_pressure = false;
|
||||
bool m_tip_hue_pressure = false;
|
||||
bool m_tip_sat_pressure = false;
|
||||
bool m_tip_val_pressure = false;
|
||||
float m_jitter_scale = 0;
|
||||
float m_jitter_angle = 0;
|
||||
float m_jitter_spread = 0;
|
||||
float m_jitter_flow = 0;
|
||||
float m_jitter_hue = 0;
|
||||
float m_jitter_sat = 0;
|
||||
float m_jitter_val = 0;
|
||||
int m_blend_mode = 0;
|
||||
};
|
||||
|
||||
struct StrokeSample
|
||||
{
|
||||
glm::vec3 col = { 0, 0, 0 };
|
||||
glm::vec2 pos = { 0, 0 };
|
||||
glm::vec2 origin = { 0,0 };
|
||||
float size = 0;
|
||||
float flow = 0;
|
||||
float angle = 0;
|
||||
bool valid() const
|
||||
{
|
||||
return !(
|
||||
glm::any(glm::isnan(col)) ||
|
||||
glm::any(glm::isnan(pos)) ||
|
||||
glm::any(glm::isnan(origin))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class BrushMesh
|
||||
{
|
||||
public:
|
||||
GLuint buffers[3]{ 0 };
|
||||
GLuint vao{ 0 };
|
||||
struct vertex_t { glm::vec4 pos; glm::vec2 uvs; };
|
||||
struct instance_t { glm::mat4 mvp; float flow; };
|
||||
int loc_flow = 0;
|
||||
int loc_mvp = 0;
|
||||
|
||||
bool create();
|
||||
void draw(const std::vector<StrokeSample>& samples, const glm::mat4& proj);
|
||||
};
|
||||
|
||||
class Stroke
|
||||
{
|
||||
public:
|
||||
struct Keypoint
|
||||
{
|
||||
glm::vec2 pos = { 0, 0 };
|
||||
float pressure = 0;
|
||||
float dist = 0;
|
||||
};
|
||||
struct Camera
|
||||
{
|
||||
glm::vec2 rot = { 0, 0 };
|
||||
float fov = 0;
|
||||
};
|
||||
int m_layer = 0;
|
||||
float m_curve = 0;
|
||||
float m_dist = 0;
|
||||
float m_step = 0;
|
||||
Camera m_camera;
|
||||
ui::Brush m_brush;
|
||||
cbuffer<float, 3> m_curve_angles;
|
||||
cbuffer<float, 10> m_pressure_buff;
|
||||
cbuffer<glm::vec3, 3> m_hsv_jitter;
|
||||
StrokeSample m_prev_sample;
|
||||
std::vector<Keypoint> m_keypoints;
|
||||
std::vector<StrokeSample> m_samples;
|
||||
int m_last_kp;
|
||||
std::minstd_rand prng;
|
||||
void start(const ui::Brush& brush);
|
||||
void add_point(glm::vec2 pos, float pressure);
|
||||
void reset(bool clear_keypoints = false);
|
||||
bool has_sample();
|
||||
std::vector<StrokeSample> compute_samples();
|
||||
StrokeSample randomize_sample(const glm::vec2& pos, float pressure, float curve_angle);
|
||||
};
|
||||
|
||||
NS_END
|
||||
2134
src/canvas.cpp
Normal file
2134
src/canvas.cpp
Normal file
File diff suppressed because it is too large
Load Diff
234
src/canvas.h
Normal file
234
src/canvas.h
Normal file
@@ -0,0 +1,234 @@
|
||||
#pragma once
|
||||
#include "rtt.h"
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "shape.h"
|
||||
#include "brush.h"
|
||||
#include "action.h"
|
||||
#include "canvas_modes.h"
|
||||
|
||||
NS_START
|
||||
|
||||
#define CANVAS_RES 512
|
||||
|
||||
class Layer
|
||||
{
|
||||
public:
|
||||
RTT m_rtt[6];
|
||||
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
|
||||
bool m_dirty_face[6] = SIXPLETTE(false);
|
||||
bool m_visible = true;
|
||||
bool m_alpha_locked = false;
|
||||
float m_opacity = 1.f;
|
||||
bool m_hightlight = false;
|
||||
std::string m_name;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
struct Snapshot
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> image[6] = SIXPLETTE(0);
|
||||
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
|
||||
bool m_dirty_face[6] = SIXPLETTE(false);
|
||||
void create(int w, int h)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
image[i] = std::make_unique<uint8_t[]>(w*h*4);
|
||||
}
|
||||
};
|
||||
bool create(int width, int height, std::string name);
|
||||
void clear(const glm::vec4& c);
|
||||
Snapshot snapshot(std::string data_path);
|
||||
void restore(const Snapshot& snap);
|
||||
void destroy();
|
||||
};
|
||||
|
||||
class Canvas
|
||||
{
|
||||
public:
|
||||
Plane m_plane;
|
||||
Plane m_plane_brush;
|
||||
BrushMesh m_mesh;
|
||||
bool m_unsaved = false;
|
||||
bool m_newdoc = true;
|
||||
bool m_dirty = false;
|
||||
bool m_commit_delayed = false;
|
||||
bool m_dirty_stroke = false;
|
||||
|
||||
static Canvas* I;
|
||||
NodeCanvas* m_node = nullptr;
|
||||
bool m_alpha_lock = false;
|
||||
bool m_touch_lock = true;
|
||||
glm::mat4 m_mv{ 1 };
|
||||
glm::mat4 m_proj{ 1 };
|
||||
glm::vec4 m_box{ 0 };
|
||||
glm::vec4 m_vp{ 0 };
|
||||
glm::vec2 m_pan{ 0 };
|
||||
int m_width = 0;
|
||||
int m_height = 0;
|
||||
bool m_use_instanced = false;
|
||||
int m_current_layer_idx = 0;
|
||||
std::unique_ptr<Stroke> m_current_stroke;
|
||||
bool m_show_tmp = false;
|
||||
std::vector<Layer> m_layers;
|
||||
std::vector<int> m_order;
|
||||
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
|
||||
bool m_dirty_face[6] = SIXPLETTE(false);
|
||||
Layer m_smask; // selection mask
|
||||
bool m_smask_active = false;
|
||||
RTT m_tmp[6];
|
||||
RTT m_mixer;
|
||||
float m_mixer_scale = 1;
|
||||
ui::StrokeSample m_mixer_sample;
|
||||
bool m_mixer_idle = true;
|
||||
Texture2D m_brush_mix;
|
||||
Texture2D m_tex[6];
|
||||
Texture2D m_tex2[6];
|
||||
bool m_pick_ready[6];
|
||||
std::unique_ptr<glm::u8vec4[]> m_pick_data[6] = SIXPLETTE(nullptr);
|
||||
static glm::vec3 m_plane_origin[6];
|
||||
static glm::vec3 m_plane_normal[6];
|
||||
static glm::vec3 m_plane_tangent[6];
|
||||
static glm::mat4 m_plane_transform[6];
|
||||
glm::vec2 stencil_offset;
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_linear;
|
||||
Sampler m_sampler_brush;
|
||||
Sampler m_sampler_bg;
|
||||
Sampler m_sampler_mask;
|
||||
Sampler m_sampler_stencil;
|
||||
Sampler m_sampler_mix;
|
||||
glm::vec2 m_cam_rot{ 0 };
|
||||
glm::vec3 m_cam_pos{ 0 };
|
||||
float m_cam_fov = 85;
|
||||
glm::vec2 m_cur_pos;
|
||||
|
||||
Brush m_current_brush;
|
||||
|
||||
enum class kCanvasMode { Draw, Erase, Line, Camera, Grid, Fill, MaskFree, MaskLine, COUNT };
|
||||
kCanvasMode m_state{ kCanvasMode::Draw };
|
||||
static std::vector<CanvasMode*> modes[];
|
||||
std::vector<CanvasMode*>* m_mode = nullptr;
|
||||
kCanvasMode m_current_mode = kCanvasMode::Draw;
|
||||
static void set_mode(kCanvasMode mode)
|
||||
{
|
||||
if (I->m_mode)
|
||||
for (auto& m : *I->m_mode)
|
||||
m->leave();
|
||||
I->m_mode = &modes[(int)mode];
|
||||
I->m_state = mode;
|
||||
I->m_current_mode = mode;
|
||||
if (I->m_mode)
|
||||
for (auto& m : *I->m_mode)
|
||||
m->enter();
|
||||
}
|
||||
|
||||
std::vector<Layer::Snapshot> m_layers_snapshot;
|
||||
|
||||
Canvas() { I = this; }
|
||||
bool create(int width, int height);
|
||||
void resize(int width, int height);
|
||||
void layer_remove(int idx);
|
||||
void layer_add(std::string name);
|
||||
void layer_order(int idx, int pos);
|
||||
void layer_merge(int source_idx, int dest_idx);
|
||||
void stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush);
|
||||
void stroke_update(glm::vec2 point, float pressure);
|
||||
void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz);
|
||||
void stroke_draw();
|
||||
void stroke_end();
|
||||
void stroke_cancel();
|
||||
void stroke_commit();
|
||||
void clear(const glm::vec4& color = { 1, 1, 1, 0 });
|
||||
void pick_start();
|
||||
void pick_update(int plane);
|
||||
glm::vec4 pick_get(glm::vec2 canvas_loc);
|
||||
void pick_end();
|
||||
void snapshot_save(std::string data_path);
|
||||
void snapshot_restore();
|
||||
void snap_history(const std::vector<int>& planes);
|
||||
class ActionStroke* create_action(int layer);
|
||||
void clear_context();
|
||||
void import_equirectangular(std::string file_path);
|
||||
void import_equirectangular_thread(std::string file_path);
|
||||
void export_equirectangular(std::string file_path);
|
||||
void export_equirectangular_thread(std::string file_path);
|
||||
void export_anim(std::string data_path);
|
||||
void export_cubes(std::string data_path);
|
||||
void project_save(std::function<void()> on_complete = nullptr);
|
||||
void project_save(std::string file_path, std::function<void()> on_complete = nullptr);
|
||||
void project_save_thread(std::string file_path);
|
||||
void project_open(std::string file_path, std::function<void()> on_complete = nullptr);
|
||||
void project_open_thread(std::string file_path);
|
||||
void inject_xmp(std::string jpg_path);
|
||||
ui::Image thumbnail_generate(int w, int h);
|
||||
ui::Image thumbnail_read(std::string data_path);
|
||||
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>);
|
||||
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>, Layer& layer);
|
||||
bool ray_intersect(glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin,
|
||||
glm::vec3 plane_normal, glm::vec3 plane_tangent, glm::vec3 &out_hit);
|
||||
void point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj,
|
||||
glm::vec3 &out_origin, glm::vec3 &out_dir);
|
||||
bool point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
|
||||
glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id);
|
||||
bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
|
||||
glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id);
|
||||
std::vector<ui::Shape::vertex_t> triangulate(const std::vector<std::shared_ptr<p2t::Point>>& points);
|
||||
void project2Dpoints(std::vector<ui::Shape::vertex_t>& vertices);
|
||||
};
|
||||
|
||||
class ActionStroke : public Action
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<Stroke> m_stroke;
|
||||
std::unique_ptr<uint8_t[]> m_image[6] = SIXPLETTE(nullptr);
|
||||
glm::ivec4 m_old_box[6] = SIXPLETTE(glm::ivec4(0));
|
||||
bool m_old_dirty[6] = SIXPLETTE(false);
|
||||
glm::ivec4 m_box[6] = SIXPLETTE(glm::ivec4(0));
|
||||
bool m_dirty[6] = SIXPLETTE(false);
|
||||
bool clear_layer = false;
|
||||
int m_layer_idx;
|
||||
Canvas* m_canvas;
|
||||
virtual void run() override
|
||||
{
|
||||
|
||||
}
|
||||
virtual Action* get_redo()
|
||||
{
|
||||
auto redo = m_canvas->create_action(m_layer_idx);
|
||||
return redo;
|
||||
}
|
||||
virtual void undo() override
|
||||
{
|
||||
if (clear_layer)
|
||||
m_canvas->m_layers[m_layer_idx].clear({ 0, 0, 0, 0 });
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
// empty data
|
||||
if (!m_image[i])
|
||||
continue;
|
||||
|
||||
m_canvas->m_layers[m_layer_idx].m_dirty_box[i] = m_old_box[i];
|
||||
m_canvas->m_layers[m_layer_idx].m_dirty_face[i] = m_old_dirty[i];
|
||||
|
||||
m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture();
|
||||
glm::vec2 box_sz = zw(m_box[i]) - xy(m_box[i]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, (int)m_box[i].x, (int)m_box[i].y, (int)box_sz.x, (int)box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get());
|
||||
m_canvas->m_layers[m_layer_idx].m_rtt[i].unbindTexture();
|
||||
}
|
||||
}
|
||||
virtual size_t memory() override
|
||||
{
|
||||
size_t mem = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
glm::ivec2 sz = zw(m_box[i]) - xy(m_box[i]);
|
||||
mem += sz.x * sz.y * 4 + sizeof(*this);
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
virtual ~ActionStroke()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
NS_END
|
||||
788
src/canvas_modes.cpp
Normal file
788
src/canvas_modes.cpp
Normal file
@@ -0,0 +1,788 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "canvas_modes.h"
|
||||
#include "layout.h"
|
||||
#include "canvas.h"
|
||||
#include "shader.h"
|
||||
#include "node_canvas.h"
|
||||
#include "app.h"
|
||||
#include "util.h"
|
||||
|
||||
NodeCanvas* CanvasMode::node;
|
||||
ui::Canvas* CanvasMode::canvas;
|
||||
|
||||
void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
break;
|
||||
case kEventType::MouseDownR:
|
||||
if (App::I.keys[(int)kKey::KeyAlt])
|
||||
break;
|
||||
m_draggingR = true;
|
||||
m_dragR_start = me->m_pos;
|
||||
m_pan_start = canvas->m_pan;
|
||||
node->mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseUpR:
|
||||
m_draggingR = false;
|
||||
node->mouse_release();
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (m_draggingR)
|
||||
canvas->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * glm::vec2(-1, -1) * (canvas->m_cam_fov / 85.f);
|
||||
canvas->m_cam_rot = canvas->m_pan * 0.003f;
|
||||
break;
|
||||
case kEventType::MouseScroll:
|
||||
m_zoom_canvas += me->m_scroll_delta * 0.1f;
|
||||
canvas->m_cam_fov -= me->m_scroll_delta * 2.0f;
|
||||
App::I.brush_update();
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
m_draggingR = false;
|
||||
node->mouse_release();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeBasicCamera::on_GestureEvent(GestureEvent* ge)
|
||||
{
|
||||
switch (ge->m_type)
|
||||
{
|
||||
case kEventType::GestureStart:
|
||||
m_pan_start = canvas->m_pan;
|
||||
m_zoom_start = m_zoom_canvas;
|
||||
m_camera_fov = canvas->m_cam_fov;
|
||||
break;
|
||||
case kEventType::GestureMove:
|
||||
canvas->m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(-1, -1) * 0.3f * (canvas->m_cam_fov / 85.f);
|
||||
canvas->m_cam_fov = m_camera_fov - ge->m_distance_delta * .05f;
|
||||
canvas->m_cam_rot = canvas->m_pan * 0.003f;
|
||||
App::I.brush_update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
if (App::I.keys[(int)kKey::KeyAlt] || m_picking)
|
||||
{
|
||||
m_picking = true;
|
||||
canvas->pick_start();
|
||||
glm::vec4 pix = canvas->pick_get(loc);
|
||||
canvas->m_current_brush.m_tip_color = pix;
|
||||
App::I.color->set_color(pix);
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->stroke_start(loc, me->m_pressure, canvas->m_current_brush);
|
||||
}
|
||||
m_dragging = true;
|
||||
node->mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
if (m_dragging && !m_picking)
|
||||
{
|
||||
node->mouse_release();
|
||||
canvas->stroke_end();
|
||||
}
|
||||
if (m_dragging && m_picking)
|
||||
{
|
||||
node->mouse_release();
|
||||
glm::vec4 pix = canvas->pick_get(loc);
|
||||
canvas->m_current_brush.m_tip_color = pix;
|
||||
App::I.color->set_color(pix);
|
||||
canvas->pick_end();
|
||||
}
|
||||
m_dragging = false;
|
||||
m_picking = false;
|
||||
break;
|
||||
case kEventType::MouseDownR:
|
||||
if (App::I.keys[(int)kKey::KeyAlt])
|
||||
{
|
||||
m_resizing = true;
|
||||
m_dragging = true;
|
||||
m_size_pos_start = m_cur_pos;
|
||||
m_size_value_start = canvas->m_current_brush.m_tip_size;
|
||||
node->mouse_capture();
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseUpR:
|
||||
if (m_dragging && m_resizing)
|
||||
{
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
m_resizing = false;
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (m_dragging && !m_picking && !m_resizing)
|
||||
canvas->stroke_update(loc, me->m_pressure);
|
||||
if (m_dragging && m_picking)
|
||||
{
|
||||
glm::vec4 pix = canvas->pick_get(loc);
|
||||
canvas->m_current_brush.m_tip_color = pix;
|
||||
App::I.color->set_color(pix);
|
||||
}
|
||||
if (m_dragging && m_resizing)
|
||||
{
|
||||
auto diff = m_cur_pos - m_size_pos_start;
|
||||
canvas->m_current_brush.m_tip_size = m_size_value_start + diff.x * 0.001f;
|
||||
}
|
||||
m_cur_pos = loc;
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
if (m_dragging)
|
||||
{
|
||||
canvas->stroke_cancel();
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
}
|
||||
if (m_picking)
|
||||
m_picking = false;
|
||||
if (m_resizing)
|
||||
m_resizing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
#ifndef __IOS__
|
||||
//if (!m_dragging)
|
||||
{
|
||||
auto pos = m_resizing ? m_size_pos_start : m_cur_pos;
|
||||
if (App::I.keys[(int)kKey::KeyAlt] && !m_resizing)
|
||||
pos.x = pos.x - canvas->m_current_brush.m_tip_size * 500;
|
||||
ui::ShaderManager::use(ui::kShader::StrokePreview);
|
||||
ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_current_brush.m_tip_flow);
|
||||
auto tip_color = glm::vec4(glm::vec3(canvas->m_current_brush.m_tip_color), 1);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color);
|
||||
//ui::ShaderManager::u_int(ui::kShaderUniform::Highlight, 0);
|
||||
float tip_scale = 1.f / glm::tan(glm::radians(ui::Canvas::I->m_cam_fov * 0.5f));
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP,
|
||||
glm::scale(glm::vec3(1, -1, 1)) *
|
||||
ortho *
|
||||
glm::translate(glm::vec3(pos, 0)) *
|
||||
glm::scale(glm::vec3(canvas->m_current_brush.m_tip_size * 800.f * tip_scale))
|
||||
);
|
||||
glEnable(GL_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
auto& tex = TextureManager::get(canvas->m_current_brush.m_tex_id);
|
||||
tex.bind();
|
||||
canvas->m_sampler_brush.bind(0);
|
||||
canvas->m_plane.draw_fill();
|
||||
tex.unbind();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CanvasModePen::leave()
|
||||
{
|
||||
m_brush = canvas->m_current_brush;
|
||||
}
|
||||
|
||||
void CanvasModePen::enter()
|
||||
{
|
||||
m_cur_pos = ui::Canvas::I->m_cur_pos;
|
||||
if (m_valid_brush)
|
||||
{
|
||||
canvas->m_current_brush = m_brush;
|
||||
App::I.brush_update();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_brush = canvas->m_current_brush;
|
||||
m_valid_brush = true;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
node->mouse_capture();
|
||||
m_dragging = true;
|
||||
m_drag_start = loc;
|
||||
m_drag_pos = loc;
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
node->mouse_release();
|
||||
if (m_dragging)
|
||||
{
|
||||
canvas->stroke_start(m_drag_start, 1.f, canvas->m_current_brush);
|
||||
canvas->stroke_update(m_drag_pos, 1.f);
|
||||
canvas->stroke_end();
|
||||
}
|
||||
m_dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (m_dragging)
|
||||
m_drag_pos = loc;
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
if (m_dragging)
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, canvas->m_current_brush.m_tip_color);
|
||||
static glm::vec4 AB[2];
|
||||
AB[0] = { m_drag_start, 0, 1 };
|
||||
AB[1] = { m_drag_pos, 0, 1 };
|
||||
AB[0].y = canvas->m_box.w - AB[0].y - 1; // invert Y
|
||||
AB[1].y = canvas->m_box.w - AB[1].y - 1; // invert Y
|
||||
m_line.update_vertices(AB);
|
||||
m_line.draw_stroke();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeLine::init()
|
||||
{
|
||||
m_line.create();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownR:
|
||||
canvas->m_cam_pos = { 0, 0, 0 };
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_dragging = true;
|
||||
m_drag_start = me->m_pos;
|
||||
m_pos_start = xy(canvas->m_cam_pos);
|
||||
node->mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
canvas->m_cam_pos = { 0, 0, 0 };
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (m_dragging)
|
||||
canvas->m_cam_pos = glm::vec3(m_pos_start + (me->m_pos - m_drag_start) * glm::vec2(1, -1) * 0.001f, canvas->m_cam_pos.z);
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeGrid::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
node->mouse_capture();
|
||||
glm::vec3 ro, rd, hit_o, hit_d;
|
||||
glm::vec2 fb_pos;
|
||||
if (canvas->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, m_plane_id))
|
||||
{
|
||||
m_lines.push_back({ hit_o, hit_d });
|
||||
origin = hit_o;
|
||||
dir = hit_d;
|
||||
m_dragging = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseUpL:
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
//commit();
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
{
|
||||
glm::vec3 ro, rd, hit_o, hit_d;
|
||||
glm::vec2 hit_fb;
|
||||
if (m_dragging && canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, m_plane_id))
|
||||
{
|
||||
m_lines.back() = { hit_o, hit_d };
|
||||
origin = hit_o;
|
||||
dir = hit_d;
|
||||
m_dragging = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseCancel:
|
||||
if (m_dragging)
|
||||
m_lines.pop_back();
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeGrid::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
//if (m_dragging)
|
||||
for (auto l : m_lines)
|
||||
{
|
||||
auto origin = l.o;
|
||||
auto dir = l.d;
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 0, 0, 1});
|
||||
static glm::vec4 AB[2];
|
||||
AB[0] = {origin - dir * 10.f, 1};
|
||||
AB[1] = {origin + dir * 10.f, 1 };
|
||||
m_line.update_vertices(AB);
|
||||
m_line.draw_stroke();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeGrid::init()
|
||||
{
|
||||
m_line.create();
|
||||
}
|
||||
|
||||
void CanvasModeGrid::commit()
|
||||
{
|
||||
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj){
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 0, 0, 1});
|
||||
static glm::vec4 AB[2];
|
||||
AB[0] = {origin - dir * 10.f, 1};
|
||||
AB[1] = {origin + dir * 10.f, 1 };
|
||||
m_line.update_vertices(AB);
|
||||
m_line.draw_stroke();
|
||||
};
|
||||
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void CanvasModeGrid::clear()
|
||||
{
|
||||
m_lines.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeMaskFree::init()
|
||||
{
|
||||
m_shape.create();
|
||||
}
|
||||
|
||||
void CanvasModeMaskFree::leave()
|
||||
{
|
||||
// canvas->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(), std::placeholders::_1, std::placeholders::_2));
|
||||
// m_points.clear();
|
||||
}
|
||||
|
||||
void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
static glm::vec2 oldpos;
|
||||
static glm::vec2 oldvec;
|
||||
static float acc = 0.f;
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
node->mouse_capture();
|
||||
m_dragging = true;
|
||||
m_points2d.clear();
|
||||
m_points.clear();
|
||||
oldpos = loc;
|
||||
oldvec = {1.f, 0.f};
|
||||
acc = 0;
|
||||
ui::Shape::vertex_t vert;
|
||||
vert.pos = glm::vec4(loc, 0, 1);
|
||||
m_points2d.push_back(loc);
|
||||
m_points2d.push_back(loc);
|
||||
m_points.push_back(vert);
|
||||
m_points.push_back(vert);
|
||||
canvas->m_smask.clear({0, 0, 0, 0});
|
||||
canvas->m_smask_active = true;
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseUpL:
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
if (m_points2d.size() > 3)
|
||||
{
|
||||
std::vector<std::shared_ptr<p2t::Point>> points;
|
||||
for (int i = 0; i < (int)m_points2d.size() - 1; i++)
|
||||
points.emplace_back(std::make_shared<p2t::Point>(m_points2d[i].x, m_points2d[i].y));
|
||||
auto v = canvas->triangulate(points);
|
||||
canvas->project2Dpoints(v);
|
||||
LOG("%d points", (int)v.size());
|
||||
|
||||
m_shape.update_vertices(v.data(), (int)v.size());
|
||||
|
||||
if (!m_points.empty())
|
||||
{
|
||||
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
|
||||
//glEnable(GL_BLEND);
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1});
|
||||
m_shape.draw_fill();
|
||||
};
|
||||
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask);
|
||||
|
||||
//m_points.clear();
|
||||
// close the path
|
||||
m_points.push_back(m_points[m_points.size() - 2]);
|
||||
m_points.push_back(m_points.front());
|
||||
canvas->project2Dpoints(m_points);
|
||||
// reset m_shape to contour rendering
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->m_smask_active = false;
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
{
|
||||
if (m_dragging)
|
||||
{
|
||||
auto v = loc-oldpos;
|
||||
float len = glm::length(v);
|
||||
if (len > 5)
|
||||
{
|
||||
m_points.back().pos = glm::vec4(loc, 0, 1);
|
||||
m_points2d.back() = loc;
|
||||
|
||||
v = glm::normalize(v);
|
||||
float d = 1-glm::dot(v, oldvec);
|
||||
acc += d;
|
||||
oldpos = loc;
|
||||
oldvec = v;
|
||||
if (acc > 0.001) // angle change tollerance
|
||||
{
|
||||
//LOG("d=%f acc=%f", d, acc);
|
||||
acc = 0;
|
||||
|
||||
m_points2d.push_back(loc);
|
||||
ui::Shape::vertex_t vert;
|
||||
vert.pos = glm::vec4(loc, 0, 1);
|
||||
m_points.push_back(vert);
|
||||
m_points.push_back(vert);
|
||||
}
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseCancel:
|
||||
if (m_dragging)
|
||||
{
|
||||
m_points.pop_back();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
if (m_points.size() < 4)
|
||||
{
|
||||
m_points.clear();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeMaskFree::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
if (m_points.size() > 3)
|
||||
{
|
||||
if (m_dragging)
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, glm::scale(glm::vec3(1,-1,1)) * ortho);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
//m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill();
|
||||
m_shape.draw_stroke();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
m_shape.draw_stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeMaskLine::init()
|
||||
{
|
||||
m_shape.create();
|
||||
}
|
||||
|
||||
void CanvasModeMaskLine::leave()
|
||||
{
|
||||
if (m_points2d.size() > 3)
|
||||
{
|
||||
std::vector<std::shared_ptr<p2t::Point>> points;
|
||||
for (int i = 0; i < (int)m_points2d.size(); i++)
|
||||
points.emplace_back(std::make_shared<p2t::Point>(m_points2d[i].x, m_points2d[i].y));
|
||||
auto v = canvas->triangulate(points);
|
||||
canvas->project2Dpoints(v);
|
||||
LOG("%d points", (int)v.size());
|
||||
|
||||
m_shape.update_vertices(v.data(), (int)v.size());
|
||||
|
||||
if (!m_points.empty())
|
||||
{
|
||||
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
|
||||
//glEnable(GL_BLEND);
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1});
|
||||
m_shape.draw_fill();
|
||||
};
|
||||
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask);
|
||||
|
||||
//m_points.clear();
|
||||
// close the path
|
||||
m_points.push_back(m_points.back());
|
||||
m_points.push_back(m_points.back());
|
||||
m_points.push_back(m_points.front());
|
||||
canvas->project2Dpoints(m_points);
|
||||
// reset m_shape to contour rendering
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->m_smask_active = false;
|
||||
}
|
||||
m_active_tool = false;
|
||||
}
|
||||
|
||||
void CanvasModeMaskLine::enter()
|
||||
{
|
||||
m_points2d.clear();
|
||||
m_points.clear();
|
||||
canvas->m_smask.clear({0, 0, 0, 0});
|
||||
canvas->m_smask_active = true;
|
||||
m_active_tool = true;
|
||||
}
|
||||
|
||||
void CanvasModeMaskLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
node->mouse_capture();
|
||||
m_dragging = true;
|
||||
m_points2d.push_back(loc);
|
||||
ui::Shape::vertex_t vert;
|
||||
vert.pos = glm::vec4(loc, 0, 1);
|
||||
m_points.push_back(vert);
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseUpL:
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
if (m_points.size() > 1)
|
||||
{
|
||||
m_points.push_back(m_points.back());
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
{
|
||||
if (m_dragging)
|
||||
{
|
||||
m_points.back().pos = glm::vec4(loc, 0, 1);
|
||||
m_points2d.back() = loc;
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseCancel:
|
||||
if (m_dragging)
|
||||
{
|
||||
m_points.pop_back();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
if (m_points.size() < 4)
|
||||
{
|
||||
m_points.clear();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeMaskLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
if (m_points.size() > 3)
|
||||
{
|
||||
if (m_active_tool)
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, glm::scale(glm::vec3(1,-1,1)) * ortho);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
//m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill();
|
||||
m_shape.draw_stroke();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
m_shape.draw_stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CanvasModeFill::init()
|
||||
{
|
||||
m_shape.create();
|
||||
}
|
||||
|
||||
void CanvasModeFill::leave()
|
||||
{
|
||||
if (m_points.size() > 2)
|
||||
{
|
||||
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1});
|
||||
m_shape.draw_fill();
|
||||
};
|
||||
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask);
|
||||
m_points.clear();
|
||||
canvas->m_smask_active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->m_smask_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
switch (me->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
node->mouse_capture();
|
||||
m_dragging = true;
|
||||
glm::vec3 ro, rd, hit_o, hit_d;
|
||||
glm::vec2 hit_fb;
|
||||
int plane_id;
|
||||
if (canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0))
|
||||
{
|
||||
m_dirty_planes[plane_id]++;
|
||||
ui::Shape::vertex_t v;
|
||||
v.pos = glm::vec4(hit_o, 1);
|
||||
v.uvs = glm::vec2(0);
|
||||
if (m_points.size() < 3)
|
||||
{
|
||||
m_points.push_back(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto last = m_points.back();
|
||||
m_points.push_back(m_points[0]);
|
||||
m_points.push_back(last);
|
||||
m_points.push_back(v);
|
||||
}
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
canvas->m_smask.clear({0, 0, 0, 0});
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseUpL:
|
||||
node->mouse_release();
|
||||
m_dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
{
|
||||
glm::vec3 ro, rd, hit_o, hit_d;
|
||||
glm::vec2 fb_pos;
|
||||
int plane_id;
|
||||
if (m_dragging && canvas->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, plane_id))
|
||||
{
|
||||
ui::Shape::vertex_t v;
|
||||
v.pos = glm::vec4(hit_o, 1);
|
||||
v.uvs = glm::vec2(0);
|
||||
m_points.back() = v;
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventType::MouseCancel:
|
||||
if (m_dragging)
|
||||
{
|
||||
m_points.pop_back();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
m_dragging = false;
|
||||
node->mouse_release();
|
||||
if (m_points.size() < 4)
|
||||
{
|
||||
m_points.clear();
|
||||
m_shape.update_vertices(m_points.data(), (int)m_points.size());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasModeFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
{
|
||||
if (!m_points.empty())
|
||||
{
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, .25 });
|
||||
m_dragging ? m_shape.draw_fill() : m_shape.draw_stroke();
|
||||
}
|
||||
}
|
||||
139
src/canvas_modes.h
Normal file
139
src/canvas_modes.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
#include "event.h"
|
||||
#include "shape.h"
|
||||
#include "brush.h"
|
||||
#include <poly2tri.h>
|
||||
|
||||
NS_START
|
||||
class Canvas;
|
||||
NS_END
|
||||
|
||||
class CanvasMode
|
||||
{
|
||||
public:
|
||||
static class NodeCanvas* node;
|
||||
static ui::Canvas* canvas;
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) {}
|
||||
virtual void on_KeyEvent(KeyEvent* ke) {}
|
||||
virtual void on_GestureEvent(GestureEvent* ge) {}
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) {}
|
||||
virtual void init() {}
|
||||
virtual void enter() {}
|
||||
virtual void leave() {}
|
||||
};
|
||||
|
||||
class CanvasModeBasicCamera : public CanvasMode
|
||||
{
|
||||
bool m_draggingR = false;
|
||||
glm::vec2 m_dragR_start;
|
||||
glm::vec2 m_pan_start;
|
||||
float m_camera_fov;
|
||||
float m_zoom_canvas = 1.f;
|
||||
float m_zoom_start;
|
||||
public:
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void on_GestureEvent(GestureEvent* ge) override;
|
||||
};
|
||||
|
||||
class CanvasModePen : public CanvasMode
|
||||
{
|
||||
bool m_dragging = false;
|
||||
glm::vec2 m_pan_start;
|
||||
glm::vec2 m_cur_pos;
|
||||
glm::vec2 m_size_pos_start;
|
||||
float m_size_value_start;
|
||||
float m_camera_fov;
|
||||
float m_zoom_canvas = 1.f;
|
||||
float m_zoom_start;
|
||||
bool m_valid_brush = false;
|
||||
ui::Brush m_brush;
|
||||
// resizing the tip
|
||||
bool m_resizing = false;
|
||||
public:
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void enter() override;
|
||||
virtual void leave() override;
|
||||
bool m_picking = false;
|
||||
};
|
||||
|
||||
class CanvasModeLine : public CanvasMode
|
||||
{
|
||||
ui::LineSegment m_line;
|
||||
bool m_dragging = false;
|
||||
glm::vec2 m_drag_start;
|
||||
glm::vec2 m_drag_pos;
|
||||
public:
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void init() override;
|
||||
};
|
||||
|
||||
class CanvasModeGrid : public CanvasMode
|
||||
{
|
||||
ui::LineSegment m_line;
|
||||
glm::vec3 origin;
|
||||
glm::vec3 dir;
|
||||
int m_plane_id;
|
||||
bool m_dragging = false;
|
||||
struct ray_t { glm::vec3 o, d; };
|
||||
std::vector<ray_t> m_lines;
|
||||
public:
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void init() override;
|
||||
void commit();
|
||||
void clear();
|
||||
};
|
||||
|
||||
class CanvasModeCamera : public CanvasMode
|
||||
{
|
||||
bool m_dragging = false;
|
||||
glm::vec2 m_drag_start;
|
||||
glm::vec2 m_pos_start;
|
||||
public:
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
};
|
||||
|
||||
class CanvasModeFill : public CanvasMode
|
||||
{
|
||||
ui::DynamicShape m_shape;
|
||||
bool m_dragging = false;
|
||||
std::vector<ui::Shape::vertex_t> m_points;
|
||||
std::map<int, int> m_dirty_planes;
|
||||
public:
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void init() override;
|
||||
virtual void leave() override;
|
||||
};
|
||||
|
||||
class CanvasModeMaskFree : public CanvasMode
|
||||
{
|
||||
ui::DynamicShape m_shape;
|
||||
bool m_dragging = false;
|
||||
std::vector<ui::Shape::vertex_t> m_points;
|
||||
std::vector<glm::vec2> m_points2d;
|
||||
std::map<int, int> m_dirty_planes;
|
||||
public:
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void init() override;
|
||||
virtual void leave() override;
|
||||
};
|
||||
|
||||
class CanvasModeMaskLine : public CanvasMode
|
||||
{
|
||||
ui::DynamicShape m_shape;
|
||||
bool m_dragging = false;
|
||||
std::vector<ui::Shape::vertex_t> m_points;
|
||||
std::vector<glm::vec2> m_points2d;
|
||||
std::map<int, int> m_dirty_planes;
|
||||
bool m_active_tool = false;
|
||||
public:
|
||||
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
|
||||
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;
|
||||
virtual void init() override;
|
||||
virtual void enter() override;
|
||||
virtual void leave() override;
|
||||
};
|
||||
3
src/event.cpp
Normal file
3
src/event.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "pch.h"
|
||||
#include "event.h"
|
||||
|
||||
154
src/event.h
Normal file
154
src/event.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
enum class kKey : uint8_t
|
||||
{
|
||||
Unknown,
|
||||
AndroidVolumeUp,
|
||||
AndroidVolumeDown,
|
||||
AndroidHome,
|
||||
AndroidBack,
|
||||
KeySpacebar,
|
||||
KeyA,
|
||||
KeyB,
|
||||
KeyC,
|
||||
KeyD,
|
||||
KeyE,
|
||||
KeyF,
|
||||
KeyG,
|
||||
KeyH,
|
||||
KeyI,
|
||||
KeyJ,
|
||||
KeyK,
|
||||
KeyL,
|
||||
KeyM,
|
||||
KeyN,
|
||||
KeyO,
|
||||
KeyP,
|
||||
KeyQ,
|
||||
KeyR,
|
||||
KeyS,
|
||||
KeyT,
|
||||
KeyU,
|
||||
KeyV,
|
||||
KeyW,
|
||||
KeyX,
|
||||
KeyY,
|
||||
KeyZ,
|
||||
Key0,
|
||||
Key1,
|
||||
Key2,
|
||||
Key3,
|
||||
Key4,
|
||||
Key5,
|
||||
Key6,
|
||||
Key7,
|
||||
Key8,
|
||||
Key9,
|
||||
KeyF1,
|
||||
KeyF2,
|
||||
KeyF3,
|
||||
KeyF4,
|
||||
KeyF5,
|
||||
KeyF6,
|
||||
KeyF7,
|
||||
KeyF8,
|
||||
KeyF9,
|
||||
KeyF10,
|
||||
KeyF11,
|
||||
KeyF12,
|
||||
KeyF13,
|
||||
KeyF14,
|
||||
KeyF15,
|
||||
KeyF16,
|
||||
KeyF17,
|
||||
KeyF18,
|
||||
KeyF19,
|
||||
KeyF20,
|
||||
KeyF21,
|
||||
KeyF22,
|
||||
KeyF23,
|
||||
KeyF24,
|
||||
KeyAlt,
|
||||
KeyCtrl,
|
||||
KeyShift,
|
||||
KeyTab,
|
||||
};
|
||||
|
||||
enum class kEventResult : uint8_t
|
||||
{
|
||||
Consumed,
|
||||
Available,
|
||||
};
|
||||
|
||||
enum class kEventCategory : uint8_t
|
||||
{
|
||||
MouseEvent,
|
||||
KeyEvent,
|
||||
ButtonEvent,
|
||||
GestureEvent,
|
||||
};
|
||||
|
||||
enum class kEventType : uint8_t
|
||||
{
|
||||
MouseDownL,
|
||||
MouseDownR,
|
||||
MouseMove,
|
||||
MouseUpL,
|
||||
MouseUpR,
|
||||
MouseEnter,
|
||||
MouseLeave,
|
||||
MouseScroll,
|
||||
MouseCancel,
|
||||
GestureStart,
|
||||
GestureMove,
|
||||
GestureEnd,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
KeyChar,
|
||||
ButtonDown,
|
||||
ButtonUp,
|
||||
};
|
||||
|
||||
enum class kEventSource : uint8_t
|
||||
{
|
||||
Mouse,
|
||||
Touch,
|
||||
Stylus,
|
||||
};
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
kEventCategory m_cat;
|
||||
kEventType m_type;
|
||||
};
|
||||
|
||||
class MouseEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseEvent() { m_cat = kEventCategory::MouseEvent; }
|
||||
glm::vec2 m_pos;
|
||||
float m_pressure = 0;
|
||||
float m_scroll_delta = 0;
|
||||
kEventSource m_source = kEventSource::Mouse;
|
||||
};
|
||||
|
||||
class KeyEvent : public Event
|
||||
{
|
||||
public:
|
||||
KeyEvent() { m_cat = kEventCategory::KeyEvent; }
|
||||
kKey m_key;
|
||||
char m_char;
|
||||
};
|
||||
|
||||
class GestureEvent : public Event
|
||||
{
|
||||
public:
|
||||
GestureEvent() { m_cat = kEventCategory::GestureEvent; }
|
||||
float m_distance;
|
||||
float m_distance_delta;
|
||||
float m_angle;
|
||||
float m_angle_delta;
|
||||
glm::vec2 m_pos;
|
||||
glm::vec2 m_pos_delta;
|
||||
};
|
||||
136
src/font.cpp
Normal file
136
src/font.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "font.h"
|
||||
#include "shader.h"
|
||||
#include "asset.h"
|
||||
|
||||
std::map<kFont, Font> FontManager::m_fonts;
|
||||
Sampler FontManager::m_sampler;
|
||||
|
||||
bool Font::load(const char* ttf, int font_size)
|
||||
{
|
||||
Asset file;
|
||||
LOG("Font::load %s", ttf);
|
||||
if (file.open(ttf) && file.read_all())
|
||||
{
|
||||
LOG("Font::load loaded");
|
||||
auto bitmap = std::make_unique<uint8_t[]>(w*h);
|
||||
chars.resize(num_chars);
|
||||
stbtt_BakeFontBitmap(file.m_data, 0, (float)font_size*2, bitmap.get(), w, h, start_char, num_chars, chars.data());
|
||||
font_tex.create(w, h, GL_R8, GL_RED, bitmap.get());
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontManager::init()
|
||||
{
|
||||
m_sampler.create();
|
||||
}
|
||||
|
||||
bool FontManager::load(kFont id, const char* ttf, int sz)
|
||||
{
|
||||
return m_fonts[id].load(ttf, sz);
|
||||
}
|
||||
|
||||
const Font& FontManager::get(kFont id)
|
||||
{
|
||||
return m_fonts[id];
|
||||
}
|
||||
|
||||
bool TextMesh::create()
|
||||
{
|
||||
glGenBuffers(2, font_buffers);
|
||||
#if USE_VBO
|
||||
glGenVertexArrays(1, &font_array);
|
||||
glBindVertexArray(font_array);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float)*2));
|
||||
glBindVertexArray(0);
|
||||
#endif // USE_VBO
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextMesh::update(kFont id, const char* text)
|
||||
{
|
||||
font_id = id;
|
||||
auto& f = FontManager::get(id);
|
||||
if (f.chars.size())
|
||||
{
|
||||
const auto len = strlen(text);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
std::vector<glm::vec4> v;
|
||||
std::vector<GLushort> idx;
|
||||
glm::vec2 bbmin(FLT_MAX);
|
||||
glm::vec2 bbmax(-FLT_MAX);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int c = text[i] - f.start_char;
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c, &x, &y, &q, true);
|
||||
auto n = (int)v.size();
|
||||
v.emplace_back(q.x0/2.f, q.y1/2.f, q.s0, q.t1);
|
||||
v.emplace_back(q.x0/2.f, q.y0/2.f, q.s0, q.t0);
|
||||
v.emplace_back(q.x1/2.f, q.y0/2.f, q.s1, q.t0);
|
||||
v.emplace_back(q.x1/2.f, q.y1/2.f, q.s1, q.t1);
|
||||
idx.push_back(n+0);
|
||||
idx.push_back(n+1);
|
||||
idx.push_back(n+2);
|
||||
idx.push_back(n+0);
|
||||
idx.push_back(n+2);
|
||||
idx.push_back(n+3);
|
||||
bbmin = glm::min(bbmin, { q.x0/2.f, q.y0/2.f });
|
||||
bbmax = glm::max(bbmax, { q.x1/2.f, q.y1/2.f });
|
||||
}
|
||||
for (int i = 0; i < len*4; i++)
|
||||
{
|
||||
v[i] -= glm::vec4(bbmin, 0, 0);
|
||||
}
|
||||
bb = bbmax - bbmin;
|
||||
font_array_count = (int)idx.size();
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(glm::vec4), v.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TextMesh::draw()
|
||||
{
|
||||
auto& f = FontManager::get(font_id);
|
||||
if (f.font_tex.ready())
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
f.font_tex.bind();
|
||||
FontManager::m_sampler.bind(0);
|
||||
|
||||
#if USE_VBO
|
||||
glBindVertexArray(font_array);
|
||||
glDrawElements(GL_TRIANGLES, font_array_count, GL_UNSIGNED_SHORT, 0);
|
||||
glBindVertexArray(0);
|
||||
#else
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float) * 2));
|
||||
glDrawElements(GL_TRIANGLES, font_array_count, GL_UNSIGNED_SHORT, 0);
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
#endif // USE_VBO
|
||||
|
||||
f.font_tex.unbind();
|
||||
FontManager::m_sampler.unbind();
|
||||
}
|
||||
}
|
||||
48
src/font.h
Normal file
48
src/font.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "texture.h"
|
||||
#include "util.h"
|
||||
#include <stb/stb_truetype.h>
|
||||
|
||||
enum class kFont : uint16_t
|
||||
{
|
||||
Arial_11 = const_hash("arial-11"),
|
||||
Arial_30 = const_hash("arial-30"),
|
||||
};
|
||||
|
||||
class Font
|
||||
{
|
||||
public:
|
||||
const int w = 512;
|
||||
const int h = 512;
|
||||
const int num_chars = 96;
|
||||
const int start_char = 32;
|
||||
stbtt_fontinfo font;
|
||||
Texture2D font_tex;
|
||||
std::vector<stbtt_bakedchar> chars;
|
||||
|
||||
bool load(const char* ttf, int sz);
|
||||
};
|
||||
|
||||
class FontManager
|
||||
{
|
||||
public:
|
||||
static std::map<kFont, Font> m_fonts;
|
||||
static Sampler m_sampler;
|
||||
static void init();
|
||||
static bool load(kFont id, const char* ttf, int sz);
|
||||
static const Font& get(kFont id);
|
||||
static void invalidate() { m_fonts.clear(); }
|
||||
};
|
||||
|
||||
class TextMesh
|
||||
{
|
||||
public:
|
||||
GLuint font_array = 0;
|
||||
int font_array_count = 0;
|
||||
GLuint font_buffers[2] = {0, 0};
|
||||
kFont font_id;
|
||||
glm::vec2 bb = { 0, 0 };
|
||||
bool create();
|
||||
void update(kFont id, const char* text);
|
||||
void draw();
|
||||
};
|
||||
80
src/image.cpp
Normal file
80
src/image.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "image.h"
|
||||
#include "asset.h"
|
||||
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
using namespace ui;
|
||||
|
||||
bool Image::load(std::string filename)
|
||||
{
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
Asset file;
|
||||
if (!(file.open(filename.c_str()) && file.read_all()))
|
||||
{
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
uint8_t* buffer = stbi_load_from_memory(file.m_data, file.m_len, &width, &height, nullptr, 4);
|
||||
file.close();
|
||||
comp = 4;
|
||||
m_data = std::unique_ptr<uint8_t[]>(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::load_file(std::string filename)
|
||||
{
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
uint8_t* buffer = stbi_load(filename.c_str(), &width, &height, nullptr, 4);
|
||||
comp = 4;
|
||||
m_data = std::unique_ptr<uint8_t[]>(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Image::flip()
|
||||
{
|
||||
auto flipped = std::make_unique<uint8_t[]>(width*height*4);
|
||||
int line_size = width * 4;
|
||||
const uint8_t* src = m_data.get();
|
||||
uint8_t* dst = flipped.get() + line_size * (height - 1);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
std::copy(src, src+line_size, dst);
|
||||
src += line_size;
|
||||
dst -= line_size;
|
||||
}
|
||||
std::swap(m_data, flipped);
|
||||
}
|
||||
|
||||
ui::Image ui::Image::resize(int w, int h)
|
||||
{
|
||||
Image ret;
|
||||
ret.create(w, h);
|
||||
auto temp = (glm::u8vec4*)ret.data();
|
||||
auto pixels = (glm::u8vec4*)data();
|
||||
float x_ratio = ((float)(width - 1)) / w;
|
||||
float y_ratio = ((float)(height - 1)) / h;
|
||||
int offset = 0;
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
int x = (int)(x_ratio * j);
|
||||
int y = (int)(y_ratio * i);
|
||||
float x_diff = (x_ratio * j) - x;
|
||||
float y_diff = (y_ratio * i) - y;
|
||||
int index = y * width + x;
|
||||
|
||||
// range is 0 to 255 thus bitwise AND with 0xff
|
||||
glm::vec4 A = pixels[index];
|
||||
glm::vec4 B = pixels[index + 1];
|
||||
glm::vec4 C = pixels[index + width];
|
||||
glm::vec4 D = pixels[index + width + 1];
|
||||
|
||||
// Y = A(1-w)(1-h) + B(w)(1-h) + C(h)(1-w) + Dwh
|
||||
glm::vec4 gray = A*(1 - x_diff)*(1 - y_diff) + B * (x_diff)*(1 - y_diff) +
|
||||
C * (y_diff)*(1 - x_diff) + D * (x_diff*y_diff);
|
||||
temp[offset++] = glm::clamp(gray, glm::vec4(0), glm::vec4(255));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
32
src/image.h
Normal file
32
src/image.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<uint8_t[]> m_data;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int comp = 4;
|
||||
bool load(std::string filename);
|
||||
bool load_file(std::string filename);
|
||||
const uint8_t* data() const { return m_data.get(); }
|
||||
int size() const { return width * height * comp; }
|
||||
void create(int w, int h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
comp = 4;
|
||||
m_data = std::make_unique<uint8_t[]>(size());
|
||||
}
|
||||
void copy_from(const uint8_t* data)
|
||||
{
|
||||
std::copy(data, data + size(), m_data.get());
|
||||
}
|
||||
void flip();
|
||||
void create() { m_data = std::make_unique<uint8_t[]>(size()); }
|
||||
Image resize(int w, int h);
|
||||
};
|
||||
|
||||
}
|
||||
439
src/keymap.h
Normal file
439
src/keymap.h
Normal file
@@ -0,0 +1,439 @@
|
||||
enum {
|
||||
kVK_ANSI_A = 0x00,
|
||||
kVK_ANSI_S = 0x01,
|
||||
kVK_ANSI_D = 0x02,
|
||||
kVK_ANSI_F = 0x03,
|
||||
kVK_ANSI_H = 0x04,
|
||||
kVK_ANSI_G = 0x05,
|
||||
kVK_ANSI_Z = 0x06,
|
||||
kVK_ANSI_X = 0x07,
|
||||
kVK_ANSI_C = 0x08,
|
||||
kVK_ANSI_V = 0x09,
|
||||
kVK_ANSI_B = 0x0B,
|
||||
kVK_ANSI_Q = 0x0C,
|
||||
kVK_ANSI_W = 0x0D,
|
||||
kVK_ANSI_E = 0x0E,
|
||||
kVK_ANSI_R = 0x0F,
|
||||
kVK_ANSI_Y = 0x10,
|
||||
kVK_ANSI_T = 0x11,
|
||||
kVK_ANSI_1 = 0x12,
|
||||
kVK_ANSI_2 = 0x13,
|
||||
kVK_ANSI_3 = 0x14,
|
||||
kVK_ANSI_4 = 0x15,
|
||||
kVK_ANSI_6 = 0x16,
|
||||
kVK_ANSI_5 = 0x17,
|
||||
kVK_ANSI_Equal = 0x18,
|
||||
kVK_ANSI_9 = 0x19,
|
||||
kVK_ANSI_7 = 0x1A,
|
||||
kVK_ANSI_Minus = 0x1B,
|
||||
kVK_ANSI_8 = 0x1C,
|
||||
kVK_ANSI_0 = 0x1D,
|
||||
kVK_ANSI_RightBracket = 0x1E,
|
||||
kVK_ANSI_O = 0x1F,
|
||||
kVK_ANSI_U = 0x20,
|
||||
kVK_ANSI_LeftBracket = 0x21,
|
||||
kVK_ANSI_I = 0x22,
|
||||
kVK_ANSI_P = 0x23,
|
||||
kVK_ANSI_L = 0x25,
|
||||
kVK_ANSI_J = 0x26,
|
||||
kVK_ANSI_Quote = 0x27,
|
||||
kVK_ANSI_K = 0x28,
|
||||
kVK_ANSI_Semicolon = 0x29,
|
||||
kVK_ANSI_Backslash = 0x2A,
|
||||
kVK_ANSI_Comma = 0x2B,
|
||||
kVK_ANSI_Slash = 0x2C,
|
||||
kVK_ANSI_N = 0x2D,
|
||||
kVK_ANSI_M = 0x2E,
|
||||
kVK_ANSI_Period = 0x2F,
|
||||
kVK_ANSI_Grave = 0x32,
|
||||
kVK_ANSI_KeypadDecimal = 0x41,
|
||||
kVK_ANSI_KeypadMultiply = 0x43,
|
||||
kVK_ANSI_KeypadPlus = 0x45,
|
||||
kVK_ANSI_KeypadClear = 0x47,
|
||||
kVK_ANSI_KeypadDivide = 0x4B,
|
||||
kVK_ANSI_KeypadEnter = 0x4C,
|
||||
kVK_ANSI_KeypadMinus = 0x4E,
|
||||
kVK_ANSI_KeypadEquals = 0x51,
|
||||
kVK_ANSI_Keypad0 = 0x52,
|
||||
kVK_ANSI_Keypad1 = 0x53,
|
||||
kVK_ANSI_Keypad2 = 0x54,
|
||||
kVK_ANSI_Keypad3 = 0x55,
|
||||
kVK_ANSI_Keypad4 = 0x56,
|
||||
kVK_ANSI_Keypad5 = 0x57,
|
||||
kVK_ANSI_Keypad6 = 0x58,
|
||||
kVK_ANSI_Keypad7 = 0x59,
|
||||
kVK_ANSI_Keypad8 = 0x5B,
|
||||
kVK_ANSI_Keypad9 = 0x5C
|
||||
};
|
||||
|
||||
/* keycodes for keys that are independent of keyboard layout*/
|
||||
enum {
|
||||
kVK_Return = 0x24,
|
||||
kVK_Tab = 0x30,
|
||||
kVK_Space = 0x31,
|
||||
kVK_Delete = 0x33,
|
||||
kVK_Escape = 0x35,
|
||||
kVK_Command = 0x37,
|
||||
kVK_Shift = 0x38,
|
||||
kVK_CapsLock = 0x39,
|
||||
kVK_Option = 0x3A,
|
||||
kVK_Control = 0x3B,
|
||||
kVK_RightShift = 0x3C,
|
||||
kVK_RightOption = 0x3D,
|
||||
kVK_RightControl = 0x3E,
|
||||
kVK_Function = 0x3F,
|
||||
kVK_F17 = 0x40,
|
||||
kVK_VolumeUp = 0x48,
|
||||
kVK_VolumeDown = 0x49,
|
||||
kVK_Mute = 0x4A,
|
||||
kVK_F18 = 0x4F,
|
||||
kVK_F19 = 0x50,
|
||||
kVK_F20 = 0x5A,
|
||||
kVK_F5 = 0x60,
|
||||
kVK_F6 = 0x61,
|
||||
kVK_F7 = 0x62,
|
||||
kVK_F3 = 0x63,
|
||||
kVK_F8 = 0x64,
|
||||
kVK_F9 = 0x65,
|
||||
kVK_F11 = 0x67,
|
||||
kVK_F13 = 0x69,
|
||||
kVK_F16 = 0x6A,
|
||||
kVK_F14 = 0x6B,
|
||||
kVK_F10 = 0x6D,
|
||||
kVK_F12 = 0x6F,
|
||||
kVK_F15 = 0x71,
|
||||
kVK_Help = 0x72,
|
||||
kVK_Home = 0x73,
|
||||
kVK_PageUp = 0x74,
|
||||
kVK_ForwardDelete = 0x75,
|
||||
kVK_F4 = 0x76,
|
||||
kVK_End = 0x77,
|
||||
kVK_F2 = 0x78,
|
||||
kVK_PageDown = 0x79,
|
||||
kVK_F1 = 0x7A,
|
||||
kVK_LeftArrow = 0x7B,
|
||||
kVK_RightArrow = 0x7C,
|
||||
kVK_DownArrow = 0x7D,
|
||||
kVK_UpArrow = 0x7E
|
||||
};
|
||||
|
||||
kKey convert_key(int key)
|
||||
{
|
||||
#define CASE(K,V) case K: return V;
|
||||
switch(key)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
CASE(kVK_ANSI_A, kKey::KeyA);
|
||||
CASE(kVK_ANSI_S, kKey::KeyS);
|
||||
CASE(kVK_ANSI_D, kKey::KeyD);
|
||||
CASE(kVK_ANSI_F, kKey::KeyF);
|
||||
CASE(kVK_ANSI_H, kKey::KeyH);
|
||||
CASE(kVK_ANSI_G, kKey::KeyG);
|
||||
CASE(kVK_ANSI_Z, kKey::KeyZ);
|
||||
CASE(kVK_ANSI_X, kKey::KeyX);
|
||||
CASE(kVK_ANSI_C, kKey::KeyC);
|
||||
CASE(kVK_ANSI_V, kKey::KeyV);
|
||||
CASE(kVK_ANSI_B, kKey::KeyB);
|
||||
CASE(kVK_ANSI_Q, kKey::KeyQ);
|
||||
CASE(kVK_ANSI_W, kKey::KeyW);
|
||||
CASE(kVK_ANSI_E, kKey::KeyE);
|
||||
CASE(kVK_ANSI_R, kKey::KeyR);
|
||||
CASE(kVK_ANSI_Y, kKey::KeyY);
|
||||
CASE(kVK_ANSI_T, kKey::KeyT);
|
||||
CASE(kVK_ANSI_1, kKey::Key1);
|
||||
CASE(kVK_ANSI_2, kKey::Key2);
|
||||
CASE(kVK_ANSI_3, kKey::Key3);
|
||||
CASE(kVK_ANSI_4, kKey::Key4);
|
||||
CASE(kVK_ANSI_6, kKey::Key6);
|
||||
CASE(kVK_ANSI_5, kKey::Key5);
|
||||
CASE(kVK_ANSI_Equal, kKey::Unknown);
|
||||
CASE(kVK_ANSI_9, kKey::Key9);
|
||||
CASE(kVK_ANSI_7, kKey::Key7);
|
||||
CASE(kVK_ANSI_Minus, kKey::Unknown);
|
||||
CASE(kVK_ANSI_8, kKey::Key8);
|
||||
CASE(kVK_ANSI_0, kKey::Key0);
|
||||
CASE(kVK_ANSI_RightBracket, kKey::Unknown);
|
||||
CASE(kVK_ANSI_O, kKey::KeyO);
|
||||
CASE(kVK_ANSI_U, kKey::KeyU);
|
||||
CASE(kVK_ANSI_LeftBracket, kKey::Unknown);
|
||||
CASE(kVK_ANSI_I, kKey::KeyI);
|
||||
CASE(kVK_ANSI_P, kKey::KeyP);
|
||||
CASE(kVK_ANSI_L, kKey::KeyL);
|
||||
CASE(kVK_ANSI_J, kKey::KeyJ);
|
||||
CASE(kVK_ANSI_Quote, kKey::Unknown);
|
||||
CASE(kVK_ANSI_K, kKey::KeyK);
|
||||
CASE(kVK_ANSI_Semicolon, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Backslash, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Comma, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Slash, kKey::Unknown);
|
||||
CASE(kVK_ANSI_N, kKey::KeyN);
|
||||
CASE(kVK_ANSI_M, kKey::KeyM);
|
||||
CASE(kVK_ANSI_Period, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Grave, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadDecimal, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadMultiply, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadPlus, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadClear, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadDivide, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadEnter, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadMinus, kKey::Unknown);
|
||||
CASE(kVK_ANSI_KeypadEquals, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad0, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad1, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad2, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad3, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad4, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad5, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad6, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad7, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad8, kKey::Unknown);
|
||||
CASE(kVK_ANSI_Keypad9, kKey::Unknown);
|
||||
CASE(kVK_Return, kKey::Unknown);
|
||||
CASE(kVK_Tab, kKey::KeyTab);
|
||||
CASE(kVK_Space, kKey::Unknown);
|
||||
CASE(kVK_Delete, kKey::Unknown);
|
||||
CASE(kVK_Escape, kKey::Unknown);
|
||||
CASE(kVK_Command, kKey::KeyCtrl);
|
||||
CASE(kVK_Shift, kKey::KeyShift);
|
||||
CASE(kVK_CapsLock, kKey::Unknown);
|
||||
CASE(kVK_Option, kKey::Unknown);
|
||||
CASE(kVK_Control, kKey::Unknown);
|
||||
CASE(kVK_RightShift, kKey::Unknown);
|
||||
CASE(kVK_RightOption, kKey::Unknown);
|
||||
CASE(kVK_RightControl, kKey::Unknown);
|
||||
CASE(kVK_Function, kKey::Unknown);
|
||||
CASE(kVK_F17, kKey::Unknown);
|
||||
CASE(kVK_VolumeUp, kKey::Unknown);
|
||||
CASE(kVK_VolumeDown, kKey::Unknown);
|
||||
CASE(kVK_Mute, kKey::Unknown);
|
||||
CASE(kVK_F18, kKey::KeyF18);
|
||||
CASE(kVK_F19, kKey::KeyF19);
|
||||
CASE(kVK_F20, kKey::KeyF20);
|
||||
CASE(kVK_F5, kKey::KeyF5);
|
||||
CASE(kVK_F6, kKey::KeyF6);
|
||||
CASE(kVK_F7, kKey::KeyF7);
|
||||
CASE(kVK_F3, kKey::KeyF3);
|
||||
CASE(kVK_F8, kKey::KeyF8);
|
||||
CASE(kVK_F9, kKey::KeyF9);
|
||||
CASE(kVK_F11, kKey::KeyF11);
|
||||
CASE(kVK_F13, kKey::KeyF13);
|
||||
CASE(kVK_F16, kKey::KeyF16);
|
||||
CASE(kVK_F14, kKey::KeyF14);
|
||||
CASE(kVK_F10, kKey::KeyF10);
|
||||
CASE(kVK_F12, kKey::KeyF12);
|
||||
CASE(kVK_F15, kKey::KeyF15);
|
||||
CASE(kVK_Help, kKey::Unknown);
|
||||
CASE(kVK_Home, kKey::Unknown);
|
||||
CASE(kVK_PageUp, kKey::Unknown);
|
||||
CASE(kVK_ForwardDelete, kKey::Unknown);
|
||||
CASE(kVK_F4, kKey::Unknown);
|
||||
CASE(kVK_End, kKey::Unknown);
|
||||
CASE(kVK_F2, kKey::Unknown);
|
||||
CASE(kVK_PageDown, kKey::Unknown);
|
||||
CASE(kVK_F1, kKey::Unknown);
|
||||
CASE(kVK_LeftArrow, kKey::Unknown);
|
||||
CASE(kVK_RightArrow, kKey::Unknown);
|
||||
CASE(kVK_DownArrow, kKey::Unknown);
|
||||
CASE(kVK_UpArrow, kKey::Unknown);
|
||||
#elif defined(_WIN32)
|
||||
CASE(VK_LBUTTON, kKey::Unknown);
|
||||
CASE(VK_RBUTTON, kKey::Unknown);
|
||||
CASE(VK_CANCEL, kKey::Unknown);
|
||||
CASE(VK_MBUTTON, kKey::Unknown);
|
||||
CASE(VK_XBUTTON1, kKey::Unknown);
|
||||
CASE(VK_XBUTTON2, kKey::Unknown);
|
||||
CASE(VK_BACK, kKey::Unknown);
|
||||
CASE(VK_TAB, kKey::KeyTab);
|
||||
CASE(VK_CLEAR, kKey::Unknown);
|
||||
CASE(VK_RETURN, kKey::Unknown);
|
||||
CASE(VK_SHIFT, kKey::KeyShift);
|
||||
CASE(VK_CONTROL, kKey::KeyCtrl);
|
||||
CASE(VK_MENU, kKey::KeyAlt);
|
||||
CASE(VK_PAUSE, kKey::Unknown);
|
||||
CASE(VK_CAPITAL, kKey::Unknown);
|
||||
CASE(VK_KANA, kKey::Unknown);
|
||||
//CASE(VK_HANGEUL, kKey::Unknown); // same as VK_KANA
|
||||
//CASE(VK_HANGUL, kKey::Unknown); // same as VK_KANA
|
||||
CASE(VK_JUNJA, kKey::Unknown);
|
||||
CASE(VK_FINAL, kKey::Unknown);
|
||||
CASE(VK_HANJA, kKey::Unknown);
|
||||
//CASE(VK_KANJI, kKey::Unknown); // same as VK_HANJA
|
||||
CASE(VK_ESCAPE, kKey::Unknown);
|
||||
CASE(VK_CONVERT, kKey::Unknown);
|
||||
CASE(VK_NONCONVERT, kKey::Unknown);
|
||||
CASE(VK_ACCEPT, kKey::Unknown);
|
||||
CASE(VK_MODECHANGE, kKey::Unknown);
|
||||
CASE(VK_SPACE, kKey::KeySpacebar);
|
||||
CASE(VK_PRIOR, kKey::Unknown);
|
||||
CASE(VK_NEXT, kKey::Unknown);
|
||||
CASE(VK_END, kKey::Unknown);
|
||||
CASE(VK_HOME, kKey::Unknown);
|
||||
CASE(VK_LEFT, kKey::Unknown);
|
||||
CASE(VK_UP, kKey::Unknown);
|
||||
CASE(VK_RIGHT, kKey::Unknown);
|
||||
CASE(VK_DOWN, kKey::Unknown);
|
||||
CASE(VK_SELECT, kKey::Unknown);
|
||||
CASE(VK_PRINT, kKey::Unknown);
|
||||
CASE(VK_EXECUTE, kKey::Unknown);
|
||||
CASE(VK_SNAPSHOT, kKey::Unknown);
|
||||
CASE(VK_INSERT, kKey::Unknown);
|
||||
CASE(VK_DELETE, kKey::Unknown);
|
||||
CASE(VK_HELP, kKey::Unknown);
|
||||
CASE('0', kKey::Key0);
|
||||
CASE('1', kKey::Key1);
|
||||
CASE('2', kKey::Key2);
|
||||
CASE('3', kKey::Key3);
|
||||
CASE('4', kKey::Key4);
|
||||
CASE('5', kKey::Key5);
|
||||
CASE('6', kKey::Key6);
|
||||
CASE('7', kKey::Key7);
|
||||
CASE('8', kKey::Key8);
|
||||
CASE('9', kKey::Key9);
|
||||
CASE('A', kKey::KeyA);
|
||||
CASE('B', kKey::KeyB);
|
||||
CASE('C', kKey::KeyC);
|
||||
CASE('D', kKey::KeyD);
|
||||
CASE('E', kKey::KeyE);
|
||||
CASE('F', kKey::KeyF);
|
||||
CASE('G', kKey::KeyG);
|
||||
CASE('H', kKey::KeyH);
|
||||
CASE('I', kKey::KeyI);
|
||||
CASE('J', kKey::KeyJ);
|
||||
CASE('K', kKey::KeyK);
|
||||
CASE('L', kKey::KeyL);
|
||||
CASE('M', kKey::KeyM);
|
||||
CASE('N', kKey::KeyN);
|
||||
CASE('O', kKey::KeyO);
|
||||
CASE('P', kKey::KeyP);
|
||||
CASE('Q', kKey::KeyQ);
|
||||
CASE('R', kKey::KeyR);
|
||||
CASE('S', kKey::KeyS);
|
||||
CASE('T', kKey::KeyT);
|
||||
CASE('U', kKey::KeyU);
|
||||
CASE('V', kKey::KeyV);
|
||||
CASE('W', kKey::KeyW);
|
||||
CASE('X', kKey::KeyX);
|
||||
CASE('Y', kKey::KeyY);
|
||||
CASE('Z', kKey::KeyZ);
|
||||
CASE(VK_LWIN, kKey::Unknown);
|
||||
CASE(VK_RWIN, kKey::Unknown);
|
||||
CASE(VK_APPS, kKey::Unknown);
|
||||
CASE(VK_SLEEP, kKey::Unknown);
|
||||
CASE(VK_NUMPAD0, kKey::Unknown);
|
||||
CASE(VK_NUMPAD1, kKey::Unknown);
|
||||
CASE(VK_NUMPAD2, kKey::Unknown);
|
||||
CASE(VK_NUMPAD3, kKey::Unknown);
|
||||
CASE(VK_NUMPAD4, kKey::Unknown);
|
||||
CASE(VK_NUMPAD5, kKey::Unknown);
|
||||
CASE(VK_NUMPAD6, kKey::Unknown);
|
||||
CASE(VK_NUMPAD7, kKey::Unknown);
|
||||
CASE(VK_NUMPAD8, kKey::Unknown);
|
||||
CASE(VK_NUMPAD9, kKey::Unknown);
|
||||
CASE(VK_MULTIPLY, kKey::Unknown);
|
||||
CASE(VK_ADD, kKey::Unknown);
|
||||
CASE(VK_SEPARATOR, kKey::Unknown);
|
||||
CASE(VK_SUBTRACT, kKey::Unknown);
|
||||
CASE(VK_DECIMAL, kKey::Unknown);
|
||||
CASE(VK_DIVIDE, kKey::Unknown);
|
||||
CASE(VK_F1, kKey::KeyF1);
|
||||
CASE(VK_F2, kKey::KeyF2);
|
||||
CASE(VK_F3, kKey::KeyF3);
|
||||
CASE(VK_F4, kKey::KeyF4);
|
||||
CASE(VK_F5, kKey::KeyF5);
|
||||
CASE(VK_F6, kKey::KeyF6);
|
||||
CASE(VK_F7, kKey::KeyF7);
|
||||
CASE(VK_F8, kKey::KeyF8);
|
||||
CASE(VK_F9, kKey::KeyF9);
|
||||
CASE(VK_F10, kKey::KeyF10);
|
||||
CASE(VK_F11, kKey::KeyF11);
|
||||
CASE(VK_F12, kKey::KeyF12);
|
||||
CASE(VK_F13, kKey::KeyF13);
|
||||
CASE(VK_F14, kKey::KeyF14);
|
||||
CASE(VK_F15, kKey::KeyF15);
|
||||
CASE(VK_F16, kKey::KeyF16);
|
||||
CASE(VK_F17, kKey::KeyF17);
|
||||
CASE(VK_F18, kKey::KeyF18);
|
||||
CASE(VK_F19, kKey::KeyF19);
|
||||
CASE(VK_F20, kKey::KeyF20);
|
||||
CASE(VK_F21, kKey::KeyF21);
|
||||
CASE(VK_F22, kKey::KeyF22);
|
||||
CASE(VK_F23, kKey::KeyF23);
|
||||
CASE(VK_F24, kKey::KeyF24);
|
||||
CASE(VK_NUMLOCK, kKey::Unknown);
|
||||
CASE(VK_SCROLL, kKey::Unknown);
|
||||
//CASE(VK_OEM_NEC_EQUAL, kKey::Unknown); // same as VK_OEM_FJ_JISHO
|
||||
CASE(VK_OEM_FJ_JISHO, kKey::Unknown);
|
||||
CASE(VK_OEM_FJ_MASSHOU, kKey::Unknown);
|
||||
CASE(VK_OEM_FJ_TOUROKU, kKey::Unknown);
|
||||
CASE(VK_OEM_FJ_LOYA, kKey::Unknown);
|
||||
CASE(VK_OEM_FJ_ROYA, kKey::Unknown);
|
||||
CASE(VK_LSHIFT, kKey::Unknown);
|
||||
CASE(VK_RSHIFT, kKey::Unknown);
|
||||
CASE(VK_LCONTROL, kKey::Unknown);
|
||||
CASE(VK_RCONTROL, kKey::Unknown);
|
||||
CASE(VK_LMENU, kKey::Unknown);
|
||||
CASE(VK_RMENU, kKey::Unknown);
|
||||
CASE(VK_BROWSER_BACK, kKey::Unknown);
|
||||
CASE(VK_BROWSER_FORWARD, kKey::Unknown);
|
||||
CASE(VK_BROWSER_REFRESH, kKey::Unknown);
|
||||
CASE(VK_BROWSER_STOP, kKey::Unknown);
|
||||
CASE(VK_BROWSER_SEARCH, kKey::Unknown);
|
||||
CASE(VK_BROWSER_FAVORITES, kKey::Unknown);
|
||||
CASE(VK_BROWSER_HOME, kKey::Unknown);
|
||||
CASE(VK_VOLUME_MUTE, kKey::Unknown);
|
||||
CASE(VK_VOLUME_DOWN, kKey::Unknown);
|
||||
CASE(VK_VOLUME_UP, kKey::Unknown);
|
||||
CASE(VK_MEDIA_NEXT_TRACK, kKey::Unknown);
|
||||
CASE(VK_MEDIA_PREV_TRACK, kKey::Unknown);
|
||||
CASE(VK_MEDIA_STOP, kKey::Unknown);
|
||||
CASE(VK_MEDIA_PLAY_PAUSE, kKey::Unknown);
|
||||
CASE(VK_LAUNCH_MAIL, kKey::Unknown);
|
||||
CASE(VK_LAUNCH_MEDIA_SELECT, kKey::Unknown);
|
||||
CASE(VK_LAUNCH_APP1, kKey::Unknown);
|
||||
CASE(VK_LAUNCH_APP2, kKey::Unknown);
|
||||
CASE(VK_OEM_1, kKey::Unknown);
|
||||
CASE(VK_OEM_PLUS, kKey::Unknown);
|
||||
CASE(VK_OEM_COMMA, kKey::Unknown);
|
||||
CASE(VK_OEM_MINUS, kKey::Unknown);
|
||||
CASE(VK_OEM_PERIOD, kKey::Unknown);
|
||||
CASE(VK_OEM_2, kKey::Unknown);
|
||||
CASE(VK_OEM_3, kKey::Unknown);
|
||||
CASE(VK_OEM_4, kKey::Unknown);
|
||||
CASE(VK_OEM_5, kKey::Unknown);
|
||||
CASE(VK_OEM_6, kKey::Unknown);
|
||||
CASE(VK_OEM_7, kKey::Unknown);
|
||||
CASE(VK_OEM_8, kKey::Unknown);
|
||||
CASE(VK_OEM_AX, kKey::Unknown);
|
||||
CASE(VK_OEM_102, kKey::Unknown);
|
||||
CASE(VK_ICO_HELP, kKey::Unknown);
|
||||
CASE(VK_ICO_00, kKey::Unknown);
|
||||
CASE(VK_PROCESSKEY, kKey::Unknown);
|
||||
CASE(VK_ICO_CLEAR, kKey::Unknown);
|
||||
CASE(VK_PACKET, kKey::Unknown);
|
||||
CASE(VK_OEM_RESET, kKey::Unknown);
|
||||
CASE(VK_OEM_JUMP, kKey::Unknown);
|
||||
CASE(VK_OEM_PA1, kKey::Unknown);
|
||||
CASE(VK_OEM_PA2, kKey::Unknown);
|
||||
CASE(VK_OEM_PA3, kKey::Unknown);
|
||||
CASE(VK_OEM_WSCTRL, kKey::Unknown);
|
||||
CASE(VK_OEM_CUSEL, kKey::Unknown);
|
||||
CASE(VK_OEM_ATTN, kKey::Unknown);
|
||||
CASE(VK_OEM_FINISH, kKey::Unknown);
|
||||
CASE(VK_OEM_COPY, kKey::Unknown);
|
||||
CASE(VK_OEM_AUTO, kKey::Unknown);
|
||||
CASE(VK_OEM_ENLW, kKey::Unknown);
|
||||
CASE(VK_OEM_BACKTAB, kKey::Unknown);
|
||||
CASE(VK_ATTN, kKey::Unknown);
|
||||
CASE(VK_CRSEL, kKey::Unknown);
|
||||
CASE(VK_EXSEL, kKey::Unknown);
|
||||
CASE(VK_EREOF, kKey::Unknown);
|
||||
CASE(VK_PLAY, kKey::Unknown);
|
||||
CASE(VK_ZOOM, kKey::Unknown);
|
||||
CASE(VK_NONAME, kKey::Unknown);
|
||||
CASE(VK_PA1, kKey::Unknown);
|
||||
CASE(VK_OEM_CLEAR, kKey::Unknown);
|
||||
#endif
|
||||
default:
|
||||
return kKey::Unknown;
|
||||
}
|
||||
}
|
||||
106
src/layout.cpp
Normal file
106
src/layout.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "layout.h"
|
||||
#include "util.h"
|
||||
#include "asset.h"
|
||||
#include "node.h"
|
||||
#include "node_border.h"
|
||||
|
||||
bool LayoutManager::load(const char* path)
|
||||
{
|
||||
auto abs_path = Asset::absolute(path);
|
||||
#if _WIN32 || __OSX__
|
||||
struct stat tmp_info;
|
||||
if (stat(abs_path.c_str(), &tmp_info) != 0)
|
||||
return false;
|
||||
if (tmp_info.st_mtime <= m_file_info.st_mtime)
|
||||
return false;
|
||||
m_file_info = tmp_info;
|
||||
#else
|
||||
if (m_loaded)
|
||||
return true; // already loaded
|
||||
#endif // __ANDROID__
|
||||
|
||||
m_path = path;
|
||||
|
||||
auto old = std::move(m_layouts);
|
||||
|
||||
Asset file;
|
||||
if (!(file.open(path) && file.read_all()))
|
||||
return false;
|
||||
tinyxml2::XMLDocument xml;
|
||||
auto ret = xml.Parse((char*)file.m_data, file.m_len);
|
||||
file.close();
|
||||
if (ret != tinyxml2::XMLError::XML_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
LOG("parsing xml failed");
|
||||
}
|
||||
|
||||
LOG("parsing loaded xml");
|
||||
|
||||
tinyxml2::XMLElement* current = xml.RootElement()->FirstChildElement();
|
||||
while (current)
|
||||
{
|
||||
auto id_str = current->Attribute("id");
|
||||
if (!id_str)
|
||||
{
|
||||
LOG("Layout node without id");
|
||||
return false;
|
||||
}
|
||||
//LOG("Parsing layout: %s", id_str);
|
||||
uint16_t id = const_hash(id_str);
|
||||
auto p = m_layouts.find(id);
|
||||
if (p == m_layouts.end())
|
||||
{
|
||||
auto& node = m_layouts[id];
|
||||
kWidget node_id = (kWidget)const_hash(current->Name());
|
||||
switch (node_id)
|
||||
{
|
||||
case kWidget::Border:
|
||||
node.reset(new NodeBorder());
|
||||
break;
|
||||
default:
|
||||
node.reset(new Node());
|
||||
break;
|
||||
}
|
||||
node->m_manager = this;
|
||||
// try to copy the old size values
|
||||
if (old.count(id))
|
||||
{
|
||||
const auto& old_node = *old[id];
|
||||
YGNodeCopyStyle(node->y_node, old_node.y_node);
|
||||
}
|
||||
node->load_internal(current);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Layout id \"%s\" duplicated", id_str);
|
||||
}
|
||||
current = current->NextSiblingElement("layout");
|
||||
}
|
||||
if (on_loaded)
|
||||
on_loaded();
|
||||
m_loaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayoutManager::reload()
|
||||
{
|
||||
// avoid conflict when assigning the same string from c_str
|
||||
std::string path_copy = m_path;
|
||||
return load(path_copy.c_str());
|
||||
}
|
||||
|
||||
void LayoutManager::restore_context()
|
||||
{
|
||||
for (auto& node : m_layouts)
|
||||
node.second->restore_context();
|
||||
}
|
||||
|
||||
void LayoutManager::clear_context()
|
||||
{
|
||||
for (auto& node : m_layouts)
|
||||
node.second->clear_context();
|
||||
}
|
||||
37
src/layout.h
Normal file
37
src/layout.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "shape.h"
|
||||
#include "util.h"
|
||||
#include "shader.h"
|
||||
#include "font.h"
|
||||
#include "asset.h"
|
||||
#include "rtt.h"
|
||||
#include "bezier.h"
|
||||
#include "canvas.h"
|
||||
#include "event.h"
|
||||
#include <tinyxml2.h>
|
||||
#include <yoga/Yoga.h>
|
||||
|
||||
class LayoutManager
|
||||
{
|
||||
std::map<uint16_t, std::unique_ptr<class Node>> m_layouts;
|
||||
std::string m_path;
|
||||
struct stat m_file_info { 0 };
|
||||
public:
|
||||
bool m_loaded = false;
|
||||
std::function<void()> on_loaded;
|
||||
bool load(const char* path);
|
||||
bool reload();
|
||||
class Node* operator[](uint16_t id)
|
||||
{
|
||||
auto i = m_layouts.find(id);
|
||||
return i == m_layouts.end() ? nullptr : i->second.get();
|
||||
}
|
||||
class Node* get(uint16_t id)
|
||||
{
|
||||
auto i = m_layouts.find(id);
|
||||
return i == m_layouts.end() ? nullptr : i->second.get();
|
||||
}
|
||||
void restore_context();
|
||||
void clear_context();
|
||||
//Node& operator[](const char* ids) { return m_layouts[const_hash(ids)]; }
|
||||
};
|
||||
133
src/log.cpp
Normal file
133
src/log.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "app.h"
|
||||
|
||||
LogRemote LogRemote::I;
|
||||
|
||||
void LogRemote::start()
|
||||
{
|
||||
if (m_running || m_error)
|
||||
return; // already running
|
||||
|
||||
m_running = true;
|
||||
m_thread = std::thread([&] {
|
||||
#ifdef _WIN32
|
||||
BT_SetTerminate();
|
||||
#endif
|
||||
net_init();
|
||||
auto session_string = net_request("/start");
|
||||
m_session = atoi(session_string.c_str());
|
||||
while (m_running && !m_error)
|
||||
{
|
||||
auto m = m_mq.Get();
|
||||
auto escaped = curl_easy_escape(curl, m.c_str(), (int)m.size());
|
||||
auto data = std::make_unique<char[]>(m.size() + 64);
|
||||
int sz = snprintf(data.get(), m.size() + 64, "session=%d&m=%s", m_session, escaped);
|
||||
curl_free(escaped);
|
||||
net_request("/log", std::string(data.get(), sz));
|
||||
}
|
||||
net_close();
|
||||
LOG("NET thread loop exit");
|
||||
});
|
||||
}
|
||||
|
||||
void LogRemote::stop()
|
||||
{
|
||||
m_running = false;
|
||||
m_mq.UnlockGetters();
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void LogRemote::net_init()
|
||||
{
|
||||
if (!(curl = curl_easy_init()))
|
||||
return;
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &this->readBuffer);
|
||||
//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
|
||||
}
|
||||
std::string LogRemote::net_request(std::string cmd, std::string data /*= ""*/)
|
||||
{
|
||||
readBuffer.clear();
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||
auto url = m_url + cmd;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLcode::CURLE_OK)
|
||||
{
|
||||
LOG("NET error, closed");
|
||||
m_running = false;
|
||||
m_error = true;
|
||||
}
|
||||
return readBuffer;
|
||||
}
|
||||
void LogRemote::net_close()
|
||||
{
|
||||
if (curl)
|
||||
curl_easy_cleanup(curl);
|
||||
curl = nullptr;
|
||||
m_running = false;
|
||||
}
|
||||
void LogRemote::file_init()
|
||||
{
|
||||
if (!m_logfile.is_open())
|
||||
m_logfile.open(App::I.data_path + "/panopainter-log.txt");
|
||||
}
|
||||
void LogRemote::file_close()
|
||||
{
|
||||
if (!m_logfile.is_open())
|
||||
m_logfile.close();
|
||||
}
|
||||
void LogRemote::log(const char* format, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
int n = vsnprintf(buffer, sizeof(buffer), format, arglist);
|
||||
va_end(arglist);
|
||||
m_mq.Post(std::string(buffer, n));
|
||||
if (m_logfile.is_open())
|
||||
{
|
||||
auto line = std::string(buffer, n) + "\n";
|
||||
m_logfile.write(line.data(), line.size());
|
||||
m_logfile.flush();
|
||||
}
|
||||
}
|
||||
void LogRemote::log(const wchar_t* format, ...)
|
||||
{
|
||||
static wchar_t buffer[4096];
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
int n = vswprintf(buffer, sizeof(buffer)/sizeof(wchar_t), format, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
std::wstring string_to_convert(buffer, n);
|
||||
|
||||
//setup converter
|
||||
// using convert_type = std::codecvt_utf8<wchar_t>;
|
||||
// std::wstring_convert<convert_type, wchar_t> converter;
|
||||
|
||||
mbstate_t st = {};
|
||||
std::string converted;
|
||||
converted.reserve(string_to_convert.size());
|
||||
const wchar_t * wptr = string_to_convert.c_str();
|
||||
std::wcsrtombs((char*)converted.data(), &wptr, converted.capacity(), &st);
|
||||
|
||||
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
|
||||
//std::string converted_str = converter.to_bytes(string_to_convert);
|
||||
|
||||
m_mq.Post(std::move(converted));
|
||||
if (m_logfile.is_open())
|
||||
{
|
||||
auto line = converted + "\n";
|
||||
m_logfile.write(line.data(), line.size());
|
||||
m_logfile.flush();
|
||||
}
|
||||
}
|
||||
LogRemote::~LogRemote()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
38
src/log.h
Normal file
38
src/log.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "util.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define LOG(M,...) { printf(M"\n", ##__VA_ARGS__); LogRemote::I.log(M, ##__VA_ARGS__); }
|
||||
#elif __ANDROID__
|
||||
#define LOG(...) { ((void)__android_log_print(ANDROID_LOG_INFO, "native-engine", __VA_ARGS__)); LogRemote::I.log(__VA_ARGS__); }
|
||||
#elif _WIN32
|
||||
#define LOG(M,...) { printf(M"\n", ##__VA_ARGS__); LogRemote::I.log(M, ##__VA_ARGS__); }
|
||||
#define LOGW(M,...) { wprintf(M"\n", ##__VA_ARGS__); LogRemote::I.log(M, ##__VA_ARGS__); }
|
||||
#endif
|
||||
|
||||
class LogRemote
|
||||
{
|
||||
public:
|
||||
static LogRemote I;
|
||||
bool m_running = false;
|
||||
bool m_error = false;
|
||||
std::thread m_thread;
|
||||
ui::BlockingQueue<std::string> m_mq;
|
||||
CURL *curl = nullptr;
|
||||
CURLcode res;
|
||||
std::string readBuffer;
|
||||
std::string m_url = "http://omigamedev.ddns.net:8083";
|
||||
int m_session;
|
||||
std::ofstream m_logfile;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void net_init();
|
||||
std::string net_request(std::string cmd, std::string data = "");
|
||||
void net_close();
|
||||
void file_init();
|
||||
void file_close();
|
||||
void log(const char* format, ...);
|
||||
void log(const wchar_t* format, ...);
|
||||
~LogRemote();
|
||||
};
|
||||
767
src/main.cpp
Normal file
767
src/main.cpp
Normal file
@@ -0,0 +1,767 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "shader.h"
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
#include "image.h"
|
||||
#include "app.h"
|
||||
#include "keymap.h"
|
||||
#include "../resource.h"
|
||||
|
||||
#include <WbemCli.h>
|
||||
#include "wacom.h"
|
||||
#include <deque>
|
||||
#include <chrono>
|
||||
|
||||
#pragma comment (lib, "opengl32.lib")
|
||||
#pragma comment (lib, "glew32.lib")
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
||||
#define WM_USER_CLOSE (WM_USER + 1)
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||
HINSTANCE hInst;
|
||||
HWND hWnd;
|
||||
HDC hDC;
|
||||
HGLRC hRC;
|
||||
wchar_t* className;
|
||||
bool keys[256];
|
||||
std::mutex gl_mutex;
|
||||
std::mutex async_mutex;
|
||||
std::thread::id gl_thread;
|
||||
int gl_count = 0;
|
||||
std::deque<std::packaged_task<void()>> tasklist;
|
||||
std::mutex task_mutex;
|
||||
|
||||
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
||||
std::string GetLastErrorAsString()
|
||||
{
|
||||
//Get the error message, if any.
|
||||
DWORD errorMessageID = ::GetLastError();
|
||||
if (errorMessageID == 0)
|
||||
return std::string(); //No error message has been recorded
|
||||
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
||||
|
||||
std::string message(messageBuffer, size);
|
||||
|
||||
//Free the buffer.
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void destroy_window()
|
||||
{
|
||||
SendMessage(hWnd, WM_USER_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
void async_lock()
|
||||
{
|
||||
//std::lock_guard<std::mutex> _lock(async_mutex);
|
||||
if (gl_count == 0 || gl_thread != std::this_thread::get_id())
|
||||
{
|
||||
gl_mutex.lock();
|
||||
bool ret = wglMakeCurrent(hDC, hRC);
|
||||
if (ret == false)
|
||||
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
|
||||
gl_thread = std::this_thread::get_id();
|
||||
//LOG("lock");
|
||||
}
|
||||
gl_count++;
|
||||
//assert(ret == true);
|
||||
}
|
||||
|
||||
bool async_lock_try()
|
||||
{
|
||||
//std::lock_guard<std::mutex> _lock(async_mutex);
|
||||
if (gl_count == 0 || gl_thread != std::this_thread::get_id())
|
||||
{
|
||||
if (!gl_mutex.try_lock())
|
||||
return false;
|
||||
bool ret = wglMakeCurrent(hDC, hRC);
|
||||
if (ret == false)
|
||||
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
|
||||
gl_thread = std::this_thread::get_id();
|
||||
//LOG("lock");
|
||||
}
|
||||
gl_count++;
|
||||
//assert(ret == true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void async_swap()
|
||||
{
|
||||
SwapBuffers(hDC);
|
||||
//LOG("swap");
|
||||
}
|
||||
|
||||
void async_unlock()
|
||||
{
|
||||
//std::lock_guard<std::mutex> _lock(async_mutex);
|
||||
gl_count--;
|
||||
if (gl_count == 0)
|
||||
{
|
||||
//LOG("unlock");
|
||||
wglMakeCurrent(0, 0);
|
||||
gl_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
std::string win32_open_file()
|
||||
{
|
||||
OPENFILENAMEA ofn;
|
||||
char fileName[MAX_PATH] = "";
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||||
ofn.hwndOwner = hWnd;
|
||||
ofn.lpstrFilter = "Image Files (*.jpg, *.png)\0*.jpg;*.png";
|
||||
ofn.lpstrFile = fileName;
|
||||
ofn.nMaxFile = MAX_PATH;
|
||||
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
|
||||
ofn.lpstrDefExt = "";
|
||||
ofn.lpstrInitialDir = "";
|
||||
if (GetOpenFileNameA(&ofn) != NULL)
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
struct async_locker
|
||||
{
|
||||
async_locker() { async_lock(); }
|
||||
~async_locker() { async_unlock(); }
|
||||
};
|
||||
|
||||
int read_WMI_info()
|
||||
{
|
||||
// see: http://win32easy.blogspot.co.uk/2011/03/wmi-in-c-query-everyting-from-your-os.html
|
||||
|
||||
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (FAILED(hRes))
|
||||
{
|
||||
LOG("Unable to launch COM: %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((FAILED(hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0))))
|
||||
{
|
||||
LOG("Unable to initialize security: %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
IWbemLocator* pLocator = NULL;
|
||||
if (FAILED(hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator))))
|
||||
{
|
||||
LOG("Unable to create a WbemLocator: %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
IWbemServices* pService = NULL;
|
||||
if (FAILED(hRes = pLocator->ConnectServer(L"root\\CIMV2", NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pService)))
|
||||
{
|
||||
pLocator->Release();
|
||||
LOG("Unable to connect to \"CIMV2\": %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto log_field = [](const wchar_t* section, IWbemClassObject* clsObj, const wchar_t* field) {
|
||||
VARIANT vRet;
|
||||
VariantInit(&vRet);
|
||||
if (SUCCEEDED(clsObj->Get(field, 0, &vRet, NULL, NULL)) && vRet.vt == VT_BSTR)
|
||||
{
|
||||
LOGW(L"%s %s: %s", section, field, vRet.bstrVal);
|
||||
VariantClear(&vRet);
|
||||
}
|
||||
};
|
||||
|
||||
// GET DEVICE INFO
|
||||
{
|
||||
IEnumWbemClassObject* pEnumerator = NULL;
|
||||
if (FAILED(hRes = pService->ExecQuery(L"WQL", L"SELECT * FROM Win32_ComputerSystem", WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
|
||||
{
|
||||
pLocator->Release();
|
||||
pService->Release();
|
||||
LOG("Unable to retrive desktop monitors: %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
IWbemClassObject* clsObj = NULL;
|
||||
int numElems;
|
||||
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
|
||||
{
|
||||
if (FAILED(hRes))
|
||||
break;
|
||||
|
||||
log_field(L"Machine", clsObj, L"Name");
|
||||
log_field(L"Machine", clsObj, L"Model");
|
||||
log_field(L"Machine", clsObj, L"Manufacturer");
|
||||
|
||||
clsObj->Release();
|
||||
}
|
||||
pEnumerator->Release();
|
||||
}
|
||||
|
||||
// GET OS INFO
|
||||
{
|
||||
IEnumWbemClassObject* pEnumerator = NULL;
|
||||
if (FAILED(hRes = pService->ExecQuery(L"WQL", L"SELECT * FROM Win32_OperatingSystem", WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator)))
|
||||
{
|
||||
pLocator->Release();
|
||||
pService->Release();
|
||||
LOG("Unable to retrive desktop monitors: %x", hRes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
IWbemClassObject* clsObj = NULL;
|
||||
int numElems;
|
||||
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE)
|
||||
{
|
||||
if (FAILED(hRes))
|
||||
break;
|
||||
|
||||
log_field(L"OS", clsObj, L"Name");
|
||||
log_field(L"OS", clsObj, L"Version");
|
||||
log_field(L"OS", clsObj, L"Locale");
|
||||
log_field(L"OS", clsObj, L"OSProductSuite");
|
||||
log_field(L"OS", clsObj, L"Manufacturer");
|
||||
log_field(L"OS", clsObj, L"Description");
|
||||
|
||||
clsObj->Release();
|
||||
}
|
||||
pEnumerator->Release();
|
||||
}
|
||||
|
||||
pService->Release();
|
||||
pLocator->Release();
|
||||
CoUninitialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
INT_PTR g_iLogHandle = -1;
|
||||
static void SetupExceptionHandler()
|
||||
{
|
||||
// Setup exception handler
|
||||
BT_SetAppName(_T("PanoPainter"));
|
||||
//BT_SetSupportEMail(_T("your@email.com"));
|
||||
BT_SetFlags(BTF_DETAILEDMODE | BTF_ATTACHREPORT | BTF_SCREENCAPTURE);
|
||||
|
||||
// = BugTrapServer ===========================================
|
||||
BT_SetSupportServer(_T("omigamedev.ddns.net"), 8088);
|
||||
// - or -
|
||||
//BT_SetSupportServer(_T("127.0.0.1"), 9999);
|
||||
|
||||
// = BugTrapWebServer ========================================
|
||||
//BT_SetSupportServer(_T("http://localhost/BugTrapWebServer/RequestHandler.aspx"), BUGTRAP_HTTP_PORT);
|
||||
//BT_SetSupportServer(_T("http://omigamedev.ddns.net:8088/source/Server/BugTrapWebServer/RequestHandler.aspx"), BUGTRAP_HTTP_PORT);
|
||||
|
||||
// required for VS 2005 & 2008
|
||||
BT_InstallSehFilter();
|
||||
|
||||
// Add custom log file using default name
|
||||
// g_iLogHandle = BT_OpenLogFile(NULL, BTLF_TEXT);
|
||||
// BT_SetLogSizeInEntries(g_iLogHandle, 100);
|
||||
// BT_SetLogFlags(g_iLogHandle, BTLF_SHOWTIMESTAMP);
|
||||
// BT_SetLogEchoMode(g_iLogHandle, BTLE_STDERR | BTLE_DBGOUT);
|
||||
//
|
||||
// PCTSTR pszLogFileName = BT_GetLogFileName(g_iLogHandle);
|
||||
TCHAR wpath[1024];
|
||||
GetFullPathNameW(L"panopainter-log.txt", 1024, wpath, nullptr);
|
||||
BT_AddLogFile(wpath);
|
||||
|
||||
BT_SetPreErrHandler([](INT_PTR){
|
||||
if (ui::Canvas::I)
|
||||
{
|
||||
auto path = App::I.data_path + "/recovery.pano";
|
||||
ui::Canvas::I->project_save_thread(path);
|
||||
static char abspath[MAX_PATH];
|
||||
GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL);
|
||||
static char message[4096];
|
||||
snprintf(message, sizeof(message), "File recovered in: %s", abspath);
|
||||
MessageBoxA(NULL, message, "File Recovery", MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
LogRemote::I.file_close();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
WNDCLASS wc;
|
||||
PIXELFORMATDESCRIPTOR pfd;
|
||||
|
||||
if (argc == 1)
|
||||
App::I.initLog();
|
||||
|
||||
if (!App::I.check_license())
|
||||
{
|
||||
MessageBoxA(NULL, "Unable to verify this demo license, please make sure you are connected to Internet.",
|
||||
"PanoPainter - License Error", MB_ICONERROR | MB_OK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SetupExceptionHandler();
|
||||
BT_SetTerminate();
|
||||
|
||||
read_WMI_info();
|
||||
|
||||
App::I.create();
|
||||
RECT clientRect = { 0, 0, (int)App::I.width, (int)App::I.height };
|
||||
|
||||
// Inizialize data structures to zero
|
||||
memset(&wc, 0, sizeof(wc));
|
||||
memset(&keys, 0, sizeof(keys));
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
|
||||
// Create the main window
|
||||
|
||||
hInst = GetModuleHandle(NULL);
|
||||
className = L"EngineMain";
|
||||
|
||||
wc.hInstance = hInst;
|
||||
wc.lpfnWndProc = (WNDPROC)WndProc;
|
||||
wc.lpszClassName = className;
|
||||
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
|
||||
RegisterClass(&wc);
|
||||
|
||||
AdjustWindowRect(&clientRect, WS_OVERLAPPEDWINDOW, false);
|
||||
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left,
|
||||
clientRect.bottom - clientRect.top, 0, 0, hInst, 0);
|
||||
|
||||
// Setup GL Rendering Context
|
||||
pfd.nSize = sizeof(pfd);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
pfd.cDepthBits = 16;
|
||||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
|
||||
hDC = GetDC(hWnd);
|
||||
int pxfmt = ChoosePixelFormat(hDC, &pfd);
|
||||
SetPixelFormat(hDC, pxfmt, &pfd);
|
||||
hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less
|
||||
wglMakeCurrent(hDC, hRC);
|
||||
|
||||
// Initialize extensions
|
||||
if (glewInit() != GLEW_OK)
|
||||
return 0;
|
||||
|
||||
LOG("GL version: %s", glGetString(GL_VERSION));
|
||||
LOG("GL vendor: %s", glGetString(GL_VENDOR));
|
||||
LOG("GL renderer: %s", glGetString(GL_RENDERER));
|
||||
|
||||
static wchar_t window_title[512];
|
||||
swprintf_s(window_title, L"PanoPainter %s", g_version_number_w);
|
||||
|
||||
// If supported create a 3.1 context
|
||||
if (wglewIsSupported("WGL_ARB_create_context"))
|
||||
{
|
||||
int contex_attribs[] =
|
||||
{
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
0
|
||||
};
|
||||
int pixel_attribs[] =
|
||||
{
|
||||
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
|
||||
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
|
||||
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
||||
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
|
||||
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
WGL_COLOR_BITS_ARB, 32,
|
||||
WGL_DEPTH_BITS_ARB, 24,
|
||||
WGL_STENCIL_BITS_ARB, 8,
|
||||
WGL_SAMPLE_BUFFERS_ARB, 1, // Number of buffers (must be 1 at time of writing)
|
||||
WGL_SAMPLES_ARB, 4, // Number of samples
|
||||
0
|
||||
};
|
||||
UINT numFormat;
|
||||
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
wglDeleteContext(hRC);
|
||||
DestroyWindow(hWnd);
|
||||
|
||||
hWnd = CreateWindow(wc.lpszClassName, window_title, WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left,
|
||||
clientRect.bottom - clientRect.top, 0, 0, hInst, 0);
|
||||
|
||||
hDC = GetDC(hWnd);
|
||||
wglChoosePixelFormatARB(hDC, pixel_attribs, nullptr, 1, &pxfmt, &numFormat);
|
||||
SetPixelFormat(hDC, pxfmt, &pfd);
|
||||
hRC = wglCreateContextAttribsARB(hDC, NULL, contex_attribs);
|
||||
wglMakeCurrent(hDC, hRC);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not supported, go fuck yourself we are not gonna support your shitty device
|
||||
return -1; // A negative number because you are a negative one
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
switch (const_hash(argv[1]))
|
||||
{
|
||||
case const_hash("convert"):
|
||||
App::I.initShaders();
|
||||
App::I.cmd_convert(argv[2], argv[3]);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
App::I.init();
|
||||
|
||||
ShowWindow(hWnd, SW_NORMAL);
|
||||
WacomTablet::I.init(hWnd);
|
||||
|
||||
SendMessage(hWnd, WM_SETICON, ICON_SMALL,
|
||||
(LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)));
|
||||
|
||||
SetTimer(hWnd, 1, 500, [](HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
|
||||
App::I.redraw = true;
|
||||
});
|
||||
|
||||
bool running = true;
|
||||
std::mutex render_mutex;
|
||||
std::condition_variable render_cv;
|
||||
|
||||
std::thread renderer([&] {
|
||||
BT_SetTerminate();
|
||||
const float target_fps = 10;
|
||||
unsigned long t0 = GetTickCount();
|
||||
unsigned long t1;
|
||||
int frames = 0;
|
||||
float one_sec = 0;
|
||||
float render_timer = 0;
|
||||
while(running)
|
||||
{
|
||||
t1 = GetTickCount();
|
||||
float dt = (float)(t1 - t0) / 1000.0f;
|
||||
one_sec += dt;
|
||||
render_timer += dt;
|
||||
t0 = t1;
|
||||
|
||||
if (one_sec > 1.f)
|
||||
{
|
||||
static wchar_t title_fps[512];
|
||||
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);
|
||||
SetWindowText(hWnd, title_fps);
|
||||
one_sec = 0;
|
||||
frames = 0;
|
||||
}
|
||||
|
||||
{
|
||||
std::deque<std::packaged_task<void()>> working_list;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
working_list = std::move(tasklist);
|
||||
}
|
||||
|
||||
async_lock();
|
||||
while (!working_list.empty())
|
||||
{
|
||||
|
||||
working_list.front()();
|
||||
working_list.pop_front();
|
||||
}
|
||||
async_unlock();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(render_mutex);
|
||||
if (render_timer > 1.0f / target_fps || App::I.redraw)
|
||||
{
|
||||
render_timer = 0;
|
||||
if (App::I.redraw)
|
||||
{
|
||||
async_lock();
|
||||
App::I.redraw = true;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
App::I.clear();
|
||||
App::I.update(dt);
|
||||
SwapBuffers(hDC);
|
||||
async_unlock();
|
||||
//LOG("swap main");
|
||||
}
|
||||
}
|
||||
frames++;
|
||||
|
||||
const int framerate = (1.f / target_fps) * 1000;
|
||||
const int diff = framerate - (t1 - t0);
|
||||
render_cv.wait_for(lock, std::chrono::milliseconds(diff));
|
||||
}
|
||||
});
|
||||
|
||||
MSG msg;
|
||||
while (running)
|
||||
{
|
||||
// If there any message in the queue process it
|
||||
auto present = App::I.animate || App::I.redraw ?
|
||||
PeekMessage(&msg, 0, 0, 0, PM_REMOVE) : GetMessage(&msg, 0, 0, 0);
|
||||
|
||||
running = !(msg.message == WM_QUIT/* || gl.keys[VK_ESCAPE]*/);
|
||||
if (present)
|
||||
{
|
||||
DispatchMessage(&msg);
|
||||
TranslateMessage(&msg);
|
||||
}
|
||||
|
||||
if (!tasklist.empty())
|
||||
render_cv.notify_all();
|
||||
}
|
||||
render_cv.notify_all();
|
||||
if (renderer.joinable())
|
||||
renderer.join();
|
||||
App::I.terminate();
|
||||
// Clean up
|
||||
WacomTablet::I.terminate();
|
||||
//DestroyWindow(hWnd);
|
||||
UnregisterClass(className, hInst);
|
||||
LogRemote::I.stop();
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
static bool leftDown = false;
|
||||
static DWORD lastTime;
|
||||
static POINT lastPoint;
|
||||
switch (msg)
|
||||
{
|
||||
case WM_USER_CLOSE:
|
||||
DestroyWindow(hWnd);
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_PAINT:
|
||||
App::I.redraw = true;
|
||||
break;
|
||||
case WM_CREATE:
|
||||
BT_SetTerminate();
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
if (App::I.request_close())
|
||||
PostQuitMessage(0);
|
||||
else
|
||||
return true;
|
||||
break;
|
||||
case WM_SIZE:
|
||||
{
|
||||
auto w = (float)LOWORD(lp);
|
||||
auto h = (float)HIWORD(lp);
|
||||
if (h != 0)
|
||||
{
|
||||
async_locker lock;
|
||||
App::I.resize(w, h);
|
||||
App::I.clear();
|
||||
App::I.redraw = true;
|
||||
App::I.update(0.f);
|
||||
SwapBuffers(hDC);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATE:
|
||||
{
|
||||
int active = GET_WM_ACTIVATE_STATE(wp, lp);
|
||||
WacomTablet::I.set_focus(active);
|
||||
break;
|
||||
}
|
||||
case WT_PACKET:
|
||||
WacomTablet::I.handle_message(hWnd, msg, wp, lp);
|
||||
break;
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYDOWN:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([wp] {
|
||||
App::I.key_down(convert_key((int)wp));
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYUP:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([wp] {
|
||||
App::I.key_up(convert_key((int)wp));
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_CHAR:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([wp]{
|
||||
App::I.key_char((int)wp);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([lp, p = WacomTablet::I.get_pressure()]{
|
||||
App::I.mouse_move((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), p, kEventSource::Mouse);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
SetCapture(hWnd);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([lp, p = WacomTablet::I.get_pressure()]{
|
||||
App::I.mouse_down(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), p, kEventSource::Mouse);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
WacomTablet::I.reset_pressure();
|
||||
ReleaseCapture();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([lp]{
|
||||
App::I.mouse_up(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(hWnd);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([lp]{
|
||||
App::I.mouse_down(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), 1.f, kEventSource::Mouse);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([lp]{
|
||||
App::I.mouse_up(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp), kEventSource::Mouse);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
async_locker lock;
|
||||
POINT pt;
|
||||
pt.x = GET_X_LPARAM(lp);
|
||||
pt.y = GET_Y_LPARAM(lp);
|
||||
ScreenToClient(hWnd, &pt);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex);
|
||||
tasklist.emplace_back([pt, wp] {
|
||||
App::I.mouse_scroll((float)pt.x, (float)pt.y,
|
||||
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_POINTERUPDATE:
|
||||
{
|
||||
POINTER_TOUCH_INFO touchInfo;
|
||||
POINTER_PEN_INFO penInfo;
|
||||
POINTER_INFO pointerInfo;
|
||||
UINT32 pointerId = GET_POINTERID_WPARAM(wp);
|
||||
POINTER_INPUT_TYPE pointerType = PT_POINTER;
|
||||
|
||||
// Retrieve common pointer information
|
||||
if (!GetPointerInfo(pointerId, &pointerInfo))
|
||||
{
|
||||
// failure, call GetLastError()
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, process pointerInfo
|
||||
if (!GetPointerType(pointerId, &pointerType))
|
||||
{
|
||||
// failure, call GetLastError()
|
||||
// set PT_POINTER to fall to default case below
|
||||
pointerType = PT_POINTER;
|
||||
}
|
||||
switch (pointerType)
|
||||
{
|
||||
case PT_TOUCH:
|
||||
// Retrieve touch information
|
||||
if (!GetPointerTouchInfo(pointerId, &touchInfo))
|
||||
{
|
||||
// failure, call GetLastError()
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, process touchInfo
|
||||
// mark as handled to skip call to DefWindowProc
|
||||
//fHandled = TRUE;
|
||||
}
|
||||
break;
|
||||
case PT_PEN:
|
||||
// Retrieve pen information
|
||||
if (!GetPointerPenInfo(pointerId, &penInfo))
|
||||
{
|
||||
// failure, call GetLastError()
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, process penInfo
|
||||
// mark as handled to skip call to DefWindowProc
|
||||
//fHandled = TRUE;
|
||||
//penInfo.pressure
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!GetPointerInfo(pointerId, &pointerInfo))
|
||||
{
|
||||
// failure.
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, proceed with pointerInfo.
|
||||
//fHandled = HandleGenericPointerInfo(&pointerInfo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid annoying alt system menu
|
||||
if (wp == SC_KEYMENU && (lp >> 16) <= 0) return 0;
|
||||
|
||||
return DefWindowProc(hWnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
||||
_In_ LPSTR lpCmdLine, _In_ int nShowCmd)
|
||||
{
|
||||
LPWSTR *szArgList{ nullptr };
|
||||
int argc{ 0 };
|
||||
char** argv{ nullptr };
|
||||
|
||||
szArgList = CommandLineToArgvW(GetCommandLine(), &argc);
|
||||
if (szArgList == NULL)
|
||||
{
|
||||
MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);
|
||||
return 10;
|
||||
}
|
||||
|
||||
argv = new char*[argc + 1];
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
auto len = wcslen(szArgList[i]) + 1;
|
||||
argv[i] = new char[len];
|
||||
wcstombs_s(nullptr, argv[i], len, szArgList[i], len);
|
||||
}
|
||||
|
||||
LocalFree(szArgList);
|
||||
|
||||
return main(argc, argv);
|
||||
}
|
||||
994
src/node.cpp
Normal file
994
src/node.cpp
Normal file
@@ -0,0 +1,994 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "log.h"
|
||||
#include "node.h"
|
||||
#include "layout.h"
|
||||
#include "util.h"
|
||||
#include "asset.h"
|
||||
#include "node_border.h"
|
||||
#include "node_image.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "node_icon.h"
|
||||
#include "node_text.h"
|
||||
#include "node_text_input.h"
|
||||
#include "node_button.h"
|
||||
#include "node_button_custom.h"
|
||||
#include "node_slider.h"
|
||||
#include "node_popup_menu.h"
|
||||
#include "node_viewport.h"
|
||||
#include "node_checkbox.h"
|
||||
#include "node_panel_layer.h"
|
||||
#include "node_panel_brush.h"
|
||||
#include "node_panel_color.h"
|
||||
#include "node_panel_stroke.h"
|
||||
#include "node_color_quad.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "node_canvas.h"
|
||||
#include "node_scroll.h"
|
||||
#include "node_dialog_browse.h"
|
||||
#include "node_dialog_cloud.h"
|
||||
#include "node_combobox.h"
|
||||
#include "node_colorwheel.h"
|
||||
#include "node_dialog_picker.h"
|
||||
#include "node_panel_grid.h"
|
||||
|
||||
void Node::async_start()
|
||||
{
|
||||
App::I.async_start();
|
||||
}
|
||||
|
||||
void Node::async_update()
|
||||
{
|
||||
App::I.async_update();
|
||||
}
|
||||
|
||||
void Node::async_end()
|
||||
{
|
||||
App::I.async_end();
|
||||
}
|
||||
|
||||
void Node::watch(std::function<bool(Node*)> observer)
|
||||
{
|
||||
bool cont = observer(this);
|
||||
if (cont)
|
||||
{
|
||||
for (auto& c : m_children)
|
||||
c->watch(observer);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::destroy()
|
||||
{
|
||||
m_destroyed = true;
|
||||
mouse_release();
|
||||
key_release();
|
||||
}
|
||||
|
||||
Node* Node::root()
|
||||
{
|
||||
|
||||
Node* ret = this;
|
||||
while (ret->parent)
|
||||
ret = ret->parent;
|
||||
return ret;
|
||||
}
|
||||
|
||||
kEventResult Node::on_event(Event* e)
|
||||
{
|
||||
kEventResult ret = kEventResult::Available;
|
||||
|
||||
if (current_mouse_capture)
|
||||
return current_mouse_capture->on_event(e);
|
||||
|
||||
bool skip_children = false;
|
||||
skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) &&
|
||||
(m_mouse_captured) && (root()->current_mouse_capture == this) && m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed
|
||||
|
||||
if (!m_display)
|
||||
return kEventResult::Available;
|
||||
|
||||
if (!skip_children)
|
||||
{
|
||||
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it)
|
||||
{
|
||||
if ((*it)->on_event(e) == kEventResult::Consumed)
|
||||
{
|
||||
if (m_flood_events)
|
||||
{
|
||||
ret = kEventResult::Consumed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == kEventResult::Consumed)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (e->m_cat)
|
||||
{
|
||||
case kEventCategory::MouseEvent:
|
||||
{
|
||||
if (m_mouse_ignore)
|
||||
break;
|
||||
MouseEvent* me = static_cast<MouseEvent*>(e);
|
||||
bool inside = point_in_rect(me->m_pos, m_clip);
|
||||
bool inside_old = m_mouse_inside;
|
||||
m_mouse_inside = inside;
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseScroll:
|
||||
case kEventType::MouseDownL:
|
||||
case kEventType::MouseDownR:
|
||||
case kEventType::MouseUpL:
|
||||
case kEventType::MouseUpR:
|
||||
if ((inside || m_mouse_captured) && handle_event(e) == kEventResult::Consumed)
|
||||
return kEventResult::Consumed;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (inside_old == false && inside == true)
|
||||
{
|
||||
MouseEvent e2 = *me;
|
||||
e2.m_type = kEventType::MouseEnter;
|
||||
handle_event(&e2);
|
||||
}
|
||||
if (inside || m_mouse_captured)
|
||||
ret = handle_event(e);
|
||||
if (inside_old == true && inside == false)
|
||||
{
|
||||
MouseEvent e2 = *me;
|
||||
e2.m_type = kEventType::MouseLeave;
|
||||
handle_event(&e2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (handle_event(e) == kEventResult::Consumed)
|
||||
return kEventResult::Consumed;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kEventCategory::GestureEvent:
|
||||
{
|
||||
if (m_mouse_ignore)
|
||||
break;
|
||||
GestureEvent* ge = static_cast<GestureEvent*>(e);
|
||||
bool inside = point_in_rect(ge->m_pos, m_clip);
|
||||
bool inside_old = m_mouse_inside;
|
||||
m_mouse_inside = inside;
|
||||
if ((inside || m_mouse_captured) && handle_event(e) == kEventResult::Consumed)
|
||||
return kEventResult::Consumed;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (handle_event(e) == kEventResult::Consumed)
|
||||
return kEventResult::Consumed;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
kEventResult Node::handle_event(Event* e)
|
||||
{
|
||||
return kEventResult::Available;
|
||||
}
|
||||
|
||||
void Node::handle_resize(glm::vec2 old_size, glm::vec2 new_size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::create()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::added(Node* parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::removed(Node* parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const Node* Node::init_template(const char* id)
|
||||
{
|
||||
const auto& m_template = static_cast<Node*>((*m_manager)[const_hash(id)]->m_children[0].get());
|
||||
for (auto& c : m_template->m_children)
|
||||
{
|
||||
auto node = c->clone();
|
||||
add_child(node);
|
||||
node->init();
|
||||
node->create();
|
||||
node->loaded();
|
||||
}
|
||||
YGNodeCopyStyle(y_node, m_template->y_node);
|
||||
return m_template;
|
||||
}
|
||||
|
||||
void Node::add_child(Node* n)
|
||||
{
|
||||
m_children.emplace_back(n);
|
||||
n->parent = this;
|
||||
n->m_manager = m_manager;
|
||||
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
|
||||
n->added(this);
|
||||
}
|
||||
|
||||
void Node::add_child(Node* n, int index)
|
||||
{
|
||||
m_children.emplace_back(n);
|
||||
n->parent = this;
|
||||
n->m_manager = m_manager;
|
||||
YGNodeInsertChild(y_node, n->y_node, index);
|
||||
n->added(this);
|
||||
}
|
||||
|
||||
void Node::add_child(std::shared_ptr<Node> n)
|
||||
{
|
||||
m_children.push_back(n);
|
||||
n->parent = this;
|
||||
n->m_manager = m_manager;
|
||||
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
|
||||
n->added(this);
|
||||
}
|
||||
|
||||
void Node::add_child(std::shared_ptr<Node> n, int index)
|
||||
{
|
||||
m_children.push_back(n);
|
||||
n->parent = this;
|
||||
n->m_manager = m_manager;
|
||||
YGNodeInsertChild(y_node, n->y_node, index);
|
||||
n->added(this);
|
||||
}
|
||||
|
||||
void Node::remove_child(Node* n)
|
||||
{
|
||||
auto i = std::find_if(m_children.begin(), m_children.end(), [=](auto& ptr) { return ptr.get() == n; });
|
||||
if (i != m_children.end())
|
||||
{
|
||||
n->removed(this);
|
||||
YGNodeRemoveChild(y_node, n->y_node);
|
||||
m_children.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::remove_all_children()
|
||||
{
|
||||
for (auto& n : m_children)
|
||||
{
|
||||
n->removed(this);
|
||||
YGNodeRemoveChild(y_node, n->y_node);
|
||||
}
|
||||
m_children.clear();
|
||||
}
|
||||
|
||||
void Node::move_child(Node* n, int index)
|
||||
{
|
||||
YGNodeRemoveChild(y_node, n->y_node);
|
||||
YGNodeInsertChild(y_node, n->y_node, index);
|
||||
}
|
||||
|
||||
void Node::move_child_offset(Node* n, int offset)
|
||||
{
|
||||
int count = YGNodeGetChildCount(y_node);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (YGNodeGetChild(y_node, i) == n->y_node)
|
||||
{
|
||||
int new_index = glm::clamp<int>(i + offset, 0, count - 1);
|
||||
YGNodeRemoveChild(y_node, n->y_node);
|
||||
YGNodeInsertChild(y_node, n->y_node, new_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Node::get_child_index(Node* n)
|
||||
{
|
||||
int count = YGNodeGetChildCount(y_node);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (YGNodeGetChild(y_node, i) == n->y_node)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Node* Node::get_child_at(int index)
|
||||
{
|
||||
auto n = YGNodeGetChild(y_node, index);
|
||||
for (auto& c : m_children)
|
||||
{
|
||||
if (c->y_node == n)
|
||||
return c.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::vec4 Node::get_children_rect() const
|
||||
{
|
||||
if (m_children.empty())
|
||||
return glm::vec4(0);
|
||||
glm::vec4 ret = m_children[0]->m_clip_uncut;
|
||||
for (auto& c : m_children)
|
||||
ret = rect_union(ret, c->m_clip_uncut);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Node::mouse_capture()
|
||||
{
|
||||
// already owner of capture
|
||||
if (root()->current_mouse_capture == this)
|
||||
return;
|
||||
|
||||
// cancel previous owner
|
||||
if (auto n = root()->current_mouse_capture)
|
||||
{
|
||||
MouseEvent e;
|
||||
e.m_type = kEventType::MouseCancel;
|
||||
n->handle_event(&e);
|
||||
}
|
||||
|
||||
// make current
|
||||
root()->current_mouse_capture = this;
|
||||
m_mouse_captured = true;
|
||||
}
|
||||
|
||||
void Node::mouse_release()
|
||||
{
|
||||
if (root()->current_mouse_capture == this)
|
||||
root()->current_mouse_capture = nullptr;
|
||||
m_mouse_captured = false;
|
||||
}
|
||||
|
||||
void Node::key_capture()
|
||||
{
|
||||
root()->current_key_capture = this;
|
||||
m_key_captured = true;
|
||||
}
|
||||
|
||||
void Node::key_release()
|
||||
{
|
||||
root()->current_key_capture = nullptr;
|
||||
m_key_captured = false;
|
||||
}
|
||||
|
||||
Node&& Node::operator=(Node&& o)
|
||||
{
|
||||
return std::forward<Node>(o);
|
||||
}
|
||||
|
||||
Node::Node()
|
||||
{
|
||||
y_node = YGNodeNew();
|
||||
}
|
||||
|
||||
Node::Node(Node&& o)
|
||||
{
|
||||
m_name = std::move(o.m_name);
|
||||
m_nodeID_s = std::move(o.m_nodeID_s);
|
||||
m_children = std::move(o.m_children);
|
||||
for (auto& c : m_children)
|
||||
c->parent = this;
|
||||
m_nodeID = o.m_nodeID;
|
||||
m_display = o.m_display;
|
||||
parent = o.parent;
|
||||
y_node = o.y_node;
|
||||
m_pos = o.m_pos;
|
||||
m_size = o.m_size;
|
||||
m_clip = o.m_clip;
|
||||
m_zoom = o.m_zoom;
|
||||
m_mouse_ignore = o.m_mouse_ignore;
|
||||
o.y_node = nullptr;
|
||||
o.parent = nullptr;
|
||||
|
||||
m_manager = o.m_manager;
|
||||
current_mouse_capture = o.current_mouse_capture;
|
||||
current_key_capture = o.current_key_capture;
|
||||
m_mouse_captured = o.m_mouse_captured;
|
||||
m_key_captured = o.m_key_captured;
|
||||
|
||||
m_proj = o.m_proj;
|
||||
m_mvp = o.m_mvp;
|
||||
m_mouse_inside = o.m_mouse_inside;
|
||||
m_flood_events = o.m_flood_events;
|
||||
m_capture_children = o.m_capture_children;
|
||||
m_destroyed = o.m_destroyed;
|
||||
|
||||
m_scale = o.m_scale;
|
||||
m_pos_origin = o.m_pos_origin;
|
||||
m_pos_offset = o.m_pos_offset;
|
||||
m_pos_offset_childred = o.m_pos_offset_childred;
|
||||
m_clip_uncut = o.m_clip_uncut;
|
||||
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
m_children.clear();
|
||||
if (y_node)
|
||||
YGNodeFree(y_node);
|
||||
}
|
||||
|
||||
void Node::SetWidth(float value)
|
||||
{
|
||||
YGNodeStyleSetWidth(y_node, value);
|
||||
m_size.x = value;
|
||||
}
|
||||
|
||||
void Node::SetWidthP(float value)
|
||||
{
|
||||
YGNodeStyleSetWidthPercent(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetHeight(float value)
|
||||
{
|
||||
YGNodeStyleSetHeight(y_node, value);
|
||||
m_size.y = value;
|
||||
}
|
||||
|
||||
void Node::SetHeightP(float value)
|
||||
{
|
||||
YGNodeStyleSetHeightPercent(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetSize(float w, float h)
|
||||
{
|
||||
SetWidth(w); SetHeight(h);
|
||||
m_size = {w, h};
|
||||
}
|
||||
|
||||
void Node::SetSize(glm::vec2 value)
|
||||
{
|
||||
SetWidth(value.x); SetHeight(value.y);
|
||||
m_size = value;
|
||||
}
|
||||
|
||||
void Node::SetPadding(float t, float r, float b, float l)
|
||||
{
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeTop, t);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeRight, r);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeBottom, b);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeLeft, l);
|
||||
}
|
||||
|
||||
glm::vec4 Node::GetPadding() const
|
||||
{
|
||||
float t = YGNodeLayoutGetPadding(y_node, YGEdgeTop);
|
||||
float r = YGNodeLayoutGetPadding(y_node, YGEdgeRight);
|
||||
float b = YGNodeLayoutGetPadding(y_node, YGEdgeBottom);
|
||||
float l = YGNodeLayoutGetPadding(y_node, YGEdgeLeft);
|
||||
return{ t, r, b, l };
|
||||
}
|
||||
|
||||
void Node::SetPosition(const glm::vec2 pos)
|
||||
{
|
||||
SetPosition(pos.x, pos.y);
|
||||
m_pos = pos;
|
||||
}
|
||||
|
||||
void Node::SetPosition(float l, float t)
|
||||
{
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeTop, t);
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeLeft, l);
|
||||
m_pos = {l, t};
|
||||
}
|
||||
|
||||
void Node::SetPosition(float l, float t, float r, float b)
|
||||
{
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeTop, t);
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeRight, r);
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeBottom, b);
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeLeft, l);
|
||||
}
|
||||
|
||||
void Node::SetFlexGrow(float value)
|
||||
{
|
||||
YGNodeStyleSetFlexGrow(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetFlexShrink(float value)
|
||||
{
|
||||
YGNodeStyleSetFlexShrink(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetFlexDir(YGFlexDirection value)
|
||||
{
|
||||
YGNodeStyleSetFlexDirection(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetFlexWrap(YGWrap value)
|
||||
{
|
||||
YGNodeStyleSetFlexWrap(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetJustify(YGJustify value)
|
||||
{
|
||||
YGNodeStyleSetJustifyContent(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetAlign(YGAlign value)
|
||||
{
|
||||
YGNodeStyleSetAlignItems(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetPositioning(YGPositionType value)
|
||||
{
|
||||
YGNodeStyleSetPositionType(y_node, value);
|
||||
}
|
||||
|
||||
void Node::SetAspectRatio(float ar)
|
||||
{
|
||||
YGNodeStyleSetAspectRatio(y_node, ar);
|
||||
}
|
||||
|
||||
void Node::SetRTL(YGDirection dir)
|
||||
{
|
||||
YGNodeStyleSetDirection(y_node, dir);
|
||||
}
|
||||
|
||||
glm::vec2 Node::GetPosition()
|
||||
{
|
||||
return{ YGNodeLayoutGetLeft(y_node), YGNodeLayoutGetTop(y_node) };
|
||||
}
|
||||
|
||||
float Node::GetWidth()
|
||||
{
|
||||
return YGNodeLayoutGetWidth(y_node);
|
||||
}
|
||||
|
||||
float Node::GetHeight()
|
||||
{
|
||||
return YGNodeLayoutGetHeight(y_node);
|
||||
}
|
||||
|
||||
glm::vec2 Node::GetSize()
|
||||
{
|
||||
return{ GetWidth(), GetHeight() };
|
||||
}
|
||||
|
||||
YGDirection Node::GetRTL()
|
||||
{
|
||||
return YGNodeStyleGetDirection(y_node);
|
||||
}
|
||||
|
||||
void Node::restore_context()
|
||||
{
|
||||
for (auto& c : m_children)
|
||||
c->restore_context();
|
||||
}
|
||||
|
||||
void Node::clear_context()
|
||||
{
|
||||
for (auto& c : m_children)
|
||||
c->clear_context();
|
||||
}
|
||||
|
||||
void Node::update(float width, float height, float zoom)
|
||||
{
|
||||
m_zoom = zoom;
|
||||
YGNodeStyleSetWidth(y_node, width / zoom);
|
||||
YGNodeStyleSetHeight(y_node, height / zoom);
|
||||
YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
|
||||
m_proj = glm::ortho(0.f, width / zoom, height / zoom, 0.f, -1.f, 1.f);
|
||||
update_internal({ 0, 0 }, m_proj);
|
||||
}
|
||||
|
||||
void Node::update()
|
||||
{
|
||||
YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
|
||||
update_internal({ 0, 0 }, m_proj);
|
||||
}
|
||||
|
||||
void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj)
|
||||
{
|
||||
float x = YGNodeLayoutGetLeft(y_node);
|
||||
float y = YGNodeLayoutGetTop(y_node);
|
||||
float w = YGNodeLayoutGetWidth(y_node);
|
||||
float h = YGNodeLayoutGetHeight(y_node);
|
||||
auto old_size = m_size;
|
||||
glm::vec2 parent_offset = parent ? parent->m_pos_offset_childred : glm::vec2(0.f);
|
||||
m_pos = glm::floor(origin + glm::vec2(x, y) + m_pos_offset + parent_offset);
|
||||
m_pos_origin = glm::floor(origin + glm::vec2(x, y));
|
||||
m_size = glm::floor(glm::vec2(w, h));
|
||||
|
||||
if (parent)
|
||||
{
|
||||
// correct the padding clip
|
||||
// should not clip the padded area
|
||||
// useful to draw decorations
|
||||
float pt = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeTop);
|
||||
float pr = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeRight);
|
||||
float pb = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeBottom);
|
||||
float pl = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeLeft);
|
||||
glm::vec2 off_p(pl, pt);
|
||||
glm::vec2 off_s(pr, pb);
|
||||
m_clip_uncut = glm::vec4(m_pos - off_p - glm::vec2(1), m_size + off_p + off_s + glm::vec2(2));
|
||||
m_clip = rect_intersection(m_clip_uncut, parent->m_clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_clip_uncut = m_clip = glm::vec4(m_pos, m_size);
|
||||
}
|
||||
|
||||
glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f));
|
||||
glm::mat4 scale = glm::scale(glm::vec3(m_size, 1.f));
|
||||
glm::mat4 prescale = glm::scale(glm::vec3(m_scale, 1.f));
|
||||
glm::mat4 pos = glm::translate(glm::vec3(m_pos, 0));
|
||||
m_mvp = proj * pos * scale * pivot * prescale;
|
||||
m_proj = proj;
|
||||
|
||||
for (int i = 0; i < m_children.size(); i++)
|
||||
{
|
||||
if (m_children[i]->m_destroyed)
|
||||
{
|
||||
remove_child(m_children[i].get());
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_size != old_size)
|
||||
handle_resize(old_size, m_size);
|
||||
|
||||
for (auto& c : m_children)
|
||||
c->update_internal(m_pos, proj);
|
||||
}
|
||||
|
||||
void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::id:
|
||||
m_nodeID_s = attr->Value();
|
||||
m_nodeID = const_hash(attr->Value());
|
||||
break;
|
||||
case kAttribute::Width:
|
||||
if (strcmp(attr->Value(), "auto") == 0)
|
||||
{
|
||||
YGNodeStyleSetWidth(y_node, YGUndefined);
|
||||
YGNodeStyleSetWidthPercent(y_node, YGUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strchr(attr->Value(), '%'))
|
||||
YGNodeStyleSetWidthPercent(y_node, attr->FloatValue());
|
||||
else
|
||||
YGNodeStyleSetWidth(y_node, attr->FloatValue());
|
||||
}
|
||||
break;
|
||||
case kAttribute::MinWidth:
|
||||
YGNodeStyleSetMinWidth(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::MaxWidth:
|
||||
YGNodeStyleSetMaxWidth(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::Height:
|
||||
if (strcmp(attr->Value(), "auto") == 0)
|
||||
{
|
||||
YGNodeStyleSetHeight(y_node, YGUndefined);
|
||||
YGNodeStyleSetHeightPercent(y_node, YGUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strchr(attr->Value(), '%'))
|
||||
YGNodeStyleSetHeightPercent(y_node, attr->FloatValue());
|
||||
else
|
||||
YGNodeStyleSetHeight(y_node, attr->FloatValue());
|
||||
}
|
||||
break;
|
||||
case kAttribute::MinHeight:
|
||||
YGNodeStyleSetMinHeight(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::MaxHeight:
|
||||
YGNodeStyleSetMaxHeight(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::Grow:
|
||||
YGNodeStyleSetFlexGrow(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::Shrink:
|
||||
YGNodeStyleSetFlexShrink(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::FlexDir:
|
||||
{
|
||||
YGFlexDirection dir = YGFlexDirectionRow;
|
||||
if (strcmp("col", attr->Value()) == 0)
|
||||
dir = YGFlexDirectionColumn;
|
||||
else if (strcmp("col-reverse", attr->Value()) == 0)
|
||||
dir = YGFlexDirectionColumnReverse;
|
||||
else if (strcmp("row", attr->Value()) == 0)
|
||||
dir = YGFlexDirectionRow;
|
||||
else if (strcmp("row-reverse", attr->Value()) == 0)
|
||||
dir = YGFlexDirectionRowReverse;
|
||||
YGNodeStyleSetFlexDirection(y_node, dir);
|
||||
break;
|
||||
}
|
||||
case kAttribute::FlexWrap:
|
||||
YGNodeStyleSetFlexWrap(y_node, attr->IntValue() ? YGWrapWrap : YGWrapNoWrap);
|
||||
break;
|
||||
case kAttribute::Justify:
|
||||
{
|
||||
YGJustify v = YGJustifyFlexStart;
|
||||
if (strcmp("center", attr->Value()) == 0)
|
||||
v = YGJustifyCenter;
|
||||
else if (strcmp("flex-start", attr->Value()) == 0)
|
||||
v = YGJustifyFlexStart;
|
||||
else if (strcmp("flex-end", attr->Value()) == 0)
|
||||
v = YGJustifyFlexEnd;
|
||||
else if (strcmp("space-around", attr->Value()) == 0)
|
||||
v = YGJustifySpaceAround;
|
||||
else if (strcmp("space-between", attr->Value()) == 0)
|
||||
v = YGJustifySpaceBetween;
|
||||
YGNodeStyleSetJustifyContent(y_node, v);
|
||||
break;
|
||||
}
|
||||
case kAttribute::Align:
|
||||
{
|
||||
YGAlign v = YGAlignStretch;
|
||||
if (strcmp("stretch", attr->Value()) == 0)
|
||||
v = YGAlignStretch;
|
||||
else if (strcmp("flex-start", attr->Value()) == 0)
|
||||
v = YGAlignFlexStart;
|
||||
else if (strcmp("flex-end", attr->Value()) == 0)
|
||||
v = YGAlignFlexEnd;
|
||||
else if (strcmp("center", attr->Value()) == 0)
|
||||
v = YGAlignCenter;
|
||||
YGNodeStyleSetAlignItems(y_node, v);
|
||||
break;
|
||||
}
|
||||
case kAttribute::Positioning:
|
||||
{
|
||||
YGPositionType v = YGPositionTypeRelative;
|
||||
if (strcmp("relative", attr->Value()) == 0)
|
||||
v = YGPositionTypeRelative;
|
||||
else if (strcmp("absolute", attr->Value()) == 0)
|
||||
v = YGPositionTypeAbsolute;
|
||||
YGNodeStyleSetPositionType(y_node, v);
|
||||
break;
|
||||
}
|
||||
case kAttribute::Position:
|
||||
{
|
||||
glm::vec4 v;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w);
|
||||
if (n == 2)
|
||||
{
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeLeft, v.x);
|
||||
YGNodeStyleSetPosition(y_node, YGEdgeTop, v.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeLeft, v.x);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeTop, v.y);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeRight, v.z);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeBottom, v.w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kAttribute::Padding:
|
||||
{
|
||||
glm::vec4 pad;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
|
||||
if (n == 1)
|
||||
{
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.x);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.x);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.y);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.z);
|
||||
YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kAttribute::Margin:
|
||||
{
|
||||
glm::vec4 pad;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
|
||||
if (n == 1)
|
||||
{
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.x);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.x);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.y);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.z);
|
||||
YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kAttribute::FloodEvents:
|
||||
m_flood_events = attr->IntValue() > 0;
|
||||
break;
|
||||
case kAttribute::AspectRatio:
|
||||
YGNodeStyleSetAspectRatio(y_node, attr->FloatValue());
|
||||
break;
|
||||
case kAttribute::RTL:
|
||||
if (strcmp("rtl", attr->Value()) == 0)
|
||||
SetRTL(YGDirectionRTL);
|
||||
else if (strcmp("ltr", attr->Value()) == 0)
|
||||
SetRTL(YGDirectionLTR);
|
||||
else if (strcmp("inherit", attr->Value()) == 0)
|
||||
SetRTL(YGDirectionInherit);
|
||||
else
|
||||
{
|
||||
LOG("Attribute %s for RTL unrecognized", attr->Value());
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::load_internal(const tinyxml2::XMLElement* x_node)
|
||||
{
|
||||
m_name = x_node->Name();
|
||||
//LOG("node %s", m_name.c_str());
|
||||
|
||||
init();
|
||||
|
||||
auto attr = x_node->FirstAttribute();
|
||||
while (attr)
|
||||
{
|
||||
parse_attributes((kAttribute)const_hash(attr->Name()), attr);
|
||||
attr = attr->Next();
|
||||
}
|
||||
|
||||
create();
|
||||
|
||||
auto x_child = x_node->FirstChildElement();
|
||||
while (x_child)
|
||||
{
|
||||
kWidget child_id = (kWidget)const_hash(x_child->Name());
|
||||
switch (child_id)
|
||||
{
|
||||
#define CASE(W,C) case W: { auto n = new C(); add_child(n); n->load_internal(x_child); break; }
|
||||
CASE(kWidget::Node, Node);
|
||||
CASE(kWidget::Border, NodeBorder);
|
||||
CASE(kWidget::Image, NodeImage);
|
||||
CASE(kWidget::ImageTexture, NodeImageTexture);
|
||||
CASE(kWidget::Icon, NodeIcon);
|
||||
CASE(kWidget::Text, NodeText);
|
||||
CASE(kWidget::TextInput, NodeTextInput);
|
||||
CASE(kWidget::Button, NodeButton);
|
||||
CASE(kWidget::ButtonCustom, NodeButtonCustom);
|
||||
CASE(kWidget::ComboBox, NodeComboBox);
|
||||
CASE(kWidget::SliderH, NodeSliderH);
|
||||
CASE(kWidget::SliderV, NodeSliderV);
|
||||
CASE(kWidget::SliderHue, NodeSliderHue);
|
||||
CASE(kWidget::PopupMenu, NodePopupMenu);
|
||||
CASE(kWidget::Viewport, NodeViewport);
|
||||
CASE(kWidget::CheckBox, NodeCheckBox);
|
||||
CASE(kWidget::Layer, NodeLayer);
|
||||
CASE(kWidget::PanelLayer, NodePanelLayer);
|
||||
CASE(kWidget::PanelBrush, NodePanelBrush);
|
||||
CASE(kWidget::PanelColor, NodePanelColor);
|
||||
CASE(kWidget::PanelStroke, NodePanelStroke);
|
||||
CASE(kWidget::PanelGrid, NodePanelGrid);
|
||||
CASE(kWidget::ColorQuad, NodeColorQuad);
|
||||
CASE(kWidget::StrokePreview, NodeStrokePreview);
|
||||
CASE(kWidget::Canvas, NodeCanvas);
|
||||
CASE(kWidget::Scroll, NodeScroll);
|
||||
CASE(kWidget::DialogBrowse, NodeDialogBrowse);
|
||||
CASE(kWidget::DialogBrowseItem, NodeDialogBrowseItem);
|
||||
CASE(kWidget::DialogCloud, NodeDialogCloud);
|
||||
CASE(kWidget::DialogCloudItem, NodeDialogCloudItem);
|
||||
CASE(kWidget::ColorWheel, NodeColorWheel);
|
||||
CASE(kWidget::ColorPicker, NodeColorPicker);
|
||||
#undef CASE
|
||||
case kWidget::Ref:
|
||||
{
|
||||
auto ids = x_child->Attribute("id");
|
||||
auto id = const_hash(ids);
|
||||
auto& ref = (*m_manager)[id]->m_children[0];
|
||||
auto n = ref->clone();
|
||||
n->m_nodeID_s = ids;
|
||||
n->m_nodeID = id;
|
||||
add_child(n);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG("instancing UNKNOWN node: %s", x_child->Name());
|
||||
auto n = new Node();
|
||||
add_child(n);
|
||||
n->load_internal(x_child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
x_child = x_child->NextSiblingElement();
|
||||
}
|
||||
loaded();
|
||||
}
|
||||
|
||||
void Node::draw()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Node* Node::clone()
|
||||
{
|
||||
Node* n = clone_instantiate();
|
||||
clone_copy(n);
|
||||
clone_children(n);
|
||||
clone_finalize(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
Node* Node::clone_instantiate() const
|
||||
{
|
||||
return new Node();
|
||||
}
|
||||
|
||||
void Node::clone_copy(Node* dest) const
|
||||
{
|
||||
YGNodeCopyStyle(dest->y_node, y_node);
|
||||
dest->m_manager = m_manager;
|
||||
|
||||
dest->m_name = m_name;
|
||||
dest->m_nodeID_s = m_nodeID_s;
|
||||
dest->m_nodeID = m_nodeID;
|
||||
dest->m_display = m_display;
|
||||
dest->m_pos = m_pos;
|
||||
dest->m_size = m_size;
|
||||
dest->m_clip = m_clip;
|
||||
dest->m_flood_events = m_flood_events;
|
||||
|
||||
dest->m_manager = m_manager;
|
||||
dest->current_mouse_capture = current_mouse_capture;
|
||||
dest->current_key_capture = current_key_capture;
|
||||
dest->m_mouse_captured = m_mouse_captured;
|
||||
dest->m_key_captured = m_key_captured;
|
||||
dest->m_proj = m_proj;
|
||||
dest->m_mvp = m_mvp;
|
||||
dest->m_mouse_inside = m_mouse_inside;
|
||||
dest->m_capture_children = m_capture_children;
|
||||
dest->m_destroyed = m_destroyed;
|
||||
dest->m_scale = m_scale;
|
||||
dest->m_pos_origin = m_pos_origin;
|
||||
dest->m_pos_offset = m_pos_offset;
|
||||
dest->m_pos_offset_childred = m_pos_offset_childred;
|
||||
dest->m_clip_uncut = m_clip_uncut;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Node::clone_children(Node* dest) const
|
||||
{
|
||||
for (auto& c : m_children)
|
||||
{
|
||||
Node* cn = c->clone();
|
||||
dest->m_children.emplace_back(cn);
|
||||
cn->parent = dest;
|
||||
cn->m_manager = dest->m_manager;
|
||||
cn->loaded();
|
||||
YGNodeInsertChild(dest->y_node, cn->y_node, YGNodeGetChildCount(dest->y_node));
|
||||
}
|
||||
}
|
||||
|
||||
void Node::clone_finalize(Node* dest) const
|
||||
{
|
||||
/* find controllers and stuff */
|
||||
}
|
||||
224
src/node.h
Normal file
224
src/node.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
#include "event.h"
|
||||
|
||||
enum class kAttribute : uint16_t
|
||||
{
|
||||
id = const_hash("id"),
|
||||
Width = const_hash("width"),
|
||||
MinWidth = const_hash("min-width"),
|
||||
MaxWidth = const_hash("max-width"),
|
||||
Height = const_hash("height"),
|
||||
MinHeight = const_hash("min-height"),
|
||||
MaxHeight = const_hash("max-height"),
|
||||
Divisions = const_hash("divisions"),
|
||||
InnerRadius = const_hash("inner-radius"),
|
||||
OuterRadius = const_hash("outer-radius"),
|
||||
Grow = const_hash("grow"),
|
||||
Shrink = const_hash("shrink"),
|
||||
FlexDir = const_hash("dir"),
|
||||
FlexWrap = const_hash("wrap"),
|
||||
Padding = const_hash("pad"),
|
||||
Margin = const_hash("margin"),
|
||||
Color = const_hash("color"),
|
||||
Thickness = const_hash("thickness"),
|
||||
BorderColor = const_hash("border-color"),
|
||||
Type = const_hash("type"),
|
||||
Text = const_hash("text"),
|
||||
FontFace = const_hash("font-face"),
|
||||
FontSize = const_hash("font-size"),
|
||||
Justify = const_hash("justify"),
|
||||
Align = const_hash("align"),
|
||||
Path = const_hash("path"),
|
||||
Region = const_hash("region"),
|
||||
Position = const_hash("position"),
|
||||
Positioning = const_hash("positioning"),
|
||||
FloodEvents = const_hash("flood-events"),
|
||||
Icon = const_hash("icon"),
|
||||
Selected = const_hash("selected"),
|
||||
Template = const_hash("template"),
|
||||
Value = const_hash("value"),
|
||||
Range = const_hash("range"),
|
||||
AspectRatio = const_hash("aspect-ratio"),
|
||||
ComboList = const_hash("combo-list"),
|
||||
Mips = const_hash("mips"),
|
||||
Default = const_hash("default"),
|
||||
RTL = const_hash("rtl"),
|
||||
};
|
||||
|
||||
enum class kWidget : uint16_t
|
||||
{
|
||||
Node = const_hash("node"),
|
||||
Border = const_hash("border"),
|
||||
Shape = const_hash("shape"),
|
||||
Text = const_hash("text"),
|
||||
TextInput = const_hash("text-input"),
|
||||
Image = const_hash("image"),
|
||||
ImageTexture = const_hash("image-texture"),
|
||||
Icon = const_hash("icon"),
|
||||
Button = const_hash("button"),
|
||||
ButtonCustom = const_hash("button-custom"),
|
||||
ComboBox = const_hash("combobox"),
|
||||
SliderH = const_hash("slider-h"),
|
||||
SliderV = const_hash("slider-v"),
|
||||
SliderHue = const_hash("slider-hue"),
|
||||
PopupMenu = const_hash("popup-menu"),
|
||||
Viewport = const_hash("viewport"),
|
||||
Ref = const_hash("ref"),
|
||||
CheckBox = const_hash("checkbox"),
|
||||
Layer = const_hash("layer"),
|
||||
PanelLayer = const_hash("panel-layer"),
|
||||
PanelBrush = const_hash("panel-brush"),
|
||||
PanelColor = const_hash("panel-color"),
|
||||
PanelStroke = const_hash("panel-stroke"),
|
||||
PanelGrid = const_hash("panel-grid"),
|
||||
ColorQuad = const_hash("color-quad"),
|
||||
StrokePreview = const_hash("stroke-preview"),
|
||||
Canvas = const_hash("canvas"),
|
||||
Scroll = const_hash("scroll"),
|
||||
DialogBrowse = const_hash("dialog-browse"),
|
||||
DialogBrowseItem = const_hash("dialog-browse-item"),
|
||||
DialogCloud = const_hash("dialog-cloud"),
|
||||
DialogCloudItem = const_hash("dialog-cloud-item"),
|
||||
ColorWheel = const_hash("colorwheel"),
|
||||
ColorPicker = const_hash("color-picker"),
|
||||
};
|
||||
|
||||
class Node
|
||||
{
|
||||
friend class LayoutManager;
|
||||
public:
|
||||
Node* parent{ nullptr };
|
||||
YGNodeRef y_node{ nullptr };
|
||||
class LayoutManager* m_manager;
|
||||
uint16_t m_nodeID;
|
||||
std::string m_nodeID_s;
|
||||
std::vector<std::shared_ptr<Node>> m_children;
|
||||
Node* current_mouse_capture = nullptr;
|
||||
Node* current_key_capture = nullptr;
|
||||
bool m_mouse_captured = false;
|
||||
bool m_key_captured = false;
|
||||
|
||||
glm::mat4 m_proj;
|
||||
glm::mat4 m_mvp;
|
||||
bool m_mouse_inside = false;
|
||||
bool m_flood_events = false;
|
||||
bool m_capture_children = true; // wether to capture children events when xx_capture() is used
|
||||
bool m_destroyed = false;
|
||||
|
||||
bool m_mouse_ignore = true;
|
||||
float m_zoom = 1.f;
|
||||
glm::vec2 m_scale{ 1, 1 };
|
||||
glm::vec2 m_pos{ 0, 0 };
|
||||
glm::vec2 m_pos_origin{ 0, 0 }; // original layout position without offset
|
||||
glm::vec2 m_pos_offset{ 0, 0 }; // artificial position offset for scrolling
|
||||
glm::vec2 m_pos_offset_childred{ 0, 0 }; // artificial position offset for scrolling
|
||||
glm::vec2 m_size{ 0, 0 };
|
||||
glm::vec4 m_clip{ 0, 0, 0, 0 };
|
||||
glm::vec4 m_clip_uncut{ 0, 0, 0, 0 };
|
||||
std::string m_name;
|
||||
bool m_display = true;
|
||||
|
||||
Node(const Node&) = delete;
|
||||
Node& operator=(const Node&) = delete;
|
||||
Node&& operator=(Node&& o);
|
||||
Node(Node&& o);
|
||||
Node();
|
||||
~Node();
|
||||
|
||||
void SetWidth(float value);
|
||||
void SetWidthP(float value);
|
||||
void SetHeight(float value);
|
||||
void SetHeightP(float value);
|
||||
void SetSize(glm::vec2 value);
|
||||
void SetSize(float w, float h);
|
||||
|
||||
void SetPadding(float t, float r, float b, float l);
|
||||
glm::vec4 GetPadding() const;
|
||||
void SetPosition(float l, float t, float r, float b);
|
||||
void SetPosition(float l, float t);
|
||||
void SetPosition(const glm::vec2 pos);
|
||||
|
||||
void SetFlexGrow(float value);
|
||||
void SetFlexShrink(float value);
|
||||
void SetFlexDir(YGFlexDirection value);
|
||||
void SetFlexWrap(YGWrap value);
|
||||
void SetJustify(YGJustify value);
|
||||
void SetAlign(YGAlign value);
|
||||
void SetPositioning(YGPositionType value);
|
||||
void SetAspectRatio(float ar);
|
||||
void SetRTL(YGDirection dir);
|
||||
|
||||
glm::vec2 GetPosition();
|
||||
float GetWidth();
|
||||
float GetHeight();
|
||||
glm::vec2 GetSize();
|
||||
YGDirection GetRTL();
|
||||
|
||||
virtual void restore_context();;
|
||||
virtual void clear_context();
|
||||
void update(float width, float height, float zoom);
|
||||
void update();
|
||||
void update_internal(const glm::vec2& origin, const glm::mat4& proj);
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr);
|
||||
void load_internal(const tinyxml2::XMLElement* x_node);
|
||||
virtual void draw();
|
||||
Node* clone();
|
||||
virtual Node* clone_instantiate() const;
|
||||
virtual void clone_copy(Node* dest) const;
|
||||
virtual void clone_children(Node* dest) const;
|
||||
virtual void clone_finalize(Node* dest) const;;
|
||||
void watch(std::function<bool(Node*)> observer);
|
||||
void destroy();
|
||||
Node* root();
|
||||
|
||||
template<class T = Node> T* find(const char* ids)
|
||||
{
|
||||
uint16_t id = const_hash(ids);
|
||||
if (id == m_nodeID)
|
||||
return static_cast<T*>(this);
|
||||
for (auto& c : m_children)
|
||||
if (auto found = c->find(ids))
|
||||
return static_cast<T*>(found);
|
||||
return nullptr;
|
||||
}
|
||||
template<class T> T* add_child()
|
||||
{
|
||||
auto* n = new T;
|
||||
n->m_manager = m_manager;
|
||||
n->parent = parent;
|
||||
n->init();
|
||||
n->create();
|
||||
n->loaded();
|
||||
add_child(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
virtual kEventResult on_event(Event* e);
|
||||
virtual kEventResult handle_event(Event* e);
|
||||
virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size);;
|
||||
virtual void create();
|
||||
virtual void init();
|
||||
virtual void loaded();
|
||||
virtual void added(Node* parent);
|
||||
virtual void removed(Node* parent);
|
||||
const Node* init_template(const char* id);
|
||||
void async_start();
|
||||
void async_update();
|
||||
void async_end();
|
||||
void add_child(Node* n);
|
||||
void add_child(Node* n, int index);
|
||||
void add_child(std::shared_ptr<Node> n);
|
||||
void add_child(std::shared_ptr<Node> n, int index);
|
||||
void remove_child(Node* n);
|
||||
void remove_all_children();
|
||||
void move_child(Node* n, int index);
|
||||
void move_child_offset(Node* n, int offset);
|
||||
int get_child_index(Node* n);
|
||||
Node* get_child_at(int index);
|
||||
glm::vec4 get_children_rect() const;
|
||||
void mouse_capture();
|
||||
void mouse_release();
|
||||
void key_capture();
|
||||
void key_release();
|
||||
};
|
||||
|
||||
83
src/node_border.cpp
Normal file
83
src/node_border.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_border.h"
|
||||
#include "shader.h"
|
||||
|
||||
ui::Plane NodeBorder::m_plane;
|
||||
|
||||
NodeBorder::NodeBorder()
|
||||
{
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
void NodeBorder::static_init()
|
||||
{
|
||||
m_plane.create<1>(1, 1);
|
||||
}
|
||||
Node* NodeBorder::clone_instantiate() const
|
||||
{
|
||||
return new NodeBorder();
|
||||
}
|
||||
void NodeBorder::clone_copy(Node* dest) const
|
||||
{
|
||||
Node::clone_copy(dest);
|
||||
NodeBorder* n = static_cast<NodeBorder*>(dest);
|
||||
n->m_color = m_color;
|
||||
n->m_border_color = m_border_color;
|
||||
n->m_thinkness = m_thinkness;
|
||||
n->m_mouse_ignore = false;
|
||||
}
|
||||
void NodeBorder::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
Node::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Color:
|
||||
{
|
||||
glm::vec4 pad;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
|
||||
if (n == 1)
|
||||
m_color = glm::vec4(pad.x, pad.x, pad.x, 1);
|
||||
else
|
||||
m_color = pad;
|
||||
break;
|
||||
}
|
||||
case kAttribute::BorderColor:
|
||||
{
|
||||
glm::vec4 pad;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
|
||||
if (n == 1)
|
||||
m_border_color = glm::vec4(pad.x);
|
||||
else
|
||||
m_border_color = pad;
|
||||
break;
|
||||
}
|
||||
case kAttribute::Thickness:
|
||||
m_thinkness = attr->FloatValue();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void NodeBorder::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
ui::ShaderManager::use(kShader::Color);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
|
||||
if (m_color.a > 0.f)
|
||||
{
|
||||
m_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color);
|
||||
m_plane.draw_fill();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
if (m_thinkness > 0 && m_border_color.a > 0.f)
|
||||
{
|
||||
glLineWidth(m_thinkness);
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, m_border_color);
|
||||
m_border_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
m_plane.draw_stroke();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
18
src/node_border.h
Normal file
18
src/node_border.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "shape.h"
|
||||
|
||||
class NodeBorder : public Node
|
||||
{
|
||||
public:
|
||||
static ui::Plane m_plane;
|
||||
glm::vec4 m_color{ 0, 0, 0, 1 };
|
||||
glm::vec4 m_border_color{ 1, 1, 1, 1 };
|
||||
float m_thinkness{ 0 };
|
||||
NodeBorder();
|
||||
static void static_init();
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
virtual void draw() override;
|
||||
};
|
||||
127
src/node_button.cpp
Normal file
127
src/node_button.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_button.h"
|
||||
|
||||
Node* NodeButton::clone_instantiate() const
|
||||
{
|
||||
return new NodeButton();
|
||||
}
|
||||
|
||||
void NodeButton::clone_children(Node* dest) const
|
||||
{
|
||||
Node::clone_children(dest);
|
||||
NodeButton* n = static_cast<NodeButton*>(dest);
|
||||
n->m_border = (NodeBorder*)n->m_children[0].get();
|
||||
n->m_text = (NodeText*)n->m_border->m_children[0].get();
|
||||
}
|
||||
|
||||
void NodeButton::clone_copy(Node* dest) const
|
||||
{
|
||||
Node::clone_copy(dest);
|
||||
NodeButton* n = static_cast<NodeButton*>(dest);
|
||||
//n->m_border = (NodeBorder*)m_border->clone();
|
||||
//n->m_text = (NodeText*)m_text->clone();
|
||||
n->color_normal = color_normal;
|
||||
n->color_hover = color_hover;
|
||||
n->color_down = color_down;
|
||||
//n->on_click = on_click;
|
||||
n->m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeButton::init()
|
||||
{
|
||||
m_border = new NodeBorder();
|
||||
m_text = new NodeText();
|
||||
add_child(m_border);
|
||||
m_border->add_child(m_text);
|
||||
m_border->init();
|
||||
m_border->m_color = color_normal;
|
||||
m_text->init();
|
||||
m_text->m_font = "arial";
|
||||
m_text->m_font_size = 11;
|
||||
m_border->SetAlign(YGAlignCenter);
|
||||
m_border->SetJustify(YGJustifyCenter);
|
||||
m_border->m_mouse_ignore = false;
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeButton::create()
|
||||
{
|
||||
m_border->create();
|
||||
m_text->create();
|
||||
m_border->m_mouse_ignore = false;
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeButton::loaded()
|
||||
{
|
||||
m_border->m_thinkness = 1;
|
||||
m_border->m_border_color = glm::vec4(0, 0, 0, 1);
|
||||
m_border->m_color = color_normal;
|
||||
m_border->m_mouse_ignore = false;
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeButton::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Color:
|
||||
m_border->parse_attributes(ka, attr);
|
||||
color_normal = m_border->m_color;
|
||||
color_hover = glm::clamp(m_border->m_color + glm::vec4(.1, .1, .1, 0), {0,0,0,0}, {1,1,1,1});
|
||||
color_down = glm::clamp(m_border->m_color + glm::vec4(.2, .2, .2, 0), {0,0,0,0}, {1,1,1,1});
|
||||
case kAttribute::Padding:
|
||||
case kAttribute::Width:
|
||||
case kAttribute::Height:
|
||||
case kAttribute::Thickness:
|
||||
case kAttribute::BorderColor:
|
||||
m_border->parse_attributes(ka, attr);
|
||||
break;
|
||||
case kAttribute::Text:
|
||||
case kAttribute::FontFace:
|
||||
case kAttribute::FontSize:
|
||||
m_text->parse_attributes(ka, attr);
|
||||
break;
|
||||
default:
|
||||
Node::parse_attributes(ka, attr);
|
||||
break;
|
||||
}
|
||||
// m_border->parse_attributes(ka, attr);
|
||||
// m_text->parse_attributes(ka, attr);
|
||||
}
|
||||
|
||||
void NodeButton::set_color(const glm::vec4& c)
|
||||
{
|
||||
color_normal = c;
|
||||
m_border->m_color = color_normal;
|
||||
}
|
||||
|
||||
kEventResult NodeButton::handle_event(Event* e)
|
||||
{
|
||||
Node::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
m_border->m_color = color_hover;
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
m_border->m_color = color_normal;
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_border->m_color = color_down;
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
m_border->m_color = color_normal;
|
||||
if (m_mouse_inside && on_click != nullptr)
|
||||
on_click(this);
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
m_border->m_color = color_normal;
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
24
src/node_button.h
Normal file
24
src/node_button.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_border.h"
|
||||
#include "node_text.h"
|
||||
|
||||
class NodeButton : public Node
|
||||
{
|
||||
public:
|
||||
NodeBorder* m_border;
|
||||
NodeText* m_text;
|
||||
glm::vec4 color_normal{ .1, .1, .1, 1 };
|
||||
glm::vec4 color_hover{ .2, .2, .2, 1 };
|
||||
glm::vec4 color_down{ .3, .3, .3, 1 };
|
||||
std::function<void(Node* target)> on_click;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_children(Node* dest) const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void create() override;
|
||||
virtual void loaded() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
void set_color(const glm::vec4& c);
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
83
src/node_button_custom.cpp
Normal file
83
src/node_button_custom.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_button_custom.h"
|
||||
|
||||
Node* NodeButtonCustom::clone_instantiate() const
|
||||
{
|
||||
return new NodeButtonCustom();
|
||||
}
|
||||
|
||||
void NodeButtonCustom::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
NodeButtonCustom* n = static_cast<NodeButtonCustom*>(dest);
|
||||
n->color_normal = color_normal;
|
||||
n->color_hover = color_hover;
|
||||
n->color_down = color_down;
|
||||
n->m_mouse_ignore = false;
|
||||
n->m_color = color_normal;
|
||||
}
|
||||
|
||||
void NodeButtonCustom::loaded()
|
||||
{
|
||||
NodeBorder::loaded();
|
||||
//m_thinkness = 1;
|
||||
//m_border_color = glm::vec4(0, 0, 0, 1);
|
||||
m_color = color_normal;
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeButtonCustom::set_color(const glm::vec4& c)
|
||||
{
|
||||
color_normal = c;
|
||||
m_color = color_normal;
|
||||
}
|
||||
|
||||
kEventResult NodeButtonCustom::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
m_color = color_hover;
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
m_color = color_normal;
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_color = color_down;
|
||||
mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
#ifdef __IOS__
|
||||
m_color = color_normal;
|
||||
#else
|
||||
m_color = m_mouse_inside ? color_hover : color_normal;
|
||||
#endif
|
||||
mouse_release();
|
||||
if (m_mouse_inside && on_click != nullptr)
|
||||
on_click(this);
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
m_color = color_normal;
|
||||
mouse_release();
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeButtonCustom::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
NodeBorder::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Color:
|
||||
color_normal = m_color;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
17
src/node_button_custom.h
Normal file
17
src/node_button_custom.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeButtonCustom : public NodeBorder
|
||||
{
|
||||
public:
|
||||
glm::vec4 color_normal{ .2, .2, .2, 1 };
|
||||
glm::vec4 color_hover{ .3, .3, .3, 1 };
|
||||
glm::vec4 color_down{ .4, .4, .4, 1 };
|
||||
std::function<void(Node* target)> on_click;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void loaded() override;
|
||||
void set_color(const glm::vec4& c);
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
};
|
||||
451
src/node_canvas.cpp
Normal file
451
src/node_canvas.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "log.h"
|
||||
#include "node_canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
|
||||
Node* NodeCanvas::clone_instantiate() const
|
||||
{
|
||||
return new NodeCanvas();
|
||||
}
|
||||
|
||||
void NodeCanvas::init()
|
||||
{
|
||||
m_mouse_ignore = false;
|
||||
m_canvas = std::make_unique<ui::Canvas>();
|
||||
m_canvas->create(CANVAS_RES, CANVAS_RES);
|
||||
m_canvas->m_unsaved = false;
|
||||
m_canvas->m_node = this;
|
||||
|
||||
m_sampler.create();
|
||||
m_sampler.set_filter(GL_LINEAR, GL_NEAREST);
|
||||
|
||||
m_sampler_linear.create(GL_LINEAR);
|
||||
m_sampler_stencil.create(GL_LINEAR, GL_REPEAT);
|
||||
m_face_plane.create<1>(2, 2);
|
||||
m_line.create();
|
||||
CanvasMode::node = this;
|
||||
CanvasMode::canvas = m_canvas.get();
|
||||
for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++)
|
||||
for (auto m : ui::Canvas::modes[i])
|
||||
m->init();
|
||||
|
||||
m_grid.create(1, 1, m_grid_divs);
|
||||
}
|
||||
|
||||
void NodeCanvas::restore_context()
|
||||
{
|
||||
Node::restore_context();
|
||||
m_canvas->create(CANVAS_RES, CANVAS_RES);
|
||||
|
||||
m_sampler.create();
|
||||
|
||||
m_sampler.set_filter(GL_LINEAR, GL_NEAREST);
|
||||
m_face_plane.create<1>(2, 2);
|
||||
m_canvas->snapshot_restore();
|
||||
CanvasMode::node = this;
|
||||
CanvasMode::canvas = m_canvas.get();
|
||||
for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++)
|
||||
for (auto m : ui::Canvas::modes[i])
|
||||
m->init();
|
||||
}
|
||||
|
||||
void NodeCanvas::clear_context()
|
||||
{
|
||||
Node::clear_context();
|
||||
m_canvas->snapshot_save(data_path);
|
||||
m_canvas->clear_context();
|
||||
// TODO: clear CanvasMode objects
|
||||
}
|
||||
|
||||
void NodeCanvas::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
GLint vp[4];
|
||||
GLfloat cc[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
float zoom = root()->m_zoom;
|
||||
auto box = m_clip * zoom;
|
||||
glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w);
|
||||
glViewport(c.x, c.y, c.z, c.w);
|
||||
|
||||
//m_canvas->m_cam_rot = m_pan * 0.003f;
|
||||
|
||||
glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f);
|
||||
glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.01f, 1000.f);
|
||||
glm::mat4 camera = glm::eulerAngleXY(m_canvas->m_cam_rot.y, m_canvas->m_cam_rot.x) *
|
||||
glm::translate(m_canvas->m_cam_pos);
|
||||
|
||||
m_canvas->m_mv = camera;
|
||||
m_canvas->m_proj = proj;
|
||||
m_canvas->m_box = box;
|
||||
m_canvas->m_vp = c;
|
||||
|
||||
// auto plane_mvp = proj * camera * transform *
|
||||
// glm::scale(glm::vec3(sz, 1));
|
||||
|
||||
|
||||
m_sampler.bind(0);
|
||||
m_sampler.bind(1);
|
||||
m_sampler.bind(2);
|
||||
m_sampler_stencil.bind(3);
|
||||
auto blend = glIsEnabled(GL_BLEND);
|
||||
auto depth = glIsEnabled(GL_DEPTH_TEST);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
||||
{
|
||||
auto plane_mvp = proj * camera *
|
||||
glm::scale(glm::vec3(m_canvas->m_order.size() + 500)) *
|
||||
m_canvas->m_plane_transform[plane_index] *
|
||||
glm::translate(glm::vec3(0, 0, -1));
|
||||
|
||||
ui::ShaderManager::use(kShader::Checkerboard);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
|
||||
m_face_plane.draw_fill();
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
float pitch = 0;
|
||||
if (auto slider = root()->find<NodeSliderH>("pitch-slider"))
|
||||
pitch = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||
float yaw = 0;
|
||||
if (auto slider = root()->find<NodeSliderH>("yaw-slider"))
|
||||
yaw = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||
float roll = 0;
|
||||
if (auto slider = root()->find<NodeSliderH>("roll-slider"))
|
||||
roll = (slider->get_value() - 0.5) * glm::half_pi<float>();
|
||||
|
||||
|
||||
for (size_t i = 0; i < m_canvas->m_order.size(); i++)
|
||||
{
|
||||
auto layer_index = m_canvas->m_order[i];
|
||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
||||
{
|
||||
if (m_canvas->m_layers[layer_index].m_opacity == .0f)
|
||||
continue;
|
||||
|
||||
int z = (int)m_canvas->m_order.size() - i;
|
||||
auto plane_mvp_z = proj * camera *
|
||||
glm::scale(glm::vec3(z + 1)) *
|
||||
glm::eulerAngleYXZ(yaw, pitch, roll) *
|
||||
m_canvas->m_plane_transform[plane_index] *
|
||||
glm::translate(glm::vec3(0, 0, -1));
|
||||
|
||||
if (m_canvas->m_state == ui::Canvas::kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
|
||||
{
|
||||
m_sampler.bind(0);
|
||||
ui::ShaderManager::use(kShader::CompErase);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_int(kShaderUniform::TexStroke, 1);
|
||||
//ui::ShaderManager::u_int(kShaderUniform::TexMask, 2);
|
||||
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity);
|
||||
//ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked);
|
||||
//ui::ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_tmp[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
m_canvas->m_smask.m_rtt[plane_index].bindTexture();
|
||||
m_face_plane.draw_fill();
|
||||
m_canvas->m_smask.m_rtt[plane_index].unbindTexture();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture();
|
||||
}
|
||||
else if(m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
|
||||
{
|
||||
m_sampler.bind(0);
|
||||
auto& paper = TextureManager::get(const_hash("data/paper.jpg"));
|
||||
ui::ShaderManager::use(kShader::CompDraw);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_int(kShaderUniform::TexStroke, 1);
|
||||
ui::ShaderManager::u_int(kShaderUniform::TexMask, 2);
|
||||
//ui::ShaderManager::u_int(kShaderUniform::TexStencil, 3);
|
||||
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active);
|
||||
ui::ShaderManager::u_int(kShaderUniform::BlendMode, m_canvas->m_current_stroke->m_brush.m_blend_mode);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_tmp[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
m_canvas->m_smask.m_rtt[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
paper.bind();
|
||||
m_face_plane.draw_fill();
|
||||
paper.unbind();
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
m_canvas->m_smask.m_rtt[plane_index].unbindTexture();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_tmp[plane_index].unbindTexture();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sampler.bind(0);
|
||||
m_sampler_linear.bind(1);
|
||||
ui::ShaderManager::use(kShader::TextureAlphaSep);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_int(kShaderUniform::TexA, 1);
|
||||
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Highlight, m_canvas->m_layers[layer_index].m_hightlight);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture();
|
||||
|
||||
m_face_plane.draw_fill();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture();
|
||||
}
|
||||
|
||||
// if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
|
||||
// {
|
||||
// glDisable(GL_BLEND);
|
||||
// ui::ShaderManager::use(kShader::TextureAlpha);
|
||||
// ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
// ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity);
|
||||
// ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z);
|
||||
// ui::ShaderManager::u_float(kShaderUniform::Alpha, 1);
|
||||
// m_canvas->m_tmp[plane_index].bindTexture();
|
||||
// m_face_plane.draw_fill();
|
||||
// m_canvas->m_tmp[plane_index].unbindTexture();
|
||||
// glEnable(GL_BLEND);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (m_canvas->m_smask_active && !m_canvas->m_show_tmp)
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
||||
{
|
||||
auto plane_mvp = proj * camera *
|
||||
m_canvas->m_plane_transform[plane_index] *
|
||||
glm::translate(glm::vec3(0, 0, -1));
|
||||
|
||||
ui::ShaderManager::use(kShader::TextureAlpha);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_float(kShaderUniform::Alpha, 0.5f);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Highlight, 0);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
m_canvas->m_smask.m_rtt[plane_index].bindTexture();
|
||||
m_face_plane.draw_fill();
|
||||
m_canvas->m_smask.m_rtt[plane_index].unbindTexture();
|
||||
}
|
||||
}
|
||||
*/
|
||||
for (auto& mode : *m_canvas->m_mode)
|
||||
mode->on_Draw(ortho_proj, proj, camera);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
if (m_canvas->m_smask_active)
|
||||
{
|
||||
m_canvas->modes[(int)Canvas::kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
|
||||
m_canvas->modes[(int)Canvas::kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera);
|
||||
}
|
||||
|
||||
// keep drawing the grids
|
||||
if (m_canvas->m_state != ui::Canvas::kCanvasMode::Grid)
|
||||
for (auto& mode : ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid])
|
||||
mode->on_Draw(ortho_proj, proj, camera);
|
||||
|
||||
|
||||
if (App::I.grid->m_groud_opacity->get_value() > 0.f)
|
||||
{
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// DRAW GRIDS
|
||||
ui::ShaderManager::use(kShader::Color);
|
||||
|
||||
// ground grid
|
||||
int grid_divs = glm::floor(App::I.grid->m_groud_scale->get_value() * 100);
|
||||
if (grid_divs != m_grid_divs && (grid_divs % 2) == 0)
|
||||
{
|
||||
m_grid_divs = grid_divs;
|
||||
m_grid.create(1, 1, grid_divs);
|
||||
}
|
||||
float grid_scale = m_grid_divs * 0.01f;
|
||||
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(
|
||||
glm::vec3(App::I.grid->m_groud_value->get_value()),
|
||||
App::I.grid->m_groud_opacity->get_value()));
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera
|
||||
* glm::translate(glm::vec3(0, glm::pow(App::I.grid->m_groud_height->get_value() - 0.5f, 3), 0))
|
||||
* glm::eulerAngleX(glm::radians(90.f))
|
||||
* glm::scale(glm::vec3(grid_scale, grid_scale, 1))
|
||||
);
|
||||
//m_grid.draw_stroke();
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
App::I.grid->m_hm_plane.draw_fill();
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
App::I.grid->m_hm_plane.draw_stroke();
|
||||
}
|
||||
|
||||
// box grid
|
||||
// ceiling
|
||||
// ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(
|
||||
// glm::vec3(App::I.grid->m_groud_value->get_value()),
|
||||
// App::I.grid->m_box_opacity->get_value()));
|
||||
// ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera
|
||||
// * glm::translate(glm::vec3(0, (App::I.grid->m_groud_height->get_value() + App::I.grid->m_box_height->get_value() - 0.5f) * 2.f, 0))
|
||||
// * glm::eulerAngleX(glm::radians(90.f))
|
||||
// * glm::scale(glm::vec3(grid_scale, grid_scale, 1))
|
||||
// );
|
||||
// m_grid.draw_stroke();
|
||||
|
||||
|
||||
//ui::ShaderManager::use(kShader::Equirect);
|
||||
//ui::ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(.5, .5, 1)));
|
||||
//ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
//glBindTexture(GL_TEXTURE_CUBE_MAP, m_canvas->cube_id);
|
||||
//m_face_plane.draw_fill();
|
||||
//glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
|
||||
// ui::ShaderManager::use(kShader::Color);
|
||||
// ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera);
|
||||
// ui::ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 });
|
||||
// static glm::vec4 AB[4]{ {-.75, 0, -1, 1},{ -.75, 0, 1, 1 } };
|
||||
// m_line.update_vertices(AB);
|
||||
// m_line.draw_stroke();
|
||||
|
||||
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
m_sampler.unbind();
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
}
|
||||
|
||||
void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size)
|
||||
{
|
||||
if (new_size.x != m_canvas->m_width || new_size.y != m_canvas->m_height)
|
||||
{
|
||||
#if __IOS__
|
||||
m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale,
|
||||
(int)new_size.y * m_canvas->m_mixer_scale, -1, GL_RGBA16F);
|
||||
#else
|
||||
m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale,
|
||||
(int)new_size.y * m_canvas->m_mixer_scale, -1, GL_RGBA32F);
|
||||
#endif
|
||||
if (auto img = root()->find<NodeImageTexture>("tex-debug"))
|
||||
img->tex.assign(m_canvas->m_mixer.getTextureID());
|
||||
// m_canvas->resize((int)new_size.x, (int)new_size.y);
|
||||
// m_canvas->clear();
|
||||
}
|
||||
}
|
||||
|
||||
kEventResult NodeCanvas::handle_event(Event* e)
|
||||
{
|
||||
static std::vector<CanvasMode>* old_mode = nullptr;
|
||||
Node::handle_event(e);
|
||||
MouseEvent* me = static_cast<MouseEvent*>(e);
|
||||
KeyEvent* ke = static_cast<KeyEvent*>(e);
|
||||
GestureEvent* ge = static_cast<GestureEvent*>(e);
|
||||
auto loc = (me->m_pos - m_pos) * root()->m_zoom;
|
||||
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseScroll:
|
||||
case kEventType::MouseDownL:
|
||||
case kEventType::MouseUpL:
|
||||
case kEventType::MouseDownR:
|
||||
case kEventType::MouseUpR:
|
||||
case kEventType::MouseMove:
|
||||
case kEventType::MouseCancel:
|
||||
m_canvas->m_cur_pos = loc;
|
||||
if (!(m_canvas->m_touch_lock && me->m_source == kEventSource::Touch))
|
||||
for (auto& mode : *m_canvas->m_mode)
|
||||
mode->on_MouseEvent(me, loc);
|
||||
break;
|
||||
case kEventType::KeyDown:
|
||||
if (ke->m_key == kKey::KeyE)
|
||||
ui::Canvas::set_mode(ui::Canvas::kCanvasMode::Erase);
|
||||
if (ke->m_key == kKey::AndroidBack)
|
||||
if (!ActionManager::empty())
|
||||
ActionManager::undo();
|
||||
|
||||
break;
|
||||
case kEventType::KeyUp:
|
||||
if (ke->m_key == kKey::KeyE)
|
||||
ui::Canvas::set_mode(ui::Canvas::kCanvasMode::Draw);
|
||||
if (ke->m_key == kKey::KeyTab)
|
||||
App::I.toggle_ui();
|
||||
if (ke->m_key == kKey::KeyZ && App::I.keys[(int)kKey::KeyCtrl])
|
||||
App::I.keys[(int)kKey::KeyShift] ? ActionManager::redo() : ActionManager::undo();
|
||||
if (ke->m_key == kKey::KeyS && App::I.keys[(int)kKey::KeyCtrl] && !App::I.keys[(int)kKey::KeyShift])
|
||||
{
|
||||
if (ui::Canvas::I->m_newdoc)
|
||||
{
|
||||
App::I.dialog_save();
|
||||
}
|
||||
else if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
ui::Canvas::I->project_save();
|
||||
}
|
||||
}
|
||||
if (ke->m_key == kKey::KeyS && App::I.keys[(int)kKey::KeyCtrl] && App::I.keys[(int)kKey::KeyShift])
|
||||
{
|
||||
if (ui::Canvas::I->m_newdoc)
|
||||
{
|
||||
App::I.dialog_save();
|
||||
}
|
||||
else if (ui::Canvas::I->m_unsaved)
|
||||
{
|
||||
App::I.dialog_save_ver();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kEventType::GestureStart:
|
||||
mouse_capture();
|
||||
for (auto& mode : *m_canvas->m_mode)
|
||||
mode->on_GestureEvent(ge);
|
||||
break;
|
||||
case kEventType::GestureMove:
|
||||
for (auto& mode : *m_canvas->m_mode)
|
||||
mode->on_GestureEvent(ge);
|
||||
break;
|
||||
case kEventType::GestureEnd:
|
||||
mouse_release();
|
||||
for (auto& mode : *m_canvas->m_mode)
|
||||
mode->on_GestureEvent(ge);
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeCanvas::reset_camera()
|
||||
{
|
||||
m_canvas->m_cam_rot = {0, 0};
|
||||
m_canvas->m_cam_pos = {0, 0, 0};
|
||||
m_canvas->m_cam_fov = 85;
|
||||
m_canvas->m_pan = {0, 0};
|
||||
}
|
||||
25
src/node_canvas.h
Normal file
25
src/node_canvas.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "canvas.h"
|
||||
|
||||
class NodeCanvas : public Node
|
||||
{
|
||||
public:
|
||||
std::string data_path;
|
||||
std::unique_ptr<ui::Canvas> m_canvas;
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_linear;
|
||||
Sampler m_sampler_stencil;
|
||||
ui::Plane m_face_plane;
|
||||
ui::LineSegment m_line;
|
||||
ui::Plane m_grid;
|
||||
int m_grid_divs = 30;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
virtual void restore_context() override;
|
||||
virtual void clear_context() override;
|
||||
virtual void draw() override;
|
||||
virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
void reset_camera();
|
||||
};
|
||||
75
src/node_checkbox.cpp
Normal file
75
src/node_checkbox.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_checkbox.h"
|
||||
|
||||
Node* NodeCheckBox::clone_instantiate() const
|
||||
{
|
||||
return new NodeCheckBox();
|
||||
}
|
||||
|
||||
void NodeCheckBox::clone_children(Node* dest) const
|
||||
{
|
||||
Node::clone_children(dest);
|
||||
NodeCheckBox* n = static_cast<NodeCheckBox*>(dest);
|
||||
n->m_outer = (NodeBorder*)n->m_children[0].get();
|
||||
n->m_inner = (NodeBorder*)n->m_outer->m_children[0].get();
|
||||
n->m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeCheckBox::init()
|
||||
{
|
||||
m_outer = new NodeBorder();
|
||||
m_inner = new NodeBorder();
|
||||
add_child(m_outer);
|
||||
m_outer->add_child(m_inner);
|
||||
m_outer->init();
|
||||
m_outer->m_color = { .3, .3, .3, 1 };
|
||||
m_outer->SetAlign(YGAlignCenter);
|
||||
m_outer->SetJustify(YGJustifyCenter);
|
||||
m_outer->SetPadding(5, 5, 5, 5);
|
||||
m_outer->SetWidthP(100);
|
||||
m_outer->SetHeightP(100);
|
||||
m_outer->m_mouse_ignore = false;
|
||||
m_inner->init();
|
||||
m_inner->SetWidthP(100);
|
||||
m_inner->SetHeightP(100);
|
||||
m_inner->m_border_color = glm::vec4(.8, .8, .8, 1);
|
||||
m_inner->m_thinkness = 1;
|
||||
m_inner->m_color = glm::vec4(.8, .8, .8, 1);
|
||||
m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeCheckBox::create()
|
||||
{
|
||||
m_outer->create();
|
||||
m_inner->create();
|
||||
}
|
||||
|
||||
kEventResult NodeCheckBox::handle_event(Event* e)
|
||||
{
|
||||
Node::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
checked = !checked;
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, checked);
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeCheckBox::draw()
|
||||
{
|
||||
m_inner->m_color = checked ? glm::vec4(.4, .4, .4, 1) : glm::vec4(.8, .8, .8, 1);
|
||||
Node::draw();
|
||||
}
|
||||
18
src/node_checkbox.h
Normal file
18
src/node_checkbox.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeCheckBox : public Node
|
||||
{
|
||||
public:
|
||||
std::function<void(Node* target, bool checked)> on_value_changed;
|
||||
NodeBorder* m_outer;
|
||||
NodeBorder* m_inner;
|
||||
bool checked = false;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_children(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void create() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
virtual void draw() override;
|
||||
};
|
||||
99
src/node_color_quad.cpp
Normal file
99
src/node_color_quad.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_color_quad.h"
|
||||
#include "shader.h"
|
||||
|
||||
Node* NodeColorQuad::clone_instantiate() const
|
||||
{
|
||||
return new NodeColorQuad();
|
||||
}
|
||||
|
||||
void NodeColorQuad::clone_finalize(Node* dest) const
|
||||
{
|
||||
auto n = (NodeColorQuad*)dest;
|
||||
n->m_picker = (NodeBorder*)n->m_children[0].get();
|
||||
}
|
||||
|
||||
void NodeColorQuad::init()
|
||||
{
|
||||
m_picker = new NodeBorder;
|
||||
m_picker->SetSize({ 20, 20 });
|
||||
m_picker->SetPositioning(YGPositionTypeAbsolute);
|
||||
m_picker->SetPosition(0, 0);
|
||||
m_picker->m_thinkness = 1;
|
||||
m_picker->m_color = glm::vec4(0);
|
||||
add_child(m_picker);
|
||||
}
|
||||
|
||||
void NodeColorQuad::set_value(float x, float y)
|
||||
{
|
||||
auto sz = m_size;
|
||||
auto pos = glm::clamp(glm::vec2(x, y) * sz, { 0, 0 }, sz);
|
||||
//m_picker->SetPosition(pos - m_picker->GetSize() * .5f);
|
||||
m_value = pos / glm::max({ 1,1 }, sz); // avoid div0
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, m_value);
|
||||
}
|
||||
|
||||
kEventResult NodeColorQuad::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
m_old_value = m_value;
|
||||
dragging = true;
|
||||
mouse_capture();
|
||||
auto sz = m_size;
|
||||
auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz);
|
||||
m_picker->SetPosition(pos - m_picker->GetSize() * .5f);
|
||||
m_value = pos / glm::max({ 1,1 }, sz); // avoid div0
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, m_value);
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
mouse_release();
|
||||
dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (dragging)
|
||||
{
|
||||
auto sz = m_size;
|
||||
auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz);
|
||||
m_picker->SetPosition(pos - m_picker->GetSize() * .5f);
|
||||
m_value = pos / glm::max({ 1,1 }, sz); // avoid div0
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, m_value);
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
mouse_release();
|
||||
dragging = false;
|
||||
m_value = m_old_value;
|
||||
set_value(m_value.x, m_value.y);
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, m_value);
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeColorQuad::draw()
|
||||
{
|
||||
m_picker->m_border_color = m_value.y > .5f ? glm::vec4(1) : glm::vec4(0, 0, 0, 1);
|
||||
|
||||
auto sz = m_size;
|
||||
auto pos = glm::clamp(m_value * sz, { 0, 0 }, sz);
|
||||
m_picker->SetPosition(pos - m_picker->GetSize() * .5f);
|
||||
|
||||
using namespace ui;
|
||||
ui::ShaderManager::use(kShader::ColorQuad);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(convert_rgb2hsv(glm::vec3(m_color)), 1));
|
||||
m_plane.draw_fill();
|
||||
}
|
||||
18
src/node_color_quad.h
Normal file
18
src/node_color_quad.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeColorQuad : public NodeBorder
|
||||
{
|
||||
NodeBorder* m_picker{nullptr};
|
||||
bool dragging = false;
|
||||
public:
|
||||
glm::vec2 m_value{0.f};
|
||||
glm::vec2 m_old_value{0.f};
|
||||
std::function<void(Node* target, glm::vec2 value)> on_value_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void set_value(float x, float y);
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
virtual void draw() override;
|
||||
};
|
||||
189
src/node_colorwheel.cpp
Normal file
189
src/node_colorwheel.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "pch.h"
|
||||
#include "node_colorwheel.h"
|
||||
#include "shader.h"
|
||||
#include "log.h"
|
||||
|
||||
Node* NodeColorWheel::clone_instantiate() const
|
||||
{
|
||||
return new NodeColorWheel;
|
||||
}
|
||||
|
||||
void NodeColorWheel::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeColorWheel* n = (NodeColorWheel*)dest;
|
||||
n->init_controls();
|
||||
n->m_mouse_ignore = false;
|
||||
}
|
||||
|
||||
void NodeColorWheel::init()
|
||||
{
|
||||
m_mouse_ignore = false;
|
||||
//init_template("color-picker");
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeColorWheel::init_controls()
|
||||
{
|
||||
}
|
||||
|
||||
void NodeColorWheel::loaded()
|
||||
{
|
||||
m_circle.create<64>(.5, .4, ui::Circle::kUVMapping::Tube);
|
||||
m_cur_hue.create<16>(.05, 0.04);
|
||||
m_cur_quad.create<16>(.04, 0.03, ui::Circle::kUVMapping::Tube);
|
||||
|
||||
float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
|
||||
m_quad.create<1>(quad_scale, quad_scale);
|
||||
|
||||
struct vertex_t { glm::vec4 pos; glm::vec2 uvs; glm::vec4 col; };
|
||||
std::vector<vertex_t> vertices;
|
||||
float l = 0.4;
|
||||
vertices.push_back({{glm::cos(4.f/3.f*glm::pi<float>())*l,glm::sin(4.f/3.f*glm::pi<float>())*l,0,1},{1,-1},{1,1,1,1}});
|
||||
vertices.push_back({{glm::cos(2.f/3.f*glm::pi<float>())*l,glm::sin(2.f/3.f*glm::pi<float>())*l,0,1},{0,0},{0,0,0,1}});
|
||||
vertices.push_back({{l,0,0,1},{1,1},{1,0,0,1}});
|
||||
|
||||
glGenBuffers(1, &buffers);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(vertex_t), vertices.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glGenVertexArrays(1, &arrays);
|
||||
glBindVertexArray(arrays);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs));
|
||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, col));
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void NodeColorWheel::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
ShaderManager::use(kShader::ColorHue);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::eulerAngleZ(glm::radians(-90.f)));
|
||||
ShaderManager::u_int(kShaderUniform::Direction, 0); // set horizontal
|
||||
m_circle.draw_fill();
|
||||
|
||||
// ShaderManager::use(kShader::ColorTri);
|
||||
// ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
// ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(m_hsv, 0.f));
|
||||
// GLenum type = GL_TRIANGLES;
|
||||
// glBindVertexArray(arrays);
|
||||
// glDrawArrays(type, 0, 3);
|
||||
// glBindVertexArray(0);
|
||||
|
||||
ShaderManager::use(kShader::Color);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::eulerAngleZ(glm::radians(-360.f * m_hsv.x)) * glm::translate(glm::vec3(.45f,0.f,0.f)));
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, {convert_hsv2rgb({glm::fract(m_hsv.x + 0.5f), 1.f, 1.f}), 1.f});
|
||||
m_cur_hue.draw_stroke();
|
||||
|
||||
ShaderManager::use(kShader::ColorQuad);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(m_hsv, 0.f));
|
||||
m_quad.draw_fill();
|
||||
|
||||
float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
|
||||
glm::vec3 pos = glm::vec3(glm::vec2(m_hsv.y, 1.f - m_hsv.z) - 0.5f, 0);
|
||||
ShaderManager::use(kShader::Color);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp * glm::translate(pos * quad_scale));
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, {convert_hsv2rgb({glm::fract(m_hsv.x + 0.5f), 1.f, 1.f}), 1.f});
|
||||
m_cur_quad.draw_fill();
|
||||
}
|
||||
|
||||
glm::vec4 NodeColorWheel::get_quad_rect() const
|
||||
{
|
||||
float quad_scale = glm::sin(glm::radians(45.f)) * 0.8f;
|
||||
glm::vec2 size = m_size * quad_scale;
|
||||
glm::vec2 pos = (m_size - size) * 0.5f;
|
||||
return glm::vec4(pos, size);
|
||||
}
|
||||
|
||||
bool NodeColorWheel::inside_quad(glm::vec2 pos, glm::vec2& out_coord) const
|
||||
{
|
||||
auto r = get_quad_rect();
|
||||
out_coord = (pos - xy(r)) / zw(r);
|
||||
return point_in_rect(pos, r);
|
||||
}
|
||||
|
||||
void NodeColorWheel::handle_color_change()
|
||||
{
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, m_hsv);
|
||||
}
|
||||
|
||||
kEventResult NodeColorWheel::handle_event(Event* e)
|
||||
{
|
||||
Node::handle_event(e);
|
||||
auto* me = static_cast<MouseEvent*>(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
{
|
||||
m_old_value = m_hsv;
|
||||
dragging = true;
|
||||
mouse_capture();
|
||||
|
||||
auto pos = (me->m_pos - m_pos - GetSize() * 0.5f) / GetSize();
|
||||
float l = glm::length(pos);
|
||||
|
||||
glm::vec2 quad_pos(0);
|
||||
if (inside_quad(me->m_pos - m_pos, quad_pos))
|
||||
{
|
||||
mode = 2;
|
||||
m_hsv.y = glm::clamp(quad_pos.x, 0.f, 1.f);
|
||||
m_hsv.z = 1.f - glm::clamp(quad_pos.y, 0.f, 1.f);
|
||||
handle_color_change();
|
||||
}
|
||||
else if (l >= 0.4f && l <= 0.5f)
|
||||
{
|
||||
mode = 1;
|
||||
auto pos = glm::normalize(me->m_pos - m_pos - GetSize() * 0.5f);
|
||||
m_hsv.x = (glm::atan(pos.y, -pos.x) + glm::pi<float>()) / glm::two_pi<float>();
|
||||
handle_color_change();
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
mouse_release();
|
||||
dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (dragging)
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
auto pos = glm::normalize(me->m_pos - m_pos - GetSize() * 0.5f);
|
||||
m_hsv.x = (glm::atan(pos.y, -pos.x) + glm::pi<float>()) / glm::two_pi<float>();
|
||||
handle_color_change();
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
glm::vec2 quad_pos(0);
|
||||
inside_quad(me->m_pos - m_pos, quad_pos);
|
||||
m_hsv.y = glm::clamp(quad_pos.x, 0.f, 1.f);
|
||||
m_hsv.z = 1.f - glm::clamp(quad_pos.y, 0.f, 1.f);
|
||||
handle_color_change();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
mouse_release();
|
||||
dragging = false;
|
||||
m_hsv = m_old_value;
|
||||
handle_color_change();
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
35
src/node_colorwheel.h
Normal file
35
src/node_colorwheel.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "util.h"
|
||||
#include "node.h"
|
||||
#include "shape.h"
|
||||
#include "node_slider.h"
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeColorWheel : public Node
|
||||
{
|
||||
public:
|
||||
ui::Circle m_circle;
|
||||
ui::Plane m_cur_hue;
|
||||
ui::Circle m_cur_quad;
|
||||
ui::Plane m_quad;
|
||||
NodeBorder* m_color_cur;
|
||||
glm::vec3 m_hsv;
|
||||
GLuint m_tri_vbo;
|
||||
GLuint m_tri_vao;
|
||||
GLuint buffers;
|
||||
GLuint arrays;
|
||||
std::function<void(Node* target, glm::vec3 hsv)> on_value_changed;
|
||||
int mode; // 1:hue 2:quad
|
||||
bool dragging = false;
|
||||
glm::vec3 m_old_value{0.f};
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void loaded() override;
|
||||
virtual void draw() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
void init_controls();
|
||||
glm::vec4 get_quad_rect() const;
|
||||
bool inside_quad(glm::vec2 pos, glm::vec2& out_coord) const;
|
||||
void handle_color_change();
|
||||
};
|
||||
92
src/node_combobox.cpp
Normal file
92
src/node_combobox.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_combobox.h"
|
||||
#include "node_popup_menu.h"
|
||||
|
||||
Node* NodeComboBox::clone_instantiate() const
|
||||
{
|
||||
return new NodeComboBox;
|
||||
}
|
||||
|
||||
void NodeComboBox::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeButton::clone_copy(dest);
|
||||
NodeComboBox* n = static_cast<NodeComboBox*>(dest);
|
||||
n->m_data = m_data;
|
||||
n->m_current_index = m_current_index;
|
||||
}
|
||||
|
||||
void NodeComboBox::loaded()
|
||||
{
|
||||
NodeButton::loaded();
|
||||
on_click = [this](Node* target) {
|
||||
NodePopupMenu* popup = new NodePopupMenu;
|
||||
popup->init();
|
||||
popup->create();
|
||||
popup->loaded();
|
||||
root()->add_child(popup);
|
||||
m_items.clear();
|
||||
for (int i = 0; i < m_data.size(); i++)
|
||||
{
|
||||
if (m_data[i] == "-")
|
||||
{
|
||||
auto n = popup->add_child<NodeBorder>();
|
||||
n->SetHeight(5.f);
|
||||
n->SetWidthP(100.f);
|
||||
n->m_color = {0, 0, 0, 1};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto btn = popup->add_child<NodeButton>();
|
||||
btn->m_text->set_text(m_data[i].c_str());
|
||||
btn->m_border->SetWidthP(100.f);
|
||||
btn->m_border->SetHeight(30.f);
|
||||
int index = (int)m_items.size();
|
||||
m_items.push_back(m_data[i]);
|
||||
btn->on_click = [this,popup,btn,index](Node* target) {
|
||||
m_current_index = index;
|
||||
m_selected_child_index = popup->get_child_index(target);
|
||||
m_text->set_text(m_items[index].c_str());
|
||||
popup->mouse_release();
|
||||
popup->destroy();
|
||||
if (on_select)
|
||||
on_select(btn, index);
|
||||
};
|
||||
}
|
||||
}
|
||||
float offset = 0;
|
||||
for (int i = 0; i <= m_selected_child_index; i++)
|
||||
offset += (m_data[i] == "-") ? 5.f : 30.f;
|
||||
float height = m_items.size() * 30.f + (m_data.size() - m_items.size()) * 5.f; // add items and separators
|
||||
glm::vec2 pos = m_pos + glm::vec2(0, m_size.y - offset);
|
||||
auto screen = root()->m_size;
|
||||
if ((pos.y + height) > screen.y) pos.y = screen.y - height;
|
||||
if (pos.y < 0) pos.y = 0;
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(pos.x, pos.y);
|
||||
popup->SetSize(m_size.x, YGUndefined);
|
||||
popup->SetFlexGrow(1.f);
|
||||
popup->update();
|
||||
root()->update();
|
||||
popup->mouse_capture();
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
};
|
||||
}
|
||||
|
||||
void NodeComboBox::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
NodeButton::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::ComboList:
|
||||
{
|
||||
m_data = split(attr->Value(), ',');
|
||||
break;
|
||||
}
|
||||
case kAttribute::Default:
|
||||
m_current_index = attr->IntValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
16
src/node_combobox.h
Normal file
16
src/node_combobox.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "node_button.h"
|
||||
|
||||
class NodeComboBox : public NodeButton
|
||||
{
|
||||
public:
|
||||
std::function<void(Node* target, int index)> on_select;
|
||||
std::vector<std::string> m_data;
|
||||
std::vector<std::string> m_items;
|
||||
int m_current_index = 0;
|
||||
int m_selected_child_index = 0;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void loaded() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
};
|
||||
166
src/node_dialog_browse.cpp
Normal file
166
src/node_dialog_browse.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_dialog_browse.h"
|
||||
#include "canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "asset.h"
|
||||
#include "node_message_box.h"
|
||||
#include "app.h"
|
||||
|
||||
Node* NodeDialogBrowse::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogBrowse();
|
||||
}
|
||||
|
||||
void NodeDialogBrowse::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogBrowse* n = static_cast<NodeDialogBrowse*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogBrowse::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-browse"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;;
|
||||
m_thinkness = tpl->m_thinkness;;
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogBrowse::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
btn_delete = find<NodeButton>("btn-delete");
|
||||
btn_delete->on_click = [this](Node*) {
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = m_manager;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Delete Project");
|
||||
msgbox->m_message->set_text(("Are you sure you want to delete " + current->m_file_name + "?").c_str());
|
||||
msgbox->btn_ok->on_click = [this,msgbox](Node*){
|
||||
auto path = current->m_path;
|
||||
int idx = container->get_child_index(current);
|
||||
container->remove_child(current);
|
||||
if (!container->m_children.empty())
|
||||
{
|
||||
int newidx = std::min<int>(idx, (int)container->m_children.size() - 1);
|
||||
auto next = (NodeDialogBrowseItem*)container->get_child_at(newidx);
|
||||
current = nullptr;
|
||||
next->on_selected(next);
|
||||
next->m_selected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = nullptr;
|
||||
auto image_tex = find<NodeImageTexture>("thumb-tex");
|
||||
image_tex->tex.destroy();
|
||||
}
|
||||
Asset::delete_file(path);
|
||||
msgbox->destroy();
|
||||
};
|
||||
root()->add_child(msgbox);
|
||||
root()->update();
|
||||
};
|
||||
container = find<Node>("files-list");
|
||||
auto names = Asset::list_files(data_path, false, ".*\\.pano");
|
||||
for (const auto& n : names)
|
||||
{
|
||||
auto node = new NodeDialogBrowseItem;
|
||||
node->m_manager = m_manager;
|
||||
node->init();
|
||||
node->m_text->set_text(n.c_str());
|
||||
node->m_path = data_path + "/" + n;
|
||||
node->m_file_name = n;
|
||||
node->on_selected = [&](NodeDialogBrowseItem* target) {
|
||||
if (target == current)
|
||||
return;
|
||||
selected_path = target->m_path;
|
||||
selected_file = target->m_file_name;
|
||||
selected_name = selected_file.substr(0, selected_file.length() - 5);
|
||||
if (current)
|
||||
current->m_selected = false;
|
||||
current = target;
|
||||
};
|
||||
|
||||
// load thumb
|
||||
ui::Image thumb = ui::Canvas::I->thumbnail_read(node->m_path);
|
||||
auto image_tex = node->find<NodeImageTexture>("thumb-tex");
|
||||
image_tex->tex.destroy();
|
||||
image_tex->tex.create(thumb);
|
||||
|
||||
container->add_child(node);
|
||||
}
|
||||
// if (auto* first = (NodeDialogBrowseItem*)container->get_child_at(0))
|
||||
// {
|
||||
// first->on_selected(first);
|
||||
// first->m_selected = true;
|
||||
// }
|
||||
}
|
||||
|
||||
void NodeDialogBrowse::loaded()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Node* NodeDialogBrowseItem::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogBrowseItem;
|
||||
}
|
||||
void NodeDialogBrowseItem::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogBrowseItem* n = static_cast<NodeDialogBrowseItem*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
void NodeDialogBrowseItem::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-browse-item"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
init_controls();
|
||||
}
|
||||
void NodeDialogBrowseItem::init_controls()
|
||||
{
|
||||
m_text = find<NodeText>("title");
|
||||
}
|
||||
void NodeDialogBrowseItem::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
void NodeDialogBrowseItem::draw()
|
||||
{
|
||||
auto c = m_selected ? m_color_selected : m_color_normal;
|
||||
m_thinkness = m_selected ? 1.f : 0.f;
|
||||
m_color = m_mouse_inside ? m_color_hover : c;
|
||||
NodeBorder::draw();
|
||||
}
|
||||
kEventResult NodeDialogBrowseItem::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_selected = true;
|
||||
if (on_selected)
|
||||
on_selected(this);
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
46
src/node_dialog_browse.h
Normal file
46
src/node_dialog_browse.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_button.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "node_text.h"
|
||||
#include "node_text_input.h"
|
||||
|
||||
class NodeDialogBrowseItem : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeText* m_text;
|
||||
NodeImageTexture* m_thumb;
|
||||
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
||||
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
||||
glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
|
||||
bool m_selected = false;
|
||||
std::string m_path;
|
||||
std::string m_file_name;
|
||||
std::function<void(NodeDialogBrowseItem* target)> on_selected;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
virtual void draw() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
|
||||
class NodeDialogBrowse : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeButton* btn_delete;
|
||||
NodeDialogBrowseItem* current = nullptr;
|
||||
Node* container;
|
||||
std::string selected_path;
|
||||
std::string selected_file;
|
||||
std::string selected_name;
|
||||
std::string data_path;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
};
|
||||
211
src/node_dialog_cloud.cpp
Normal file
211
src/node_dialog_cloud.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_dialog_cloud.h"
|
||||
#include "canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "asset.h"
|
||||
#include "node_message_box.h"
|
||||
#include "app.h"
|
||||
#include "image.h"
|
||||
|
||||
Node* NodeDialogCloud::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogCloud();
|
||||
}
|
||||
|
||||
void NodeDialogCloud::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogCloud* n = static_cast<NodeDialogCloud*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogCloud::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-cloud"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;;
|
||||
m_thinkness = tpl->m_thinkness;;
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogCloud::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
container = find<Node>("files-list");
|
||||
std::thread(&NodeDialogCloud::load_thumbs_thread, this).detach();
|
||||
}
|
||||
|
||||
void NodeDialogCloud::loaded()
|
||||
{
|
||||
}
|
||||
|
||||
void NodeDialogCloud::removed(Node* parent)
|
||||
{
|
||||
closed = true;
|
||||
}
|
||||
|
||||
void NodeDialogCloud::load_thumbs_thread()
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
std::string res;
|
||||
if (curl)
|
||||
{
|
||||
async_start();
|
||||
auto* align = container->add_child<Node>();
|
||||
align->SetWidthP(100.f);
|
||||
align->SetHeightP(100.f);
|
||||
align->SetAlign(YGAlignCenter);
|
||||
align->SetJustify(YGJustifyCenter);
|
||||
auto* text = align->add_child<NodeText>();
|
||||
text->set_font(kFont::Arial_30);
|
||||
text->set_text("Connecting to the server...");
|
||||
async_update();
|
||||
async_end();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://omigamedev.com/panopainter/cloud/cloud-list.php");
|
||||
auto err = curl_easy_perform(curl);
|
||||
|
||||
if (err != CURLE_OK)
|
||||
{
|
||||
async_start();
|
||||
text->set_text("Could not connect to the server");
|
||||
async_update();
|
||||
async_end();
|
||||
return;
|
||||
}
|
||||
|
||||
async_start();
|
||||
align->destroy();
|
||||
async_end();
|
||||
|
||||
LOG("CLOUD LIST: %s", res.c_str());
|
||||
|
||||
auto names = split(res, ',');
|
||||
std::vector<NodeDialogCloudItem*> nodes;
|
||||
|
||||
// create slots with name
|
||||
App::I.async_start();
|
||||
for (const auto& n : names)
|
||||
{
|
||||
auto node = new NodeDialogCloudItem;
|
||||
node->m_manager = m_manager;
|
||||
node->init();
|
||||
node->m_text->set_text(n.c_str());
|
||||
node->m_path = data_path + "/" + n;
|
||||
node->m_file_name = n;
|
||||
container->add_child(node);
|
||||
node->on_selected = [&](NodeDialogCloudItem* target) {
|
||||
if (target == current)
|
||||
return;
|
||||
selected_path = target->m_path;
|
||||
selected_file = target->m_file_name;
|
||||
selected_name = selected_file.substr(0, selected_file.length() - 5);
|
||||
if (current)
|
||||
current->m_selected = false;
|
||||
current = target;
|
||||
};
|
||||
nodes.push_back(node);
|
||||
}
|
||||
App::I.async_update();
|
||||
App::I.async_end();
|
||||
|
||||
// load the icons
|
||||
for (int i = 0; i < names.size(); i++)
|
||||
{
|
||||
const auto& n = names[i];
|
||||
auto* node = nodes[i];
|
||||
if (closed)
|
||||
break;
|
||||
|
||||
res.clear();
|
||||
std::string url = "http://omigamedev.com/panopainter/cloud/cloud-info.php?file=" + n;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
LOG("%s", url.c_str());
|
||||
auto err = curl_easy_perform(curl);
|
||||
if (err != CURLE_OK)
|
||||
break; // TODO: handle this error with a message or something
|
||||
auto info = split(res, ',', 3);
|
||||
int width = atoi(info[0].c_str());
|
||||
int height = atoi(info[1].c_str());
|
||||
int comp = atoi(info[2].c_str());
|
||||
assert(comp == 4);
|
||||
std::string rgb;
|
||||
rgb.resize(Base64::DecodedLength(info[3]));
|
||||
Base64::Decode(info[3], &rgb);
|
||||
ui::Image thumb;
|
||||
thumb.create(width, height);
|
||||
thumb.copy_from((uint8_t*)rgb.data());
|
||||
|
||||
App::I.async_start();
|
||||
auto image_tex = node->find<NodeImageTexture>("thumb-tex");
|
||||
image_tex->tex.destroy();
|
||||
image_tex->tex.create(thumb);
|
||||
App::I.async_update();
|
||||
App::I.async_end();
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Node* NodeDialogCloudItem::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogCloudItem;
|
||||
}
|
||||
void NodeDialogCloudItem::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogCloudItem* n = static_cast<NodeDialogCloudItem*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
void NodeDialogCloudItem::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-cloud-item"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
init_controls();
|
||||
}
|
||||
void NodeDialogCloudItem::init_controls()
|
||||
{
|
||||
m_text = find<NodeText>("title");
|
||||
}
|
||||
void NodeDialogCloudItem::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
void NodeDialogCloudItem::draw()
|
||||
{
|
||||
auto c = m_selected ? m_color_selected : m_color_normal;
|
||||
m_thinkness = m_selected ? 1.f : 0.f;
|
||||
m_color = m_mouse_inside ? m_color_hover : c;
|
||||
NodeBorder::draw();
|
||||
}
|
||||
kEventResult NodeDialogCloudItem::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_selected = true;
|
||||
if (on_selected)
|
||||
on_selected(this);
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
49
src/node_dialog_cloud.h
Normal file
49
src/node_dialog_cloud.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_button.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "node_text.h"
|
||||
#include "node_text_input.h"
|
||||
|
||||
class NodeDialogCloudItem : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeText* m_text;
|
||||
NodeImageTexture* m_thumb;
|
||||
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
||||
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
||||
glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
|
||||
bool m_selected = false;
|
||||
std::string m_path;
|
||||
std::string m_file_name;
|
||||
std::function<void(NodeDialogCloudItem* target)> on_selected;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
virtual void draw() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
|
||||
class NodeDialogCloud : public NodeBorder
|
||||
{
|
||||
public:
|
||||
bool closed = false;
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeButton* btn_delete;
|
||||
NodeDialogCloudItem* current = nullptr;
|
||||
Node* container;
|
||||
std::string selected_path;
|
||||
std::string selected_file;
|
||||
std::string selected_name;
|
||||
std::string data_path;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
virtual void removed(Node* parent) override;
|
||||
void load_thumbs_thread();
|
||||
};
|
||||
47
src/node_dialog_layer_rename.cpp
Normal file
47
src/node_dialog_layer_rename.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_dialog_layer_rename.h"
|
||||
#include "canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
|
||||
Node* NodeDialogLayerRename::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogLayerRename();
|
||||
}
|
||||
|
||||
void NodeDialogLayerRename::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogLayerRename* n = static_cast<NodeDialogLayerRename*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogLayerRename::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-layer-rename"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;;
|
||||
m_thinkness = tpl->m_thinkness;;
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogLayerRename::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
input = find<NodeTextInput>("txt-input");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
}
|
||||
|
||||
void NodeDialogLayerRename::loaded()
|
||||
{
|
||||
// ui::Image thumb = ui::Canvas::I->thumbnail_read(data_path);
|
||||
// auto image_tex = find<NodeImageTexture>("thumb-tex");
|
||||
// image_tex->tex.create(thumb);
|
||||
}
|
||||
|
||||
std::string NodeDialogLayerRename::get_name()
|
||||
{
|
||||
return input ? input->m_string : "";
|
||||
}
|
||||
19
src/node_dialog_layer_rename.h
Normal file
19
src/node_dialog_layer_rename.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_button.h"
|
||||
#include "node_text_input.h"
|
||||
|
||||
class NodeDialogLayerRename : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeTextInput* input;
|
||||
std::string data_path;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
std::string get_name();
|
||||
};
|
||||
239
src/node_dialog_open.cpp
Normal file
239
src/node_dialog_open.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_dialog_open.h"
|
||||
#include "canvas.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "asset.h"
|
||||
#include "node_message_box.h"
|
||||
#include "app.h"
|
||||
|
||||
Node* NodeDialogOpen::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogOpen();
|
||||
}
|
||||
|
||||
void NodeDialogOpen::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogOpen* n = static_cast<NodeDialogOpen*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogOpen::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-open"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;;
|
||||
m_thinkness = tpl->m_thinkness;;
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeDialogOpen::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
btn_delete = find<NodeButton>("btn-delete");
|
||||
btn_delete->on_click = [this](Node*) {
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
auto msgbox = new NodeMessageBox();
|
||||
msgbox->m_manager = m_manager;
|
||||
msgbox->init();
|
||||
msgbox->m_title->set_text("Delete Project");
|
||||
msgbox->m_message->set_text(("Are you sure you want to delete " + current->m_file_name + "?").c_str());
|
||||
msgbox->btn_ok->on_click = [this,msgbox](Node*){
|
||||
auto path = current->m_path;
|
||||
int idx = container->get_child_index(current);
|
||||
container->remove_child(current);
|
||||
if (!container->m_children.empty())
|
||||
{
|
||||
int newidx = std::min<int>(idx, (int)container->m_children.size() - 1);
|
||||
auto next = (NodeDialogOpenItem*)container->get_child_at(newidx);
|
||||
current = nullptr;
|
||||
next->on_selected(next);
|
||||
next->m_selected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = nullptr;
|
||||
auto image_tex = find<NodeImageTexture>("thumb-tex");
|
||||
image_tex->tex.destroy();
|
||||
}
|
||||
Asset::delete_file(path);
|
||||
msgbox->destroy();
|
||||
};
|
||||
root()->add_child(msgbox);
|
||||
root()->update();
|
||||
};
|
||||
container = find<Node>("files-list");
|
||||
auto names = Asset::list_files(data_path, false, ".*\\.pano");
|
||||
for (const auto& n : names)
|
||||
{
|
||||
auto node = new NodeDialogOpenItem;
|
||||
node->m_manager = m_manager;
|
||||
node->init();
|
||||
node->m_text->set_text(n.c_str());
|
||||
node->m_path = data_path + "/" + n;
|
||||
node->m_file_name = n;
|
||||
node->on_selected = [&](NodeDialogOpenItem* target) {
|
||||
if (target == current)
|
||||
return;
|
||||
ui::Image thumb = ui::Canvas::I->thumbnail_read(target->m_path);
|
||||
auto image_tex = find<NodeImageTexture>("thumb-tex");
|
||||
image_tex->tex.destroy();
|
||||
image_tex->tex.create(thumb);
|
||||
selected_path = target->m_path;
|
||||
selected_file = target->m_file_name;
|
||||
selected_name = selected_file.substr(0, selected_file.length() - 5);
|
||||
if (current)
|
||||
current->m_selected = false;
|
||||
current = target;
|
||||
};
|
||||
container->add_child(node);
|
||||
}
|
||||
container->update();
|
||||
if (auto* first = (NodeDialogOpenItem*)container->get_child_at(0))
|
||||
{
|
||||
first->on_selected(first);
|
||||
first->m_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDialogOpen::loaded()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Node* NodeDialogOpenItem::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogOpenItem;
|
||||
}
|
||||
void NodeDialogOpenItem::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogOpenItem* n = static_cast<NodeDialogOpenItem*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
void NodeDialogOpenItem::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-open-item"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
init_controls();
|
||||
}
|
||||
void NodeDialogOpenItem::init_controls()
|
||||
{
|
||||
m_text = find<NodeText>("title");
|
||||
}
|
||||
void NodeDialogOpenItem::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
void NodeDialogOpenItem::draw()
|
||||
{
|
||||
auto c = m_selected ? m_color_selected : m_color_normal;
|
||||
m_thinkness = m_selected ? 1.f : 0.f;
|
||||
m_color = m_mouse_inside ? m_color_hover : c;
|
||||
NodeBorder::draw();
|
||||
}
|
||||
kEventResult NodeDialogOpenItem::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_selected = true;
|
||||
if (on_selected)
|
||||
on_selected(this);
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Node* NodeDialogSave::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogSave;
|
||||
}
|
||||
void NodeDialogSave::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogSave* n = static_cast<NodeDialogSave*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
void NodeDialogSave::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-save"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
init_controls();
|
||||
}
|
||||
void NodeDialogSave::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
input = find<NodeTextInput>("txt-input");
|
||||
input->on_return = [&](NodeTextInput* target){
|
||||
if (btn_ok->on_click)
|
||||
btn_ok->on_click(btn_ok);
|
||||
};
|
||||
}
|
||||
void NodeDialogSave::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Node* NodeDialogNewDoc::clone_instantiate() const
|
||||
{
|
||||
return new NodeDialogNewDoc;
|
||||
}
|
||||
void NodeDialogNewDoc::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeDialogNewDoc* n = static_cast<NodeDialogNewDoc*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
void NodeDialogNewDoc::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("dialog-newdoc"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
init_controls();
|
||||
}
|
||||
void NodeDialogNewDoc::init_controls()
|
||||
{
|
||||
btn_ok = find<NodeButton>("btn-ok");
|
||||
m_resolution = find<NodeComboBox>("resolution");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [this](Node*) {
|
||||
destroy();
|
||||
};
|
||||
input = find<NodeTextInput>("txt-input");
|
||||
input->on_return = [&](NodeTextInput* target){
|
||||
if (btn_ok->on_click)
|
||||
btn_ok->on_click(btn_ok);
|
||||
};
|
||||
}
|
||||
void NodeDialogNewDoc::loaded()
|
||||
{
|
||||
|
||||
}
|
||||
75
src/node_dialog_open.h
Normal file
75
src/node_dialog_open.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_button.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "node_text.h"
|
||||
#include "node_text_input.h"
|
||||
#include "node_combobox.h"
|
||||
|
||||
class NodeDialogOpenItem : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeText* m_text;
|
||||
NodeImageTexture* m_thumb;
|
||||
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
||||
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
||||
glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
|
||||
bool m_selected = false;
|
||||
std::string m_path;
|
||||
std::string m_file_name;
|
||||
std::function<void(NodeDialogOpenItem* target)> on_selected;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
virtual void draw() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
|
||||
class NodeDialogOpen : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeButton* btn_delete;
|
||||
NodeDialogOpenItem* current = nullptr;
|
||||
Node* container;
|
||||
std::string selected_path;
|
||||
std::string selected_file;
|
||||
std::string selected_name;
|
||||
std::string data_path;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
};
|
||||
|
||||
class NodeDialogSave : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeTextInput* input;
|
||||
std::string selected_path;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
};
|
||||
|
||||
class NodeDialogNewDoc : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeButton* btn_cancel;
|
||||
NodeButton* btn_ok;
|
||||
NodeTextInput* input;
|
||||
NodeComboBox* m_resolution;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
virtual void loaded() override;
|
||||
};
|
||||
93
src/node_dialog_picker.cpp
Normal file
93
src/node_dialog_picker.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "canvas.h"
|
||||
#include "node_dialog_picker.h"
|
||||
|
||||
Node* NodeColorPicker::clone_instantiate() const
|
||||
{
|
||||
return new NodeColorPicker;
|
||||
}
|
||||
|
||||
void NodeColorPicker::clone_finalize(Node* dest) const
|
||||
{
|
||||
auto n = static_cast<NodeColorPicker*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeColorPicker::init()
|
||||
{
|
||||
auto n = (NodeColorPicker*)init_template("color-picker");
|
||||
n->clone_copy(this);
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeColorPicker::draw()
|
||||
{
|
||||
NodeBorder::draw();
|
||||
// glm::vec3 rgb = glm::vec3(ui::Canvas::I->m_current_brush.m_tip_color);
|
||||
// glm::vec3 hsv = convert_rgb2hsv(rgb);
|
||||
// m_slider_h->m_value.x = hsv.x;
|
||||
// m_slider_s->m_value.x = hsv.y;
|
||||
// m_slider_v->m_value.x = hsv.z;
|
||||
// m_slider_r->m_value.x = rgb.x;
|
||||
// m_slider_g->m_value.x = rgb.y;
|
||||
// m_slider_b->m_value.x = rgb.z;
|
||||
// m_wheel->m_hsv = hsv;
|
||||
}
|
||||
|
||||
void NodeColorPicker::handle_value_changed()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NodeColorPicker::init_controls()
|
||||
{
|
||||
m_slider_h = find<NodeSliderH>("hsv-h");
|
||||
m_slider_s = find<NodeSliderH>("hsv-s");
|
||||
m_slider_v = find<NodeSliderH>("hsv-v");
|
||||
m_slider_r = find<NodeSliderH>("rgb-r");
|
||||
m_slider_g = find<NodeSliderH>("rgb-g");
|
||||
m_slider_b = find<NodeSliderH>("rgb-b");
|
||||
m_wheel = find<NodeColorWheel>("wheel");
|
||||
m_color_cur = find<NodeBorder>("color-cur");
|
||||
m_color_old = find<NodeBorder>("color-old");
|
||||
m_color_old1 = find<NodeBorder>("color-old1");
|
||||
m_color_old2 = find<NodeBorder>("color-old2");
|
||||
m_button_select = find<NodeButton>("btn-select");
|
||||
|
||||
m_button_select->on_click = [this](Node*)
|
||||
{
|
||||
m_color_old2->m_color = m_color_old1->m_color;
|
||||
m_color_old1->m_color = m_color_old->m_color;
|
||||
m_color_old->m_color = m_color_cur->m_color;
|
||||
};
|
||||
m_wheel->on_value_changed = [this](Node*, glm::vec3 hsv)
|
||||
{
|
||||
m_slider_h->m_value.x = hsv.x;
|
||||
m_slider_s->m_value.x = hsv.y;
|
||||
m_slider_v->m_value.x = hsv.z;
|
||||
glm::vec3 rgb = convert_hsv2rgb(hsv);
|
||||
m_slider_r->m_value.x = rgb.x;
|
||||
m_slider_g->m_value.x = rgb.y;
|
||||
m_slider_b->m_value.x = rgb.z;
|
||||
m_color_cur->m_color = {rgb,1};
|
||||
};
|
||||
auto hsv_setter = [this](Node* target, float v)
|
||||
{
|
||||
m_wheel->m_hsv = get_hsv();
|
||||
glm::vec3 rgb = convert_hsv2rgb(get_hsv());
|
||||
m_color_cur->m_color = {rgb,1};
|
||||
};
|
||||
m_slider_h->on_value_changed = hsv_setter;
|
||||
m_slider_s->on_value_changed = hsv_setter;
|
||||
m_slider_v->on_value_changed = hsv_setter;
|
||||
}
|
||||
|
||||
glm::vec3 NodeColorPicker::get_hsv() const
|
||||
{
|
||||
float h = m_slider_h->get_value();
|
||||
float s = m_slider_s->get_value();
|
||||
float v = m_slider_v->get_value();
|
||||
return glm::vec3(h, s, v);
|
||||
}
|
||||
33
src/node_dialog_picker.h
Normal file
33
src/node_dialog_picker.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "node.h"
|
||||
#include "node_border.h"
|
||||
#include "node_slider.h"
|
||||
#include "node_colorwheel.h"
|
||||
#include "node_button.h"
|
||||
|
||||
class NodeColorPicker : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeSliderH* m_slider_h;
|
||||
NodeSliderH* m_slider_s;
|
||||
NodeSliderH* m_slider_v;
|
||||
NodeSliderH* m_slider_r;
|
||||
NodeSliderH* m_slider_g;
|
||||
NodeSliderH* m_slider_b;
|
||||
NodeColorWheel* m_wheel;
|
||||
NodeBorder* m_color_cur;
|
||||
NodeBorder* m_color_old;
|
||||
NodeBorder* m_color_old1;
|
||||
NodeBorder* m_color_old2;
|
||||
NodeButton* m_button_select;
|
||||
glm::vec3 m_rgb;
|
||||
glm::vec3 m_hsv;
|
||||
|
||||
virtual Node* clone_instantiate() const override;
|
||||
//virtual void clone_copy(Node* dest) const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void draw() override;
|
||||
void init_controls();
|
||||
glm::vec3 get_hsv() const;
|
||||
void handle_value_changed();
|
||||
};
|
||||
68
src/node_icon.cpp
Normal file
68
src/node_icon.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_icon.h"
|
||||
#include "asset.h"
|
||||
#include "texture.h"
|
||||
|
||||
std::map<std::string, glm::vec4> NodeIcon::m_icons;
|
||||
|
||||
void NodeIcon::static_init()
|
||||
{
|
||||
// spritesheet maker: https://draeton.github.io/stitches/
|
||||
// icons: http://www.famfamfam.com/lab/icons/silk/
|
||||
// regex css -> spritesheet.txt: \.([^{]+) {\s+width: (\d+)px;\s+height: (\d+)px;\s+.*: -(\d+)px -(\d+)px;\s+}\s+
|
||||
// to: "\1",\2,\3,\4,\5\n
|
||||
Asset file;
|
||||
if (!(file.open("data/spritesheet.txt") && file.read_all()))
|
||||
return;
|
||||
char* data = (char*)file.m_data;
|
||||
int size = file.m_len;
|
||||
static char name[256];
|
||||
int x, y, w, h;
|
||||
char* s = strtok(data, "\n");
|
||||
auto i = strlen(s) + 1;
|
||||
while (i < size && sscanf(s, "%s %d %d %d %d", name, &w, &h, &x, &y) == 5)
|
||||
{
|
||||
m_icons[name] = glm::vec4(x, y, x + w, y + h);
|
||||
s = strtok(nullptr, "\n");
|
||||
i += strlen(s) + 1;
|
||||
}
|
||||
file.close();
|
||||
TextureManager::load("data/spritesheet.png");
|
||||
}
|
||||
|
||||
Node* NodeIcon::clone_instantiate() const
|
||||
{
|
||||
return new NodeIcon();
|
||||
}
|
||||
|
||||
void NodeIcon::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeImage::clone_copy(dest);
|
||||
NodeIcon* n = static_cast<NodeIcon*>(dest);
|
||||
n->m_icon_name = m_icon_name;
|
||||
}
|
||||
|
||||
void NodeIcon::create()
|
||||
{
|
||||
m_region = m_icons[m_icon_name];
|
||||
m_path = "data/spritesheet.png";
|
||||
m_tex_id = const_hash(m_path.c_str());
|
||||
m_use_atlas = true;
|
||||
NodeImage::create();
|
||||
auto tex_sz = TextureManager::get(m_tex_id).size();
|
||||
YGNodeStyleSetAspectRatio(y_node, tex_sz.x / tex_sz.y);
|
||||
}
|
||||
|
||||
void NodeIcon::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
NodeImage::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Icon:
|
||||
m_icon_name = attr->Value();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
14
src/node_icon.h
Normal file
14
src/node_icon.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "node_image.h"
|
||||
|
||||
class NodeIcon : public NodeImage
|
||||
{
|
||||
static std::map<std::string, glm::vec4> m_icons;
|
||||
std::string m_icon_name;
|
||||
public:
|
||||
static void static_init();
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void create() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
};
|
||||
103
src/node_image.cpp
Normal file
103
src/node_image.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_image.h"
|
||||
#include "shader.h"
|
||||
|
||||
ui::Plane NodeImage::m_plane;
|
||||
Sampler NodeImage::m_sampler;
|
||||
Sampler NodeImage::m_sampler_mips;
|
||||
|
||||
void NodeImage::static_init()
|
||||
{
|
||||
m_plane.create<1>(1, 1);
|
||||
m_sampler.create();
|
||||
m_sampler_mips.create();
|
||||
m_sampler_mips.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
|
||||
}
|
||||
|
||||
Node* NodeImage::clone_instantiate() const
|
||||
{
|
||||
return new NodeImage();
|
||||
}
|
||||
|
||||
void NodeImage::clone_copy(Node* dest) const
|
||||
{
|
||||
Node::clone_copy(dest);
|
||||
NodeImage* n = static_cast<NodeImage*>(dest);
|
||||
n->m_use_atlas = m_use_atlas;
|
||||
n->m_region = m_region;
|
||||
n->m_off = m_off;
|
||||
n->m_sz = m_sz;
|
||||
n->m_path = m_path;
|
||||
n->m_tex_id = m_tex_id;
|
||||
}
|
||||
|
||||
void NodeImage::create()
|
||||
{
|
||||
if (!m_path.empty() && TextureManager::load(m_path.c_str(), m_use_mipmaps))
|
||||
{
|
||||
//LOG("load image node %s", m_path.c_str());
|
||||
auto tex_sz = TextureManager::get(m_tex_id).size();
|
||||
m_off = xy(m_region) / tex_sz;
|
||||
m_sz = (zw(m_region) - xy(m_region)) / tex_sz;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeImage::restore_context()
|
||||
{
|
||||
Node::restore_context();
|
||||
create();
|
||||
}
|
||||
|
||||
void NodeImage::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
Node::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Mips:
|
||||
m_use_mipmaps = attr->BoolValue();
|
||||
break;
|
||||
case kAttribute::Path:
|
||||
m_path = attr->Value();
|
||||
m_tex_id = const_hash(attr->Value());
|
||||
break;
|
||||
case kAttribute::Region:
|
||||
{
|
||||
glm::vec4 v;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w);
|
||||
if (n == 4)
|
||||
{
|
||||
m_region = v;
|
||||
m_use_atlas = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeImage::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
TextureManager::get(m_tex_id).bind();
|
||||
auto& sampler = m_use_mipmaps ? m_sampler_mips : m_sampler;
|
||||
sampler.bind(0);
|
||||
glEnable(GL_BLEND);
|
||||
if (m_use_atlas)
|
||||
{
|
||||
ui::ShaderManager::use(kShader::Atlas);
|
||||
ui::ShaderManager::u_vec2(kShaderUniform::Tof, m_off);
|
||||
ui::ShaderManager::u_vec2(kShaderUniform::Tsz, m_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui::ShaderManager::use(kShader::Texture);
|
||||
}
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
m_plane.draw_fill();
|
||||
sampler.unbind();
|
||||
TextureManager::get(m_tex_id).unbind();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
26
src/node_image.h
Normal file
26
src/node_image.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
|
||||
class NodeImage : public Node
|
||||
{
|
||||
public:
|
||||
static ui::Plane m_plane;
|
||||
static Sampler m_sampler;
|
||||
static Sampler m_sampler_mips;
|
||||
bool m_use_atlas = false;
|
||||
bool m_use_mipmaps = false;
|
||||
glm::vec4 m_region;
|
||||
glm::vec2 m_off;
|
||||
glm::vec2 m_sz;
|
||||
std::string m_path;
|
||||
uint16_t m_tex_id;
|
||||
static void static_init();
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void create() override;
|
||||
virtual void restore_context() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
virtual void draw() override;
|
||||
};
|
||||
33
src/node_image_texture.cpp
Normal file
33
src/node_image_texture.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "shader.h"
|
||||
#include "node_image.h"
|
||||
|
||||
Node* NodeImageTexture::clone_instantiate() const
|
||||
{
|
||||
return new NodeImageTexture();
|
||||
}
|
||||
|
||||
void NodeImageTexture::clone_copy(Node* dest) const
|
||||
{
|
||||
Node::clone_copy(dest);
|
||||
NodeImageTexture* n = static_cast<NodeImageTexture*>(dest);
|
||||
n->tex = tex;
|
||||
}
|
||||
|
||||
void NodeImageTexture::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
tex.bind();
|
||||
auto& sampler = tex.has_mips ? NodeImage::m_sampler_mips : NodeImage::m_sampler;
|
||||
sampler.bind(0);
|
||||
glEnable(GL_BLEND);
|
||||
ui::ShaderManager::use(kShader::Texture);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
NodeImage::m_plane.draw_fill();
|
||||
sampler.unbind();
|
||||
tex.unbind();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
18
src/node_image_texture.h
Normal file
18
src/node_image_texture.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "texture.h"
|
||||
|
||||
class NodeImageTexture : public Node
|
||||
{
|
||||
public:
|
||||
Texture2D tex;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
// TODO: maybe we can save the texture data and restore later
|
||||
//virtual void restore_context() override
|
||||
//{
|
||||
// Node::restore_context();
|
||||
// create();
|
||||
//}
|
||||
virtual void draw() override;
|
||||
};
|
||||
25
src/node_message_box.cpp
Normal file
25
src/node_message_box.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_message_box.h"
|
||||
#include "layout.h"
|
||||
|
||||
Node* NodeMessageBox::clone_instantiate() const
|
||||
{
|
||||
return new NodeMessageBox();
|
||||
}
|
||||
|
||||
void NodeMessageBox::init()
|
||||
{
|
||||
SetPosition(0, 0);
|
||||
SetWidthP(100);
|
||||
SetHeightP(100);
|
||||
SetPositioning(YGPositionTypeAbsolute);
|
||||
m_template = (*m_manager)[const_hash("message-box")]->m_children[0]->clone();
|
||||
add_child(m_template);
|
||||
m_title = m_template->find<NodeText>("title");
|
||||
m_message = m_template->find<NodeText>("message");
|
||||
btn_ok = m_template->find<NodeButton>("btn-ok");
|
||||
btn_ok->on_click = [&](Node*) { destroy(); };
|
||||
btn_cancel = m_template->find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [&](Node*) { destroy(); };
|
||||
}
|
||||
16
src/node_message_box.h
Normal file
16
src/node_message_box.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_button.h"
|
||||
#include "node_text.h"
|
||||
|
||||
class NodeMessageBox : public Node
|
||||
{
|
||||
public:
|
||||
Node* m_template;
|
||||
NodeButton* btn_ok;
|
||||
NodeButton* btn_cancel;
|
||||
NodeText* m_message;
|
||||
NodeText* m_title;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
};
|
||||
225
src/node_panel_brush.cpp
Normal file
225
src/node_panel_brush.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_brush.h"
|
||||
#include "asset.h"
|
||||
#include "texture.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
Node* NodeButtonBrush::clone_instantiate() const
|
||||
{
|
||||
return new NodeButtonBrush();
|
||||
}
|
||||
|
||||
void NodeButtonBrush::init()
|
||||
{
|
||||
init_template("tpl-brush-icon");
|
||||
color_hover = glm::vec4(.7, .7, .7, 1);
|
||||
color_normal = glm::vec4(.3, .3, .3, 1);
|
||||
m_color = color_normal;
|
||||
img = (NodeImage*)m_children[0].get();
|
||||
}
|
||||
|
||||
void NodeButtonBrush::set_icon(const char* path)
|
||||
{
|
||||
img->m_path = path;
|
||||
img->m_tex_id = const_hash(img->m_path.c_str());
|
||||
img->m_use_mipmaps = true;
|
||||
img->create();
|
||||
}
|
||||
|
||||
void NodeButtonBrush::draw()
|
||||
{
|
||||
m_color = m_mouse_inside ? color_hover : color_normal;
|
||||
m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color;
|
||||
NodeButtonCustom::draw();
|
||||
}
|
||||
|
||||
Node* NodePanelBrush::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelBrush();
|
||||
}
|
||||
|
||||
void NodePanelBrush::init()
|
||||
{
|
||||
init_template("tpl-panel-brushes");
|
||||
//m_layers_container = find<NodeBorder>("layers-container");
|
||||
static auto icons = Asset::list_files("data/thumbs", true, ".*\\.png$");
|
||||
|
||||
if ((m_container = find<NodeBorder>("brushes")))
|
||||
{
|
||||
int count = 0;
|
||||
for (auto& i : icons)
|
||||
{
|
||||
std::string path = "data/thumbs/" + i;
|
||||
std::string path_hi = "data/brushes/" + i;
|
||||
NodeButtonBrush* brush = new NodeButtonBrush;
|
||||
m_container->add_child(brush);
|
||||
brush->init();
|
||||
brush->create();
|
||||
brush->loaded();
|
||||
brush->set_icon(path.c_str());
|
||||
brush->m_brushID = count++;
|
||||
brush->high_path = path_hi;
|
||||
brush->brush_name = i;
|
||||
brush->high_id = const_hash(path_hi.c_str());
|
||||
m_brushes.push_back(brush);
|
||||
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodePanelBrush::handle_click(Node* target)
|
||||
{
|
||||
if (target == m_current)
|
||||
return;
|
||||
if (m_current)
|
||||
m_current->m_selected = false;
|
||||
m_current = (NodeButtonBrush*)target;
|
||||
m_current->m_selected = true;
|
||||
if (on_brush_changed)
|
||||
on_brush_changed(this, m_current->m_brushID);
|
||||
}
|
||||
|
||||
int NodePanelBrush::find_brush(const std::string & name) const
|
||||
{
|
||||
for (int i = 0; i < m_brushes.size(); i++)
|
||||
{
|
||||
if (m_brushes[i]->brush_name.find(name) != std::string::npos)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t NodePanelBrush::get_texture_id(int index) const
|
||||
{
|
||||
TextureManager::load(m_brushes[index]->high_path.c_str(), true);
|
||||
return m_brushes[index]->high_id;
|
||||
}
|
||||
|
||||
int NodePanelBrush::get_brush_id(int index) const
|
||||
{
|
||||
return m_brushes[index]->m_brushID;
|
||||
}
|
||||
|
||||
// select the current brush based on the texture id
|
||||
void NodePanelBrush::select_brush(int brush_id)
|
||||
{
|
||||
if (m_current)
|
||||
m_current->m_selected = false;
|
||||
for (auto b : m_brushes)
|
||||
{
|
||||
if (b->m_brushID == brush_id)
|
||||
{
|
||||
b->m_selected = true;
|
||||
m_current = b;
|
||||
TextureManager::load(b->high_path.c_str(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
Node* NodeBrushPresetItem::clone_instantiate() const
|
||||
{
|
||||
return new NodeBrushPresetItem();
|
||||
}
|
||||
|
||||
void NodeBrushPresetItem::init()
|
||||
{
|
||||
init_template("tpl-brush-preset");
|
||||
color_hover = glm::vec4(.7, .7, .7, 1);
|
||||
color_normal = glm::vec4(.3, .3, .3, 1);
|
||||
m_color = color_normal;
|
||||
m_thumb = (NodeImage*)m_children[0].get();
|
||||
m_preview = (NodeStrokePreview*)m_children[1].get();
|
||||
}
|
||||
|
||||
void NodeBrushPresetItem::draw()
|
||||
{
|
||||
m_color = m_mouse_inside ? color_hover : color_normal;
|
||||
m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color;
|
||||
NodeButtonCustom::draw();
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
Node* NodePanelBrushPreset::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelBrushPreset();
|
||||
}
|
||||
|
||||
void NodePanelBrushPreset::init()
|
||||
{
|
||||
init_template("tpl-panel-brush-preset");
|
||||
|
||||
static auto icons = Asset::list_files("data/thumbs", true, ".*\\.png$");
|
||||
|
||||
if ((m_container = find<NodeBorder>("brushes")))
|
||||
{
|
||||
int count = 0;
|
||||
for (auto& i : icons)
|
||||
{
|
||||
std::string path = "data/thumbs/" + i;
|
||||
std::string path_hi = "data/brushes/" + i;
|
||||
NodeBrushPresetItem* brush = new NodeBrushPresetItem;
|
||||
m_container->add_child(brush);
|
||||
brush->init();
|
||||
brush->create();
|
||||
brush->loaded();
|
||||
// brush->set_icon(path.c_str());
|
||||
brush->m_brushID = count++;
|
||||
brush->high_path = path_hi;
|
||||
brush->high_id = const_hash(path_hi.c_str());
|
||||
brush->m_brush.m_tex_id = const_hash(path.c_str());
|
||||
brush->m_brush.m_tip_size = .05;
|
||||
brush->m_brush.m_tip_flow = .2;
|
||||
brush->m_brush.m_tip_opacity = 1;
|
||||
brush->m_brush.m_tip_spacing = 0.03;
|
||||
//brush->m_brush.m_jitter_spread = (rand() % 1000) * 0.0001;
|
||||
brush->m_preview->m_brush = brush->m_brush;
|
||||
brush->m_preview->draw_stroke();
|
||||
brush->m_thumb->m_path = path;
|
||||
brush->m_thumb->m_tex_id = const_hash(path.c_str());
|
||||
brush->m_thumb->create();
|
||||
m_brushes.push_back(brush);
|
||||
brush->on_click = std::bind(&NodePanelBrushPreset::handle_click, this, std::placeholders::_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodePanelBrushPreset::handle_click(Node* target)
|
||||
{
|
||||
if (target == m_current)
|
||||
return;
|
||||
if (m_current)
|
||||
m_current->m_selected = false;
|
||||
m_current = (NodeButtonBrush*)target;
|
||||
m_current->m_selected = true;
|
||||
if (on_brush_changed)
|
||||
on_brush_changed(this, m_current->m_brushID);
|
||||
}
|
||||
|
||||
ui::Brush NodePanelBrushPreset::get_brush(int index) const
|
||||
{
|
||||
auto b = m_brushes[index]->m_brush;
|
||||
TextureManager::load(m_brushes[index]->high_path.c_str(), true);
|
||||
b.m_tex_id = m_brushes[index]->high_id;
|
||||
return b;
|
||||
}
|
||||
|
||||
uint16_t NodePanelBrushPreset::get_texture_id(int index) const
|
||||
{
|
||||
TextureManager::load(m_brushes[index]->high_path.c_str(), true);
|
||||
return m_brushes[index]->high_id;
|
||||
}
|
||||
|
||||
int NodePanelBrushPreset::get_brush_id(int index) const
|
||||
{
|
||||
return m_brushes[index]->m_brushID;
|
||||
}
|
||||
71
src/node_panel_brush.h
Normal file
71
src/node_panel_brush.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_button_custom.h"
|
||||
#include "node_image.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "brush.h"
|
||||
|
||||
class NodeButtonBrush : public NodeButtonCustom
|
||||
{
|
||||
public:
|
||||
int m_brushID;
|
||||
bool m_selected = false;
|
||||
std::string brush_name;
|
||||
std::string high_path;
|
||||
uint16_t high_id;
|
||||
NodeImage* img;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
void set_icon(const char* path);
|
||||
virtual void draw() override;
|
||||
};
|
||||
|
||||
class NodePanelBrush : public Node
|
||||
{
|
||||
std::vector<NodeButtonBrush*> m_brushes;
|
||||
NodeButtonBrush* m_current = nullptr;
|
||||
Node* m_container;
|
||||
public:
|
||||
std::function<void(Node* target, int id)> on_brush_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
void handle_click(Node* target);
|
||||
std::vector<std::string> FindAllBrushes(const std::string& folder);
|
||||
int find_brush(const std::string& name) const;
|
||||
uint16_t get_texture_id(int index) const;
|
||||
int get_brush_id(int index) const;
|
||||
void select_brush(int brush_id);
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
class NodeBrushPresetItem : public NodeButtonCustom
|
||||
{
|
||||
public:
|
||||
int m_brushID;
|
||||
ui::Brush m_brush;
|
||||
std::string high_path;
|
||||
uint16_t high_id;
|
||||
bool m_selected = false;
|
||||
NodeStrokePreview* m_preview;
|
||||
NodeImage* m_thumb;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
virtual void draw() override;
|
||||
};
|
||||
|
||||
class NodePanelBrushPreset : public Node
|
||||
{
|
||||
std::vector<NodeBrushPresetItem*> m_brushes;
|
||||
NodeButtonBrush* m_current = nullptr;
|
||||
Node* m_container;
|
||||
public:
|
||||
std::function<void(Node* target, int id)> on_brush_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
void handle_click(Node* target);
|
||||
uint16_t get_texture_id(int index) const;
|
||||
ui::Brush get_brush(int index) const;
|
||||
int get_brush_id(int index) const;
|
||||
};
|
||||
|
||||
59
src/node_panel_color.cpp
Normal file
59
src/node_panel_color.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_color.h"
|
||||
#include "canvas.h"
|
||||
|
||||
Node* NodePanelColor::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelColor();
|
||||
}
|
||||
|
||||
void NodePanelColor::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodePanelColor* n = static_cast<NodePanelColor*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodePanelColor::init()
|
||||
{
|
||||
init_template("tpl-panel-color");
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodePanelColor::init_controls()
|
||||
{
|
||||
m_quad = find<NodeColorQuad>("quad");
|
||||
m_hue = find<NodeSliderHue>("hue");
|
||||
m_hue->on_hue_changed = [this](Node*, glm::vec4 hue_color) {
|
||||
m_base_color = m_quad->m_color = hue_color;
|
||||
float hue = convert_rgb2hsv(m_base_color).x;
|
||||
m_color = glm::vec4(convert_hsv2rgb(glm::vec3(hue, m_cursor.x, 1.f-m_cursor.y)), 1.f);
|
||||
m_quad->m_color = hue_color;
|
||||
if (on_color_changed)
|
||||
on_color_changed(this, m_color);
|
||||
};
|
||||
m_quad->on_value_changed = [this](Node*, glm::vec2 pos)
|
||||
{
|
||||
m_cursor = pos;
|
||||
float hue = convert_rgb2hsv(m_base_color).x;
|
||||
m_color = glm::vec4(convert_hsv2rgb(glm::vec3(hue, m_cursor.x, 1.f-m_cursor.y)), 1.f);
|
||||
if (on_color_changed)
|
||||
on_color_changed(this, m_color);
|
||||
};
|
||||
m_hue->set_value(0);
|
||||
}
|
||||
|
||||
void NodePanelColor::set_color(glm::vec3 rgb)
|
||||
{
|
||||
auto hsv = convert_rgb2hsv(rgb);
|
||||
m_hue->m_value.y = hsv.x;
|
||||
m_quad->m_value = glm::vec2(hsv.y, 1.f - hsv.z);
|
||||
m_quad->m_color = glm::vec4(rgb, 1);
|
||||
m_cursor = m_quad->m_value;
|
||||
m_base_color = glm::vec4(rgb, 1);
|
||||
}
|
||||
|
||||
void NodePanelColor::added(Node* parent)
|
||||
{
|
||||
set_color(ui::Canvas::I->m_current_brush.m_tip_color);
|
||||
}
|
||||
21
src/node_panel_color.h
Normal file
21
src/node_panel_color.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_color_quad.h"
|
||||
#include "node_slider.h"
|
||||
|
||||
class NodePanelColor : public Node
|
||||
{
|
||||
public:
|
||||
NodeColorQuad* m_quad;
|
||||
NodeSliderHue* m_hue;
|
||||
glm::vec4 m_base_color;
|
||||
glm::vec4 m_color;
|
||||
glm::vec2 m_cursor;
|
||||
std::function<void(Node* target, glm::vec4 color)> on_color_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void added(Node* parent) override;
|
||||
void init_controls();
|
||||
void set_color(glm::vec3 rgb);
|
||||
};
|
||||
65
src/node_panel_grid.cpp
Normal file
65
src/node_panel_grid.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_grid.h"
|
||||
#include "canvas.h"
|
||||
#include "app.h"
|
||||
#include "image.h"
|
||||
|
||||
Node* NodePanelGrid::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelGrid();
|
||||
}
|
||||
|
||||
void NodePanelGrid::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodePanelGrid* n = static_cast<NodePanelGrid*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodePanelGrid::init()
|
||||
{
|
||||
init_template("tpl-panel-grid");
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodePanelGrid::init_controls()
|
||||
{
|
||||
m_groud_opacity = find<NodeSliderH>("grid-ground-opacity");
|
||||
m_groud_scale = find<NodeSliderH>("grid-ground-scale");
|
||||
m_groud_value = find<NodeSliderH>("grid-ground-value");
|
||||
m_groud_height = find<NodeSliderH>("grid-ground-height");
|
||||
//m_box_opacity = find<NodeSliderH>("grid-box-opacity");
|
||||
//m_box_width = find<NodeSliderH>("grid-box-width");
|
||||
//m_box_height = find<NodeSliderH>("grid-box-height");
|
||||
//m_box_depth = find<NodeSliderH>("grid-box-depth");
|
||||
|
||||
auto update_hm = [this](Node* target, float v) {
|
||||
m_hm_plane.create(1, 1, m_hm_image, -m_hm_height->get_value());
|
||||
};
|
||||
|
||||
m_hm_preview = find<NodeImageTexture>("grid-heightmap-preview");
|
||||
m_hm_load = find<NodeButton>("grid-heightmap-load");
|
||||
m_hm_offset = find<NodeSliderH>("grid-heightmap-offset");
|
||||
m_hm_height = find<NodeSliderH>("grid-heightmap-height");
|
||||
|
||||
m_hm_height->on_value_changed = update_hm;
|
||||
m_hm_preview->SetHeight(0);
|
||||
|
||||
//m_hm_plane.create(1, 1);
|
||||
|
||||
m_hm_load->on_click = [this](Node*) {
|
||||
App::I.pick_image([this](std::string path) {
|
||||
ui::Image img;
|
||||
if (img.load_file(path))
|
||||
{
|
||||
m_hm_image = img.resize(128, 128);
|
||||
m_hm_preview->tex.create(m_hm_image);
|
||||
m_hm_preview->tex.create_mipmaps();
|
||||
auto sz = m_hm_preview->tex.size();
|
||||
m_hm_preview->SetAspectRatio(sz.x / sz.y);
|
||||
m_hm_plane.create(1, 1, m_hm_image, -m_hm_height->get_value());
|
||||
m_hm_preview->SetHeight(100);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
35
src/node_panel_grid.h
Normal file
35
src/node_panel_grid.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "node_slider.h"
|
||||
#include "brush.h"
|
||||
#include "node_checkbox.h"
|
||||
#include "node_combobox.h"
|
||||
#include "node_image_texture.h"
|
||||
#include "node_button.h"
|
||||
#include "shape.h"
|
||||
#include "image.h"
|
||||
|
||||
class NodePanelGrid : public Node
|
||||
{
|
||||
public:
|
||||
NodeSliderH* m_groud_opacity;
|
||||
NodeSliderH* m_groud_scale;
|
||||
NodeSliderH* m_groud_value;
|
||||
NodeSliderH* m_groud_height;
|
||||
NodeSliderH* m_box_opacity;
|
||||
NodeSliderH* m_box_width;
|
||||
NodeSliderH* m_box_height;
|
||||
NodeSliderH* m_box_depth;
|
||||
NodeImageTexture* m_hm_preview;
|
||||
NodeButton* m_hm_load;
|
||||
NodeSliderH* m_hm_offset;
|
||||
NodeSliderH* m_hm_height;
|
||||
ui::HeightmapPlane m_hm_plane;
|
||||
ui::Image m_hm_image;
|
||||
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
};
|
||||
245
src/node_panel_layer.cpp
Normal file
245
src/node_panel_layer.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_layer.h"
|
||||
|
||||
Node* NodeLayer::clone_instantiate() const
|
||||
{
|
||||
return new NodeLayer();
|
||||
}
|
||||
|
||||
void NodeLayer::clone_children(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_children(dest);
|
||||
NodeLayer* n = static_cast<NodeLayer*>(dest);
|
||||
n->m_label = n->find<NodeText>("label");
|
||||
n->m_visibility = n->find<NodeCheckBox>("cb");
|
||||
}
|
||||
|
||||
void NodeLayer::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
NodeLayer* n = (NodeLayer*)dest;
|
||||
n->m_selected = m_selected;
|
||||
n->m_label_text = m_label_text;
|
||||
}
|
||||
|
||||
void NodeLayer::init()
|
||||
{
|
||||
const auto& m_template = (NodeBorder*)init_template("tpl-layer");
|
||||
m_color = m_template->m_color;
|
||||
m_border_color = m_template->m_border_color;
|
||||
m_thinkness = m_template->m_thinkness;
|
||||
m_label = find<NodeText>("label");
|
||||
m_visibility = find<NodeCheckBox>("cb");
|
||||
m_opacity = find<NodeSliderH>("sl-opacity");
|
||||
}
|
||||
|
||||
void NodeLayer::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
NodeBorder::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Text:
|
||||
m_label_text = attr->Value();
|
||||
break;
|
||||
case kAttribute::Selected:
|
||||
m_selected = attr->BoolValue();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeLayer::loaded()
|
||||
{
|
||||
NodeBorder::loaded();
|
||||
if (!m_label_text.empty())
|
||||
m_label->set_text(m_label_text.c_str());
|
||||
m_opacity->on_value_changed = [this](Node*, float value) {
|
||||
if (on_opacity_changed)
|
||||
on_opacity_changed(this, value);
|
||||
};
|
||||
m_visibility->on_value_changed = [this](Node*, bool checked) {
|
||||
if (on_visibility_changed)
|
||||
on_visibility_changed(this, checked);
|
||||
};
|
||||
}
|
||||
|
||||
kEventResult NodeLayer::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseEnter:
|
||||
break;
|
||||
case kEventType::MouseLeave:
|
||||
break;
|
||||
case kEventType::MouseDownL:
|
||||
m_selected = true;
|
||||
if (on_selected)
|
||||
on_selected(this);
|
||||
if (on_highlight)
|
||||
on_highlight(this, true);
|
||||
mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
if (on_highlight)
|
||||
on_highlight(this, false);
|
||||
mouse_release();
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeLayer::draw()
|
||||
{
|
||||
auto c = m_selected ? m_color_selected : m_color_normal;
|
||||
m_thinkness = m_selected ? 1.f : 0.f;
|
||||
m_color = m_mouse_inside ? m_color_hover : c;
|
||||
NodeBorder::draw();
|
||||
}
|
||||
|
||||
void NodeLayer::set_name(const char* s)
|
||||
{
|
||||
m_label_text = s;
|
||||
m_label->set_text(s);
|
||||
}
|
||||
Node* NodePanelLayer::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelLayer();
|
||||
}
|
||||
|
||||
void NodePanelLayer::init()
|
||||
{
|
||||
LOG("NodePanelLayer::init");
|
||||
init_template("tpl-panel-layers");
|
||||
LOG("template initted");
|
||||
m_layers_container = find<NodeBorder>("layers-container");
|
||||
LOG("template container found");
|
||||
// for (int i = 0; i < 1; i++)
|
||||
// {
|
||||
// LOG("add layer");
|
||||
// add_layer();
|
||||
// }
|
||||
LOG("find components");
|
||||
// m_current_layer = m_layers[0];
|
||||
// m_layers[0]->m_selected = true;
|
||||
btn_add = find<NodeButtonCustom>("btn-add");
|
||||
btn_remove = find<NodeButtonCustom>("btn-remove");
|
||||
btn_up = find<NodeButtonCustom>("btn-up");
|
||||
btn_down = find<NodeButtonCustom>("btn-down");
|
||||
LOG("attach events");
|
||||
btn_add->on_click = [this](Node*) {
|
||||
add_layer();
|
||||
if (on_layer_add)
|
||||
on_layer_add(this);
|
||||
};
|
||||
btn_remove->on_click = [this](Node*) {
|
||||
if (m_layers.size() == 1)
|
||||
return; // dont' delete the last layer
|
||||
remove_layer(m_current_layer);
|
||||
};
|
||||
btn_up->on_click = [this](Node*) {
|
||||
int old_idx = m_layers_container->get_child_index(m_current_layer);
|
||||
m_layers_container->move_child_offset(m_current_layer, -1);
|
||||
int new_idx = m_layers_container->get_child_index(m_current_layer);
|
||||
if (on_layer_order && old_idx != new_idx)
|
||||
{
|
||||
on_layer_order(this, old_idx, new_idx);
|
||||
}
|
||||
};
|
||||
btn_down->on_click = [this](Node*) {
|
||||
int old_idx = m_layers_container->get_child_index(m_current_layer);
|
||||
m_layers_container->move_child_offset(m_current_layer, +1);
|
||||
int new_idx = m_layers_container->get_child_index(m_current_layer);
|
||||
if (on_layer_order && old_idx != new_idx)
|
||||
{
|
||||
on_layer_order(this, old_idx, new_idx);
|
||||
}
|
||||
};
|
||||
LOG("done init");
|
||||
}
|
||||
|
||||
void NodePanelLayer::add_layer(const char* name)
|
||||
{
|
||||
NodeLayer* l = new NodeLayer;
|
||||
m_layers_container->add_child(l);
|
||||
l->init();
|
||||
l->create();
|
||||
l->loaded();
|
||||
l->set_name(name);
|
||||
l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
|
||||
l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2);
|
||||
l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2);
|
||||
l->on_highlight = std::bind(&NodePanelLayer::handle_layer_highlight, this, std::placeholders::_1, std::placeholders::_2);
|
||||
if (m_current_layer)
|
||||
m_current_layer->m_selected = false;
|
||||
m_current_layer = l;
|
||||
m_current_layer->m_selected = true;
|
||||
m_layers.push_back(l);
|
||||
}
|
||||
|
||||
void NodePanelLayer::add_layer()
|
||||
{
|
||||
static char s[64];
|
||||
sprintf(s, "Layer-%d", id_counter++);
|
||||
add_layer(s);
|
||||
}
|
||||
|
||||
NodeLayer* NodePanelLayer::get_layer_at(int index)
|
||||
{
|
||||
return static_cast<NodeLayer*>(m_layers_container->get_child_at(index));
|
||||
}
|
||||
|
||||
void NodePanelLayer::remove_layer(NodeLayer* layer)
|
||||
{
|
||||
auto it = std::find(m_layers.begin(), m_layers.end(), m_current_layer);
|
||||
auto i = m_layers_container->get_child_index(m_current_layer);
|
||||
int old_idx = (int)std::distance(m_layers.begin(), it);
|
||||
m_layers_container->remove_child(m_current_layer);
|
||||
m_layers.erase(it);
|
||||
i = std::min<int>(i, (int)m_layers.size() - 1);
|
||||
m_current_layer = m_layers[i];
|
||||
m_current_layer->m_selected = true;
|
||||
if (on_layer_delete)
|
||||
on_layer_delete(this, old_idx);
|
||||
if (on_layer_change)
|
||||
on_layer_change(this, -1, i);
|
||||
}
|
||||
|
||||
void NodePanelLayer::handle_layer_opacity(NodeLayer* target, float value)
|
||||
{
|
||||
if (on_layer_opacity_changed)
|
||||
on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value);
|
||||
}
|
||||
|
||||
void NodePanelLayer::handle_layer_highlight(NodeLayer* target, bool highlight)
|
||||
{
|
||||
if (on_layer_highlight_changed)
|
||||
on_layer_highlight_changed(this, m_layers_container->get_child_index(target), highlight);
|
||||
}
|
||||
|
||||
void NodePanelLayer::handle_layer_visibility(NodeLayer* target, bool visible)
|
||||
{
|
||||
if (on_layer_visibility_changed)
|
||||
on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible);
|
||||
}
|
||||
|
||||
void NodePanelLayer::handle_layer_selected(NodeLayer* target)
|
||||
{
|
||||
if (m_current_layer)
|
||||
m_current_layer->m_selected = false;
|
||||
m_current_layer = target;
|
||||
m_current_layer->m_selected = true;
|
||||
if (on_layer_change)
|
||||
on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
|
||||
}
|
||||
|
||||
void NodePanelLayer::clear()
|
||||
{
|
||||
m_layers_container->remove_all_children();
|
||||
m_layers.clear();
|
||||
m_current_layer = nullptr;
|
||||
}
|
||||
63
src/node_panel_layer.h
Normal file
63
src/node_panel_layer.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_text.h"
|
||||
#include "node_checkbox.h"
|
||||
#include "node_slider.h"
|
||||
#include "node_button_custom.h"
|
||||
|
||||
class NodeLayer : public NodeBorder
|
||||
{
|
||||
public:
|
||||
std::function<void(NodeLayer* target)> on_selected;
|
||||
std::function<void(NodeLayer* target, float opacity)> on_opacity_changed;
|
||||
std::function<void(NodeLayer* target, bool visible)> on_visibility_changed;
|
||||
std::function<void(NodeLayer* target, bool highlight)> on_highlight;
|
||||
bool m_selected = false;
|
||||
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
|
||||
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
|
||||
glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
|
||||
std::string m_label_text;
|
||||
NodeText* m_label;
|
||||
NodeCheckBox* m_visibility;
|
||||
NodeSliderH* m_opacity;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_children(Node* dest) const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
virtual void loaded() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
virtual void draw() override;
|
||||
void set_name(const char* s);
|
||||
};
|
||||
|
||||
class NodePanelLayer : public Node
|
||||
{
|
||||
NodeButtonCustom* btn_add;
|
||||
NodeButtonCustom* btn_remove;
|
||||
NodeButtonCustom* btn_up;
|
||||
NodeButtonCustom* btn_down;
|
||||
int id_counter = 0;
|
||||
public:
|
||||
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_change;
|
||||
std::function<void(Node* target, int idx, float value)> on_layer_opacity_changed;
|
||||
std::function<void(Node* target, int idx, bool visible)> on_layer_visibility_changed;
|
||||
std::function<void(Node* target, int idx, bool highlight)> on_layer_highlight_changed;
|
||||
std::function<void(Node* target, int index)> on_layer_delete;
|
||||
std::function<void(Node* target)> on_layer_add;
|
||||
std::function<void(Node* target, int old_idx, int new_idx)> on_layer_order;
|
||||
NodeLayer* m_current_layer = nullptr;
|
||||
std::vector<NodeLayer*> m_layers;
|
||||
NodeBorder* m_layers_container;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
void add_layer();
|
||||
void add_layer(const char* name);
|
||||
NodeLayer* get_layer_at(int index);
|
||||
void remove_layer(NodeLayer* layer);
|
||||
void handle_layer_opacity(NodeLayer* target, float value);
|
||||
void handle_layer_visibility(NodeLayer* target, bool visible);
|
||||
void handle_layer_highlight(NodeLayer* target, bool highlight);
|
||||
void handle_layer_selected(NodeLayer* target);
|
||||
void clear();
|
||||
};
|
||||
123
src/node_panel_stroke.cpp
Normal file
123
src/node_panel_stroke.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_stroke.h"
|
||||
#include "canvas.h"
|
||||
|
||||
Node* NodePanelStroke::clone_instantiate() const
|
||||
{
|
||||
return new NodePanelStroke();
|
||||
}
|
||||
|
||||
void NodePanelStroke::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodePanelStroke* n = static_cast<NodePanelStroke*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodePanelStroke::init()
|
||||
{
|
||||
init_template("tpl-panel-stroke");
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodePanelStroke::update_controls()
|
||||
{
|
||||
const auto& b = ui::Canvas::I->m_current_brush;
|
||||
m_tip_size->m_value.x = glm::pow(b.m_tip_size, 1.f/3.f);
|
||||
m_tip_spacing->m_value.x = glm::pow(b.m_tip_spacing, 1.f/2.f) / 4.f;
|
||||
m_tip_flow->m_value.x = glm::pow(b.m_tip_flow, 1.f/2.f);
|
||||
m_tip_opacity->m_value.x = b.m_tip_opacity;
|
||||
m_tip_angle->m_value.x = b.m_tip_angle;
|
||||
m_tip_stencil->m_value.x = b.m_tip_stencil;
|
||||
m_tip_wet->m_value.x = b.m_tip_wet;
|
||||
m_tip_noise->m_value.x = b.m_tip_noise;
|
||||
m_jitter_scale->m_value.x = b.m_jitter_scale;
|
||||
m_jitter_angle->m_value.x = b.m_jitter_angle;
|
||||
m_jitter_spread->m_value.x = b.m_jitter_spread;
|
||||
m_jitter_flow->m_value.x = b.m_jitter_flow;
|
||||
m_jitter_hue->m_value.x = b.m_jitter_hue;
|
||||
m_jitter_sat->m_value.x = b.m_jitter_sat;
|
||||
m_jitter_val->m_value.x = b.m_jitter_val;
|
||||
m_tip_angle_follow->checked = b.m_tip_angle_follow;
|
||||
m_tip_flow_pressure->checked = b.m_tip_flow_pressure;
|
||||
m_tip_size_pressure->checked = b.m_tip_size_pressure;
|
||||
m_preview->m_brush = b;
|
||||
m_preview->draw_stroke();
|
||||
}
|
||||
|
||||
void NodePanelStroke::init_controls()
|
||||
{
|
||||
m_preview = find<NodeStrokePreview>("canvas");
|
||||
m_blend_mode = find<NodeComboBox>("blend-mode");
|
||||
m_blend_mode->on_select = [](Node*, int index) {
|
||||
ui::Canvas::I->m_current_brush.m_blend_mode = index;
|
||||
};
|
||||
|
||||
init_slider(m_tip_size, "tip-size", &ui::Brush::m_tip_size);
|
||||
init_slider(m_tip_spacing, "tip-spacing", &ui::Brush::m_tip_spacing);
|
||||
init_slider(m_tip_flow, "tip-flow", &ui::Brush::m_tip_flow);
|
||||
init_slider(m_tip_opacity, "tip-opacity", &ui::Brush::m_tip_opacity);
|
||||
init_slider(m_tip_angle, "tip-angle", &ui::Brush::m_tip_angle);
|
||||
init_slider(m_tip_mix, "tip-mix", &ui::Brush::m_tip_mix);
|
||||
init_slider(m_tip_stencil, "tip-stencil", &ui::Brush::m_tip_stencil);
|
||||
init_slider(m_tip_wet, "tip-wet", &ui::Brush::m_tip_wet);
|
||||
init_slider(m_tip_noise, "tip-noise", &ui::Brush::m_tip_noise);
|
||||
init_slider(m_tip_hue, "tip-hue", &ui::Brush::m_tip_hue);
|
||||
init_slider(m_tip_sat, "tip-sat", &ui::Brush::m_tip_sat);
|
||||
init_slider(m_tip_val, "tip-val", &ui::Brush::m_tip_val);
|
||||
init_slider(m_jitter_scale, "jitter-scale", &ui::Brush::m_jitter_scale);
|
||||
init_slider(m_jitter_angle, "jitter-angle", &ui::Brush::m_jitter_angle);
|
||||
init_slider(m_jitter_spread, "jitter-spread", &ui::Brush::m_jitter_spread);
|
||||
init_slider(m_jitter_flow, "jitter-flow", &ui::Brush::m_jitter_flow);
|
||||
init_slider(m_jitter_hue, "jitter-hue", &ui::Brush::m_jitter_hue);
|
||||
init_slider(m_jitter_sat, "jitter-sat", &ui::Brush::m_jitter_sat);
|
||||
init_slider(m_jitter_val, "jitter-val", &ui::Brush::m_jitter_val);
|
||||
m_curves[m_tip_size] = [](float v){ return glm::pow(v, 3.f); };
|
||||
m_curves[m_tip_spacing] = [](float v){ return glm::pow(v * 4.f, 2.f); };
|
||||
m_curves[m_tip_flow] = [](float v){ return glm::pow(v, 2.f); };
|
||||
|
||||
init_checkbox(m_tip_angle_follow, "tip-angle-follow", &ui::Brush::m_tip_angle_follow);
|
||||
init_checkbox(m_tip_flow_pressure, "tip-flow-pressure", &ui::Brush::m_tip_flow_pressure);
|
||||
init_checkbox(m_tip_size_pressure, "tip-size-pressure", &ui::Brush::m_tip_size_pressure);
|
||||
init_checkbox(m_tip_hue_pressure, "tip-hue-pressure", &ui::Brush::m_tip_hue_pressure);
|
||||
init_checkbox(m_tip_sat_pressure, "tip-sat-pressure", &ui::Brush::m_tip_sat_pressure);
|
||||
init_checkbox(m_tip_val_pressure, "tip-val-pressure", &ui::Brush::m_tip_val_pressure);
|
||||
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
}
|
||||
|
||||
void NodePanelStroke::init_slider(NodeSliderH*& target, const char* id, float ui::Brush::* prop)
|
||||
{
|
||||
target = find<NodeSliderH>(id);
|
||||
target->on_value_changed = std::bind(&NodePanelStroke::handle_slide,
|
||||
this, prop, std::placeholders::_1, std::placeholders::_2);
|
||||
//m_canvas->m_brush.*prop = target->m_value.x;
|
||||
}
|
||||
|
||||
void NodePanelStroke::handle_slide(float ui::Brush::* prop, Node* target, float value)
|
||||
{
|
||||
auto curve = m_curves.find((NodeSliderH*)target);
|
||||
ui::Canvas::I->m_current_brush.*prop = curve != m_curves.end() ? curve->second(value) : value;
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
if (on_stroke_change)
|
||||
on_stroke_change(this);
|
||||
}
|
||||
|
||||
void NodePanelStroke::init_checkbox(NodeCheckBox*& target, const char* id, bool ui::Brush::* prop)
|
||||
{
|
||||
target = find<NodeCheckBox>(id);
|
||||
target->on_value_changed = std::bind(&NodePanelStroke::handle_checkbox,
|
||||
this, prop, std::placeholders::_1, std::placeholders::_2);
|
||||
ui::Canvas::I->m_current_brush.*prop = target->checked;
|
||||
}
|
||||
|
||||
void NodePanelStroke::handle_checkbox(bool ui::Brush::* prop, Node *target, bool value)
|
||||
{
|
||||
ui::Canvas::I->m_current_brush.*prop = value;
|
||||
m_preview->m_brush = ui::Canvas::I->m_current_brush;
|
||||
m_preview->draw_stroke();
|
||||
if (on_stroke_change)
|
||||
on_stroke_change(this);
|
||||
}
|
||||
53
src/node_panel_stroke.h
Normal file
53
src/node_panel_stroke.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "node_slider.h"
|
||||
#include "brush.h"
|
||||
#include "node_checkbox.h"
|
||||
#include "node_combobox.h"
|
||||
|
||||
class NodePanelStroke : public Node
|
||||
{
|
||||
public:
|
||||
NodeStrokePreview* m_preview;
|
||||
NodeComboBox* m_blend_mode;
|
||||
NodeSliderH* m_tip_size;
|
||||
NodeSliderH* m_tip_spacing;
|
||||
NodeSliderH* m_tip_flow;
|
||||
NodeSliderH* m_tip_opacity;
|
||||
NodeSliderH* m_tip_angle;
|
||||
NodeSliderH* m_tip_mix;
|
||||
NodeSliderH* m_tip_stencil;
|
||||
NodeSliderH* m_tip_wet;
|
||||
NodeSliderH* m_tip_noise;
|
||||
NodeSliderH* m_tip_hue;
|
||||
NodeSliderH* m_tip_sat;
|
||||
NodeSliderH* m_tip_val;
|
||||
NodeSliderH* m_jitter_scale;
|
||||
NodeSliderH* m_jitter_angle;
|
||||
NodeSliderH* m_jitter_spread;
|
||||
NodeSliderH* m_jitter_flow;
|
||||
NodeSliderH* m_jitter_hue;
|
||||
NodeSliderH* m_jitter_sat;
|
||||
NodeSliderH* m_jitter_val;
|
||||
NodeCheckBox* m_tip_angle_follow;
|
||||
NodeCheckBox* m_tip_flow_pressure;
|
||||
NodeCheckBox* m_tip_size_pressure;
|
||||
NodeCheckBox* m_tip_hue_pressure;
|
||||
NodeCheckBox* m_tip_sat_pressure;
|
||||
NodeCheckBox* m_tip_val_pressure;
|
||||
std::function<void(Node* target)> on_stroke_change;
|
||||
std::map<NodeSliderH*, std::function<float(float)>> m_curves;
|
||||
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
void update_controls();
|
||||
|
||||
void init_slider(NodeSliderH*& slider, const char* id, float ui::Brush::* prop);
|
||||
void handle_slide(float ui::Brush::* prop, Node* target, float value);
|
||||
|
||||
void init_checkbox(NodeCheckBox*& slider, const char* id, bool ui::Brush::* prop);
|
||||
void handle_checkbox(bool ui::Brush::* prop, Node* target, bool value);
|
||||
};
|
||||
52
src/node_popup_menu.cpp
Normal file
52
src/node_popup_menu.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_popup_menu.h"
|
||||
|
||||
Node* NodePopupMenu::clone_instantiate() const
|
||||
{
|
||||
return new NodePopupMenu();
|
||||
}
|
||||
|
||||
void NodePopupMenu::init()
|
||||
{
|
||||
m_flood_events = true;
|
||||
SetPosition(0, 0);
|
||||
SetWidth(100);
|
||||
SetHeight(500);
|
||||
SetPositioning(YGPositionTypeAbsolute);
|
||||
m_mouse_ignore = false;
|
||||
m_capture_children = false;
|
||||
}
|
||||
|
||||
kEventResult NodePopupMenu::handle_event(Event* e)
|
||||
{
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
if (!m_mouse_inside)
|
||||
{
|
||||
mouse_release();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < m_children.size(); i++)
|
||||
{
|
||||
if (m_children[i]->m_mouse_inside)
|
||||
{
|
||||
if (on_select)
|
||||
on_select(this, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mouse_release();
|
||||
}
|
||||
destroy();
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
11
src/node_popup_menu.h
Normal file
11
src/node_popup_menu.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
|
||||
class NodePopupMenu : public Node
|
||||
{
|
||||
public:
|
||||
std::function<void(Node* target, int index)> on_select;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
24
src/node_progress_bar.cpp
Normal file
24
src/node_progress_bar.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_progress_bar.h"
|
||||
#include "layout.h"
|
||||
|
||||
Node* NodeProgressBar::clone_instantiate() const
|
||||
{
|
||||
return new NodeProgressBar();
|
||||
}
|
||||
|
||||
void NodeProgressBar::init()
|
||||
{
|
||||
auto tpl = static_cast<const NodeBorder*>(init_template("progress-bar"));
|
||||
m_color = tpl->m_color;
|
||||
m_border_color = tpl->m_border_color;
|
||||
m_thinkness = tpl->m_thinkness;
|
||||
|
||||
m_title = find<NodeText>("title");
|
||||
btn_cancel = find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [&](Node*) { destroy(); };
|
||||
m_progress = find<NodeBorder>("progress");
|
||||
|
||||
m_progress->SetWidthP(10);
|
||||
}
|
||||
16
src/node_progress_bar.h
Normal file
16
src/node_progress_bar.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_button.h"
|
||||
#include "node_text.h"
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeProgressBar : public NodeBorder
|
||||
{
|
||||
public:
|
||||
Node* m_template;
|
||||
NodeButton* btn_cancel;
|
||||
NodeText* m_title;
|
||||
NodeBorder* m_progress;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
};
|
||||
69
src/node_scroll.cpp
Normal file
69
src/node_scroll.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_scroll.h"
|
||||
#include "event.h"
|
||||
|
||||
Node* NodeScroll::clone_instantiate() const
|
||||
{
|
||||
return new NodeScroll;
|
||||
}
|
||||
|
||||
void NodeScroll::fix_scroll()
|
||||
{
|
||||
auto pad = GetPadding();
|
||||
glm::vec2 padoff = { pad.y + pad.w, pad.x + pad.z };
|
||||
auto rect = get_children_rect();
|
||||
m_offset = glm::clamp(m_offset, - zw(rect) + zw(m_clip_uncut) - padoff, { 0, 0 });
|
||||
m_pos_offset_childred = m_offset;
|
||||
}
|
||||
|
||||
kEventResult NodeScroll::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
auto me = static_cast<MouseEvent*>(e);
|
||||
auto ge = static_cast<GestureEvent*>(e);
|
||||
auto loc = (me->m_pos - m_pos) * root()->m_zoom;
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
m_dragging = true;
|
||||
m_drag_start = me->m_pos;
|
||||
m_offset_start = m_offset;
|
||||
mouse_capture();
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (m_dragging)
|
||||
{
|
||||
m_offset = m_offset_start + (me->m_pos - m_drag_start) * m_mask;
|
||||
fix_scroll();
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
mouse_release();
|
||||
m_dragging = false;
|
||||
break;
|
||||
case kEventType::MouseScroll:
|
||||
m_offset += me->m_scroll_delta * 50;
|
||||
fix_scroll();
|
||||
break;
|
||||
case kEventType::GestureStart:
|
||||
m_offset_start = m_offset;
|
||||
mouse_capture();
|
||||
break;
|
||||
case kEventType::GestureMove:
|
||||
m_offset = m_offset_start + ge->m_pos_delta * m_mask;
|
||||
fix_scroll();
|
||||
break;
|
||||
case kEventType::GestureEnd:
|
||||
mouse_release();
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
mouse_release();
|
||||
m_dragging = false;
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
15
src/node_scroll.h
Normal file
15
src/node_scroll.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeScroll : public NodeBorder
|
||||
{
|
||||
bool m_dragging = false;
|
||||
glm::vec2 m_drag_start;
|
||||
glm::vec2 m_offset_start;
|
||||
glm::vec2 m_offset;
|
||||
glm::vec2 m_mask{ 0, 1 };
|
||||
public:
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
void fix_scroll();
|
||||
};
|
||||
26
src/node_settings.cpp
Normal file
26
src/node_settings.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_settings.h"
|
||||
#include "layout.h"
|
||||
|
||||
Node* NodeSettings::clone_instantiate() const
|
||||
{
|
||||
return new NodeSettings();
|
||||
}
|
||||
|
||||
void NodeSettings::init()
|
||||
{
|
||||
SetPosition(0, 0);
|
||||
SetWidthP(100);
|
||||
SetHeightP(100);
|
||||
SetPositioning(YGPositionTypeAbsolute);
|
||||
m_template = (*m_manager)[const_hash("settings")]->m_children[0]->clone();
|
||||
add_child(m_template);
|
||||
btnOk = m_template->find<NodeButton>("btn-ok");
|
||||
btnOk->on_click = [&](Node*) { destroy(); };
|
||||
}
|
||||
|
||||
kEventResult NodeSettings::handle_event(Event* e)
|
||||
{
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
13
src/node_settings.h
Normal file
13
src/node_settings.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "node_button.h"
|
||||
|
||||
class NodeSettings : public Node
|
||||
{
|
||||
Node* m_template;
|
||||
NodeButton* btnOk;
|
||||
public:
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void init() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
160
src/node_slider.cpp
Normal file
160
src/node_slider.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_slider.h"
|
||||
#include "shader.h"
|
||||
|
||||
Node* NodeSliderH::clone_instantiate() const
|
||||
{
|
||||
return new NodeSliderH();
|
||||
}
|
||||
|
||||
void NodeSliderH::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
NodeSliderH* n = static_cast<NodeSliderH*>(dest);
|
||||
n->m_value = m_value;
|
||||
}
|
||||
|
||||
void NodeSliderH::init()
|
||||
{
|
||||
SetPadding(1, 1, 1, 1);
|
||||
SetWidthP(100);
|
||||
SetHeightP(100);
|
||||
m_color = glm::vec4(1);
|
||||
}
|
||||
|
||||
void NodeSliderH::draw()
|
||||
{
|
||||
NodeBorder::draw();
|
||||
|
||||
using namespace ui;
|
||||
auto sz = GetSize();
|
||||
glm::vec2 cur_size = sz * (1.f - m_mask) + m_mask * glm::vec2(10);
|
||||
glm::mat4 scale = glm::scale(glm::vec3(cur_size, 1.f));
|
||||
glm::mat4 pos = glm::translate(glm::vec3(m_value * m_mask * sz + m_pos + sz * .5f * (1.f - m_mask), 0));
|
||||
auto mvp = m_proj * pos * scale;
|
||||
|
||||
ui::ShaderManager::use(kShader::Color);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
m_plane.draw_fill();
|
||||
}
|
||||
|
||||
void NodeSliderH::set_value(float value)
|
||||
{
|
||||
m_value = glm::vec2(value) * m_mask;
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, glm::length(m_value));
|
||||
}
|
||||
|
||||
float NodeSliderH::get_value()
|
||||
{
|
||||
return glm::length(m_value * m_mask);
|
||||
}
|
||||
|
||||
void NodeSliderH::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
NodeBorder::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Value:
|
||||
m_value = glm::vec2(attr->FloatValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kEventResult NodeSliderH::handle_event(Event* e)
|
||||
{
|
||||
NodeBorder::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
dragging = true;
|
||||
mouse_capture();
|
||||
{
|
||||
m_old_value = m_value;
|
||||
auto sz = GetSize();
|
||||
auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask;
|
||||
m_value = pos / glm::max({ 1, 1 }, sz);
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, glm::length(m_value));
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
mouse_release();
|
||||
dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (dragging)
|
||||
{
|
||||
auto sz = GetSize();
|
||||
auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask;
|
||||
m_value = pos / glm::max({ 1, 1 }, sz);
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, glm::length(m_value * m_mask));
|
||||
}
|
||||
break;
|
||||
case kEventType::MouseCancel:
|
||||
mouse_release();
|
||||
if (dragging)
|
||||
{
|
||||
m_value = m_old_value;
|
||||
set_value(glm::length(m_value));
|
||||
if (on_value_changed)
|
||||
on_value_changed(this, glm::length(m_value * m_mask));
|
||||
}
|
||||
dragging = false;
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
Node* NodeSliderHue::clone_instantiate() const
|
||||
{
|
||||
return new NodeSliderHue();
|
||||
}
|
||||
|
||||
void NodeSliderHue::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeSliderV::clone_finalize(dest);
|
||||
NodeSliderHue* n = static_cast<NodeSliderHue*>(dest);
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeSliderHue::init()
|
||||
{
|
||||
NodeSliderV::init();
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeSliderHue::init_controls()
|
||||
{
|
||||
on_value_changed = [this](Node*, float value) {
|
||||
m_color = glm::vec4(convert_hsv2rgb({ value, 1, 1 }), 1);
|
||||
if (on_hue_changed)
|
||||
on_hue_changed(this, m_color);
|
||||
};
|
||||
}
|
||||
|
||||
glm::vec4 NodeSliderHue::get_hue()
|
||||
{
|
||||
m_color = glm::vec4(convert_hsv2rgb({ glm::length(m_value * m_mask), 1, 1 }), 1);
|
||||
return m_color;
|
||||
}
|
||||
|
||||
void NodeSliderHue::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
ui::ShaderManager::use(kShader::ColorHue);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
//ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Direction, 1); // set vertical
|
||||
m_plane.draw_fill();
|
||||
NodeBorder::m_color = glm::vec4(0);
|
||||
NodeSliderH::draw();
|
||||
}
|
||||
40
src/node_slider.h
Normal file
40
src/node_slider.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
|
||||
class NodeSliderH : public NodeBorder
|
||||
{
|
||||
bool dragging = false;
|
||||
public:
|
||||
glm::vec2 m_mask{ 1, 0 };
|
||||
glm::vec2 m_value{0};
|
||||
glm::vec2 m_old_value;
|
||||
std::function<void(Node* target, float value)> on_value_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void draw() override;
|
||||
void set_value(float value);
|
||||
float get_value();
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
|
||||
class NodeSliderV : public NodeSliderH
|
||||
{
|
||||
public:
|
||||
virtual Node* clone_instantiate() const override { return new NodeSliderV(); }
|
||||
NodeSliderV() { m_mask = { 0, 1 }; }
|
||||
};
|
||||
|
||||
class NodeSliderHue : public NodeSliderV
|
||||
{
|
||||
public:
|
||||
glm::vec4 m_color;
|
||||
std::function<void(Node* target, glm::vec4 color)> on_hue_changed;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
void init_controls();
|
||||
glm::vec4 get_hue();
|
||||
virtual void draw() override;
|
||||
};
|
||||
154
src/node_stroke_preview.cpp
Normal file
154
src/node_stroke_preview.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "bezier.h"
|
||||
#include "canvas.h"
|
||||
|
||||
Node* NodeStrokePreview::clone_instantiate() const
|
||||
{
|
||||
return new NodeStrokePreview();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
}
|
||||
|
||||
void NodeStrokePreview::clone_children(Node* dest) const
|
||||
{
|
||||
// stop children cloning
|
||||
}
|
||||
|
||||
void NodeStrokePreview::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeStrokePreview* n = (NodeStrokePreview*)dest;
|
||||
n->init_controls();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::init_controls()
|
||||
{
|
||||
m_mesh.create();
|
||||
m_sampler.create(GL_LINEAR, GL_REPEAT);
|
||||
m_sampler_brush.create();
|
||||
m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
|
||||
// TextureManager::load("data/thumbs/Round-Hard.png");
|
||||
// ui::Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
|
||||
}
|
||||
|
||||
void NodeStrokePreview::restore_context()
|
||||
{
|
||||
NodeBorder::restore_context();
|
||||
init_controls();
|
||||
if (m_size.x > 0 && m_size.y > 0)
|
||||
m_rtt.create(m_size.x, m_size.y);
|
||||
draw_stroke();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::clear_context()
|
||||
{
|
||||
NodeBorder::clear_context();
|
||||
m_rtt.destroy();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::draw_stroke()
|
||||
{
|
||||
m_rtt.bindFramebuffer();
|
||||
{
|
||||
using namespace ui;
|
||||
GLint vp[4];
|
||||
GLfloat cc[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
|
||||
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
|
||||
glEnable(GL_BLEND);
|
||||
glm::mat4 proj = glm::ortho<float>(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1);
|
||||
|
||||
auto b = m_brush;
|
||||
m_stroke.reset();
|
||||
m_stroke.start(b);
|
||||
if (!m_stroke.m_keypoints.empty())
|
||||
m_stroke.m_prev_sample.origin = m_stroke.m_keypoints[0].pos;
|
||||
auto samples = m_stroke.compute_samples();
|
||||
auto& tex = TextureManager::get(b.m_tex_id);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
tex.bind();
|
||||
m_sampler_brush.bind(0);
|
||||
|
||||
auto& stencil = TextureManager::get(const_hash("data/paper.jpg"));
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
stencil.bind();
|
||||
m_sampler.bind(1);
|
||||
|
||||
if (true)
|
||||
{
|
||||
ShaderManager::use(kShader::BrushStroke);
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ShaderManager::u_int(kShaderUniform::TexStencil, 1); // stencil
|
||||
ShaderManager::u_vec2(kShaderUniform::Resolution, { m_rtt.getWidth(), m_rtt.getHeight() });
|
||||
ShaderManager::u_vec2(kShaderUniform::StencilOffset, glm::vec2(0));
|
||||
ShaderManager::u_float(kShaderUniform::StencilAlpha, b.m_tip_stencil);
|
||||
m_mesh.draw(samples, proj);
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// ShaderManager::use("stroke");
|
||||
// ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color);
|
||||
// ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
// for (const auto& s : samples)
|
||||
// {
|
||||
// auto mvp = proj *
|
||||
// glm::translate(glm::vec3(s.pos, 0)) *
|
||||
// glm::scale(glm::vec3(s.size, s.size, 1)) *
|
||||
// glm::eulerAngleZ(s.angle);
|
||||
// ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
|
||||
// ShaderManager::u_float(kShaderUniform::Alpha, s.flow);
|
||||
// m_plane.draw_fill();
|
||||
// }
|
||||
//}
|
||||
|
||||
m_sampler_brush.unbind();
|
||||
m_sampler.unbind();
|
||||
tex.unbind();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
}
|
||||
m_rtt.unbindFramebuffer();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
ui::ShaderManager::use(kShader::Texture);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
m_rtt.bindTexture();
|
||||
m_sampler.bind(0);
|
||||
m_plane.draw_fill();
|
||||
m_sampler.unbind();
|
||||
m_rtt.unbindTexture();
|
||||
}
|
||||
|
||||
void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size)
|
||||
{
|
||||
float pad = 30.f;
|
||||
new_size *= root()->m_zoom;
|
||||
float w = new_size.x;
|
||||
float h = new_size.y;
|
||||
std::vector<glm::vec2> kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } };
|
||||
m_stroke.reset();
|
||||
m_stroke.start(m_brush);
|
||||
for (int i = 0; i < 20; i++)
|
||||
m_stroke.add_point(BezierCurve::Bezier2D(kp, i / 20.f), 1.f);
|
||||
|
||||
m_rtt.destroy();
|
||||
m_rtt.create((int)new_size.x, (int)new_size.y);
|
||||
draw_stroke();
|
||||
}
|
||||
27
src/node_stroke_preview.h
Normal file
27
src/node_stroke_preview.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "rtt.h"
|
||||
#include "brush.h"
|
||||
#include "texture.h"
|
||||
|
||||
class NodeStrokePreview : public NodeBorder
|
||||
{
|
||||
RTT m_rtt;
|
||||
Sampler m_sampler;
|
||||
Sampler m_sampler_brush;
|
||||
ui::BrushMesh m_mesh;
|
||||
public:
|
||||
ui::Brush m_brush;
|
||||
ui::Stroke m_stroke;
|
||||
std::vector<glm::vec2> m_bez_points;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void clone_children(Node* dest) const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
void init_controls();
|
||||
virtual void restore_context() override;
|
||||
virtual void clear_context() override;
|
||||
void draw_stroke();
|
||||
virtual void draw() override;
|
||||
virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override;
|
||||
};
|
||||
99
src/node_text.cpp
Normal file
99
src/node_text.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_text.h"
|
||||
#include "shader.h"
|
||||
|
||||
Node* NodeText::clone_instantiate() const
|
||||
{
|
||||
return new NodeText();
|
||||
}
|
||||
|
||||
void NodeText::clone_copy(Node* dest) const
|
||||
{
|
||||
Node::clone_copy(dest);
|
||||
NodeText* n = static_cast<NodeText*>(dest);
|
||||
n->m_text_mesh.create();
|
||||
n->m_text_mesh.update(font_id, m_text.c_str());
|
||||
n->m_text = m_text;
|
||||
n->m_font = m_font;
|
||||
n->m_color = m_color;
|
||||
n->m_font_size = m_font_size;
|
||||
n->font_id = font_id;
|
||||
}
|
||||
|
||||
void NodeText::create()
|
||||
{
|
||||
if (!m_font.empty())
|
||||
{
|
||||
char font[64];
|
||||
sprintf(font, "%s-%d", m_font.c_str(), m_font_size);
|
||||
font_id = (kFont)const_hash(font);
|
||||
m_text_mesh.create();
|
||||
m_text_mesh.update(font_id, m_text.c_str());
|
||||
SetSize(m_text_mesh.bb);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeText::set_font(kFont fontID)
|
||||
{
|
||||
font_id = fontID;
|
||||
m_text_mesh.create();
|
||||
m_text_mesh.update(font_id, m_text.c_str());
|
||||
SetSize(m_text_mesh.bb);
|
||||
}
|
||||
|
||||
void NodeText::restore_context()
|
||||
{
|
||||
Node::restore_context();
|
||||
create();
|
||||
}
|
||||
|
||||
void NodeText::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
Node::parse_attributes(ka, attr);
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Text:
|
||||
m_text = attr->Value();
|
||||
break;
|
||||
case kAttribute::FontFace:
|
||||
m_font = attr->Value();
|
||||
break;
|
||||
case kAttribute::FontSize:
|
||||
m_font_size = attr->IntValue();
|
||||
break;
|
||||
case kAttribute::Color:
|
||||
{
|
||||
glm::vec4 pad;
|
||||
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
|
||||
if (n == 1)
|
||||
m_color = glm::vec4(pad.x);
|
||||
else
|
||||
m_color = pad;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeText::set_text(const char* s)
|
||||
{
|
||||
m_text = s;
|
||||
m_text_mesh.update(font_id, s);
|
||||
SetSize(m_text_mesh.bb);
|
||||
}
|
||||
|
||||
void NodeText::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
glm::mat4 pos = glm::translate(glm::vec3(glm::floor(m_pos), 0));
|
||||
m_mvp = m_proj * pos;
|
||||
ui::ShaderManager::use(kShader::Font);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
|
||||
ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color);
|
||||
glEnable(GL_BLEND);
|
||||
m_text_mesh.draw();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
22
src/node_text.h
Normal file
22
src/node_text.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "font.h"
|
||||
|
||||
class NodeText : public Node
|
||||
{
|
||||
public:
|
||||
TextMesh m_text_mesh;
|
||||
std::string m_text;
|
||||
std::string m_font;
|
||||
glm::vec4 m_color{ 1, 1, 1, 1 };
|
||||
int m_font_size;
|
||||
kFont font_id;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void create() override;
|
||||
virtual void restore_context() override;
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
void set_text(const char* s);
|
||||
void set_font(kFont fontID);
|
||||
virtual void draw() override;
|
||||
};
|
||||
102
src/node_text_input.cpp
Normal file
102
src/node_text_input.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "log.h"
|
||||
#include "node_text_input.h"
|
||||
|
||||
Node* NodeTextInput::clone_instantiate() const
|
||||
{
|
||||
return new NodeTextInput();
|
||||
}
|
||||
|
||||
void NodeTextInput::clone_finalize(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
NodeTextInput* n = static_cast<NodeTextInput*>(dest);
|
||||
if (n->m_children.size())
|
||||
{
|
||||
auto t = n->m_children[0];
|
||||
n->m_text = (NodeText*)t.get();
|
||||
}
|
||||
n->m_string = m_string;
|
||||
}
|
||||
|
||||
void NodeTextInput::init()
|
||||
{
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeTextInput::init_controls()
|
||||
{
|
||||
m_text = new NodeText;
|
||||
add_child(m_text);
|
||||
m_text->m_font = "arial";
|
||||
m_text->m_font_size = 11;
|
||||
m_text->m_text = "TextInput";
|
||||
m_text->create();
|
||||
m_string = "TextInput";
|
||||
}
|
||||
|
||||
kEventResult NodeTextInput::handle_event(Event* e)
|
||||
{
|
||||
KeyEvent* ke = (KeyEvent*)e;
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
App::I.showKeyboard();
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
key_capture();
|
||||
break;
|
||||
case kEventType::KeyDown:
|
||||
//switch (ke->m_key)
|
||||
//{
|
||||
//case VK_BACK:
|
||||
// m_string.erase(m_string.end() - 1);
|
||||
// m_text->set_text(m_string.c_str());
|
||||
// break;
|
||||
//default:
|
||||
// break;
|
||||
//}
|
||||
break;
|
||||
case kEventType::KeyChar:
|
||||
if (ke->m_char == '\b') // backspace
|
||||
{
|
||||
if (!m_string.empty())
|
||||
{
|
||||
m_string.erase(m_string.end() - 1);
|
||||
m_text->set_text(m_string.c_str());
|
||||
}
|
||||
}
|
||||
else if (ke->m_char == 0x7f) // DEL
|
||||
{
|
||||
if (!m_string.empty())
|
||||
{
|
||||
m_string.erase(m_string.end() - 1);
|
||||
m_text->set_text(m_string.c_str());
|
||||
}
|
||||
}
|
||||
else if (ke->m_char == '\n' || ke->m_char == '\r') // enter/return
|
||||
{
|
||||
if (on_return)
|
||||
on_return(this);
|
||||
}
|
||||
else if (ke->m_char >= 32 && ke->m_char < (32 + 96))
|
||||
{
|
||||
m_string += (char)ke->m_char;
|
||||
m_text->set_text(m_string.c_str());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return kEventResult::Available;
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
|
||||
void NodeTextInput::set_text(const std::string& s)
|
||||
{
|
||||
if (m_text)
|
||||
m_text->set_text(s.c_str());
|
||||
m_string = s;
|
||||
}
|
||||
|
||||
17
src/node_text_input.h
Normal file
17
src/node_text_input.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_text.h"
|
||||
|
||||
class NodeTextInput : public NodeBorder
|
||||
{
|
||||
public:
|
||||
NodeText* m_text;
|
||||
std::string m_string;
|
||||
std::function<void(NodeTextInput*target)> on_return;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
void init_controls();
|
||||
void set_text(const std::string& s);
|
||||
};
|
||||
73
src/node_viewport.cpp
Normal file
73
src/node_viewport.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "pch.h"
|
||||
#include "log.h"
|
||||
#include "node_viewport.h"
|
||||
#include "shader.h"
|
||||
|
||||
void NodeViewport::draw()
|
||||
{
|
||||
using namespace ui;
|
||||
glm::mat4 cam = glm::lookAt(glm::vec3(sinf(angle) * 10, 0, -10), glm::vec3(0, 0, 0), glm::vec3(0, -1, 0));
|
||||
glm::mat4 proj = glm::perspective<float>(glm::radians(45.f), m_clip.z / m_clip.w, .1f, 100);
|
||||
|
||||
GLint vp[4];
|
||||
GLfloat cc[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
|
||||
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
auto box = m_clip * root()->m_zoom;
|
||||
glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w);
|
||||
glViewport(c.x, c.y, c.z, c.w);
|
||||
TextureManager::get(m_tex_id).bind();
|
||||
m_sampler->bind(0);
|
||||
glEnable(GL_BLEND);
|
||||
ui::ShaderManager::use(kShader::Texture);
|
||||
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * cam);
|
||||
m_faces->draw_fill();
|
||||
m_sampler->unbind();
|
||||
TextureManager::get(m_tex_id).unbind();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
}
|
||||
Node* NodeViewport::clone_instantiate() const
|
||||
{
|
||||
return new NodeViewport;
|
||||
}
|
||||
void NodeViewport::create()
|
||||
{
|
||||
m_faces = std::make_unique<ui::Plane>();
|
||||
m_faces->create<1>(10, 10);
|
||||
m_sampler = std::make_unique<Sampler>();
|
||||
m_sampler->create();
|
||||
TextureManager::load("data/uvs.jpg");
|
||||
m_tex_id = const_hash("data/uvs.jpg");
|
||||
}
|
||||
kEventResult NodeViewport::handle_event(Event* e)
|
||||
{
|
||||
Node::handle_event(e);
|
||||
switch (e->m_type)
|
||||
{
|
||||
case kEventType::MouseDownL:
|
||||
dragging = true;
|
||||
drag_end = drag_start = ((MouseEvent*)e)->m_pos;
|
||||
angle_old = angle;
|
||||
break;
|
||||
case kEventType::MouseUpL:
|
||||
dragging = false;
|
||||
break;
|
||||
case kEventType::MouseMove:
|
||||
if (dragging)
|
||||
{
|
||||
drag_end = ((MouseEvent*)e)->m_pos;
|
||||
angle = angle_old + (drag_end - drag_start).x * .01f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return kEventResult::Consumed;
|
||||
}
|
||||
23
src/node_viewport.h
Normal file
23
src/node_viewport.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "node.h"
|
||||
#include "shape.h"
|
||||
#include "texture.h"
|
||||
#include "event.h"
|
||||
|
||||
class NodeViewport : public Node
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ui::Plane> m_faces;
|
||||
std::unique_ptr<Sampler> m_sampler;
|
||||
uint16_t m_tex_id;
|
||||
glm::vec2 drag_start;
|
||||
glm::vec2 drag_end;
|
||||
bool dragging = false;
|
||||
float angle = 0.0f;
|
||||
float angle_old;
|
||||
|
||||
virtual void draw() override;
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void create() override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
};
|
||||
36
src/objc_utils.cpp
Normal file
36
src/objc_utils.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "pch.h"
|
||||
#include "objc_utils.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
|
||||
@implementation PathWithModDate
|
||||
@end
|
||||
|
||||
@implementation ObjcUtils
|
||||
+ (NSArray*)getFilesAtPathSortedByModificationDate:(NSString*)folderPath
|
||||
{
|
||||
NSArray *allPaths = [NSFileManager.defaultManager contentsOfDirectoryAtPath:folderPath error:nil];
|
||||
|
||||
NSMutableArray *sortedPaths = [NSMutableArray new];
|
||||
for (NSString *path in allPaths) {
|
||||
NSString *fullPath = [folderPath stringByAppendingPathComponent:path];
|
||||
|
||||
NSDictionary *attr = [NSFileManager.defaultManager attributesOfItemAtPath:fullPath error:nil];
|
||||
NSDate *modDate = [attr objectForKey:NSFileModificationDate];
|
||||
|
||||
PathWithModDate *pathWithDate = [[PathWithModDate alloc] init];
|
||||
pathWithDate.path = fullPath;
|
||||
pathWithDate.modDate = modDate;
|
||||
[sortedPaths addObject:pathWithDate];
|
||||
}
|
||||
|
||||
[sortedPaths sortUsingComparator:^(PathWithModDate *path1, PathWithModDate *path2) {
|
||||
// Descending (most recently modified first)
|
||||
return [path2.modDate compare:path1.modDate];
|
||||
}];
|
||||
|
||||
return sortedPaths;
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
15
src/objc_utils.h
Normal file
15
src/objc_utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
|
||||
@interface PathWithModDate : NSObject
|
||||
@property (strong) NSString *path;
|
||||
@property (strong) NSDate *modDate;
|
||||
@end
|
||||
|
||||
@interface ObjcUtils : NSObject
|
||||
@end
|
||||
|
||||
|
||||
#endif
|
||||
19
src/pch.cpp
Normal file
19
src/pch.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "pch.h"
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <stb/stb_image.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#pragma comment (lib, "libcurl_debug.lib")
|
||||
#else
|
||||
#pragma comment (lib, "libcurl.lib")
|
||||
#endif // DEBUG
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "BugTrapU-x64.lib")
|
||||
#pragma comment(lib, "shell32.lib")
|
||||
#endif // _WIN32
|
||||
132
src/pch.h
Normal file
132
src/pch.h
Normal file
@@ -0,0 +1,132 @@
|
||||
//#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#define USE_VBO 1
|
||||
#define USE_SAMPLER 1
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
|
||||
#define TARGET_OS_IOS 1
|
||||
#define __IOS__ 1
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <ZipArchive/ZipArchive.h>
|
||||
#endif
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#include <OpenGLES/ES3/glext.h>
|
||||
#define SHADER_VERSION "#version 300 es\n"
|
||||
#elif TARGET_OS_IPHONE
|
||||
#define TARGET_OS_IOS 1
|
||||
#define __IOS__ 1
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <ZipArchive/ZipArchive.h>
|
||||
#endif
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#include <OpenGLES/ES3/glext.h>
|
||||
#define SHADER_VERSION "#version 300 es\n"
|
||||
#else
|
||||
#define TARGET_OS_OSX 1
|
||||
#define __OSX__ 1
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <ZipArchive/ZipArchive.h>
|
||||
#endif
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <OpenGL/gl3ext.h>
|
||||
#define SHADER_VERSION "#version 150\n"
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#elif __ANDROID__
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android/log.h>
|
||||
#include <android_native_app_glue.h>
|
||||
|
||||
#define SHADER_VERSION "#version 300 es\n"
|
||||
|
||||
#elif _WIN32
|
||||
#define _USE_MATH_DEFINES
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define _SCL_SECURE_NO_WARNINGS
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <tchar.h>
|
||||
#include <gl\glew.h>
|
||||
#include <gl\wglew.h>
|
||||
#include <gl\GL.h>
|
||||
#include <BugTrap.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#define SHADER_VERSION "#version 150\n"
|
||||
|
||||
#endif
|
||||
|
||||
#define NS_START namespace ui {
|
||||
#define NS_END }
|
||||
#define SIXPLETTE(I) {I, I, I, I, I, I}
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <stack>
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <condition_variable>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
//#define GLM_FORCE_SWIZZLE
|
||||
//#define GLM_FORCE_MESSAGES
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <glm/gtx/intersect.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include <jpge.h>
|
||||
#include <jpgd.h>
|
||||
#include <base64.h>
|
||||
#endif
|
||||
|
||||
#include <yoga/Yoga.h>
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <stb/stb_image.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
#include <curl/curl.h>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user