move texed to its own repo
parent
a059b9826b
commit
4ea2ba6188
BIN
art/dirt.ecotex
BIN
art/dirt.ecotex
Binary file not shown.
1234
art/gen/dirt.h
1234
art/gen/dirt.h
File diff suppressed because it is too large
Load Diff
BIN
art/gen/dirt.png
BIN
art/gen/dirt.png
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
BIN
art/rock.ecotex
BIN
art/rock.ecotex
Binary file not shown.
|
@ -19,7 +19,6 @@ add_executable(eco2d
|
|||
source/signal_handling.c
|
||||
source/profiler.c
|
||||
source/debug_ui.c
|
||||
source/editors/texed.c
|
||||
|
||||
source/utils/options.c
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
void texed_run(int,char**);
|
|
@ -1,520 +0,0 @@
|
|||
#define ZPL_NO_WINDOWS_H
|
||||
#include "zpl.h"
|
||||
|
||||
#include "editors/texed.h"
|
||||
#include "raylib.h"
|
||||
#include "utils/raylib_helpers.h"
|
||||
#include "cwpack/cwpack.h"
|
||||
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
#define RAYGUI_SUPPORT_ICONS
|
||||
#include "raygui.h"
|
||||
|
||||
#define GUI_FILE_DIALOG_IMPLEMENTATION
|
||||
#include "gui_file_dialog.h"
|
||||
|
||||
#define GUI_TEXTBOX_EXTENDED_IMPLEMENTATION
|
||||
#include "gui_textbox_extended.h"
|
||||
|
||||
#define TD_DEFAULT_IMG_WIDTH 64
|
||||
#define TD_DEFAULT_IMG_HEIGHT 64
|
||||
#define TD_UI_PADDING 5.0f
|
||||
#define TD_UI_PREVIEW_BORDER 4.0f
|
||||
#define TD_UI_DEFAULT_ZOOM 4.0f
|
||||
#define TD_IMAGES_MAX_STACK 128
|
||||
|
||||
static uint16_t screenWidth = 1280;
|
||||
static uint16_t screenHeight = 720;
|
||||
static float zoom = TD_UI_DEFAULT_ZOOM;
|
||||
static float old_zoom = TD_UI_DEFAULT_ZOOM;
|
||||
static Texture2D checker_tex;
|
||||
static uint16_t old_screen_w;
|
||||
static uint16_t old_screen_h;
|
||||
static bool is_repaint_locked = false;
|
||||
static int render_tiles = 0;
|
||||
|
||||
typedef enum {
|
||||
TPARAM_FLOAT,
|
||||
TPARAM_COORD,
|
||||
TPARAM_INT,
|
||||
TPARAM_COLOR,
|
||||
TPARAM_STRING,
|
||||
TPARAM_SLIDER,
|
||||
|
||||
TPARAM_FORCE_UINT8 = UINT8_MAX
|
||||
} td_param_kind;
|
||||
|
||||
typedef struct {
|
||||
td_param_kind kind;
|
||||
char const *name;
|
||||
char str[1000];
|
||||
bool edit_mode;
|
||||
|
||||
union {
|
||||
struct {
|
||||
float flt, old_flt;
|
||||
};
|
||||
uint32_t u32;
|
||||
int32_t i32;
|
||||
Color color;
|
||||
char copy[4];
|
||||
};
|
||||
} td_param;
|
||||
|
||||
typedef enum {
|
||||
TCAT_STACK,
|
||||
TCAT_GEN,
|
||||
TCAT_DRAW,
|
||||
TCAT_MOD,
|
||||
|
||||
TCAT_FORCE_UINT8 = UINT8_MAX
|
||||
} tcat_kind;
|
||||
|
||||
typedef struct {
|
||||
tcat_kind kind;
|
||||
char const *icon;
|
||||
Color color;
|
||||
} tcat_desc;
|
||||
|
||||
typedef enum {
|
||||
TOP_NEW_IMAGE,
|
||||
TOP_DRAW_RECT,
|
||||
TOP_DRAW_LINE,
|
||||
TOP_DITHER,
|
||||
TOP_DRAW_IMAGE,
|
||||
TOP_DRAW_TEXT,
|
||||
TOP_RESIZE_IMAGE,
|
||||
TOP_COLOR_TWEAKS,
|
||||
TOP_FLIP_IMAGE,
|
||||
TOP_ROTATE_IMAGE,
|
||||
|
||||
TOP_PUSH_IMAGE,
|
||||
TOP_POP_IMAGE,
|
||||
|
||||
TOP_IMAGE_GRAD_V,
|
||||
TOP_IMAGE_GRAD_H,
|
||||
TOP_IMAGE_GRAD_RAD,
|
||||
TOP_IMAGE_CHECKED,
|
||||
TOP_IMAGE_NOISE_WHITE,
|
||||
TOP_IMAGE_NOISE_PERLIN,
|
||||
TOP_IMAGE_CELLULAR,
|
||||
|
||||
TOP_COLOR_REPLACE,
|
||||
|
||||
TOP_IMAGE_ALPHA_MASK,
|
||||
TOP_IMAGE_ALPHA_MASK_CLEAR,
|
||||
|
||||
TOP_FORCE_UINT8 = UINT8_MAX
|
||||
} td_op_kind;
|
||||
|
||||
typedef struct {
|
||||
td_op_kind kind;
|
||||
char const *name;
|
||||
tcat_kind cat;
|
||||
bool is_hidden;
|
||||
bool is_locked;
|
||||
|
||||
uint8_t num_params;
|
||||
td_param *params;
|
||||
} td_op;
|
||||
|
||||
#define OP(n) .kind = n, .name = #n
|
||||
|
||||
typedef struct {
|
||||
bool visible;
|
||||
char const *title;
|
||||
char const *message;
|
||||
char const *buttons;
|
||||
int result;
|
||||
} td_msgbox;
|
||||
|
||||
typedef struct {
|
||||
char *filepath;
|
||||
int32_t img_pos;
|
||||
Image img[TD_IMAGES_MAX_STACK];
|
||||
Texture2D tex;
|
||||
GuiFileDialogState fileDialog;
|
||||
td_msgbox msgbox;
|
||||
bool is_saved;
|
||||
|
||||
td_op *ops; //< zpl_array
|
||||
int selected_op;
|
||||
} td_ctx;
|
||||
|
||||
static td_ctx ctx = {0};
|
||||
|
||||
static char filename[200];
|
||||
|
||||
#include "texed_ops_list.c"
|
||||
|
||||
void texed_new(int w, int h);
|
||||
void texed_clear(void);
|
||||
void texed_destroy(void);
|
||||
void texed_load(void);
|
||||
void texed_save(void);
|
||||
void texed_export_cc(char const *path);
|
||||
void texed_export_png(char const *path);
|
||||
void texed_repaint_preview(void);
|
||||
void texed_compose_image(void);
|
||||
void texed_msgbox_init(char const *title, char const *message, char const *buttons);
|
||||
void texed_process_ops(void);
|
||||
void texed_process_params(void);
|
||||
|
||||
void texed_img_push(int w, int h, Color color);
|
||||
void texed_img_pop(int x, int y, int w, int h, Color tint);
|
||||
|
||||
void texed_add_op(int kind);
|
||||
void texed_rem_op(int idx);
|
||||
void texed_swp_op(int idx, int idx2);
|
||||
int texed_find_op(int kind);
|
||||
|
||||
void texed_draw_oplist_pane(zpl_aabb2 r);
|
||||
void texed_draw_props_pane(zpl_aabb2 r);
|
||||
void texed_draw_topbar(zpl_aabb2 r);
|
||||
void texed_draw_msgbox(zpl_aabb2 r);
|
||||
|
||||
static inline
|
||||
void DrawAABB(zpl_aabb2 rect, Color color) {
|
||||
DrawRectangleEco(rect.min.x, rect.min.y,
|
||||
rect.max.x-rect.min.x,
|
||||
rect.max.y-rect.min.y,
|
||||
color);
|
||||
}
|
||||
|
||||
static inline
|
||||
Rectangle aabb2_ray(zpl_aabb2 r) {
|
||||
return (Rectangle) {
|
||||
.x = r.min.x,
|
||||
.y = r.min.y,
|
||||
.width = r.max.x-r.min.x,
|
||||
.height = r.max.y-r.min.y
|
||||
};
|
||||
}
|
||||
|
||||
#include "texed_ops.c"
|
||||
#include "texed_prj.c"
|
||||
#include "texed_widgets.c"
|
||||
|
||||
void texed_run(int argc, char **argv) {
|
||||
zpl_opts opts={0};
|
||||
zpl_opts_init(&opts, zpl_heap(), argv[0]);
|
||||
zpl_opts_add(&opts, "td", "texed", "run texture editor", ZPL_OPTS_FLAG);
|
||||
zpl_opts_add(&opts, "td-i", "texed-import", "convert an image to ecotex format", ZPL_OPTS_STRING);
|
||||
zpl_opts_add(&opts, "td-ec", "texed-export-cc", "export ecotex image to C header file", ZPL_OPTS_STRING);
|
||||
zpl_opts_add(&opts, "td-ep", "texed-export-png", "export ecotex image to PNG format", ZPL_OPTS_STRING);
|
||||
uint32_t ok = zpl_opts_compile(&opts, argc, argv);
|
||||
|
||||
if (!ok) {
|
||||
zpl_opts_print_errors(&opts);
|
||||
zpl_opts_print_help(&opts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (zpl_opts_has_arg(&opts, "texed-import")) {
|
||||
zpl_string path = zpl_opts_string(&opts, "texed-import", "");
|
||||
if (FileExists(zpl_bprintf("art/%s", path)) && IsFileExtension(path, ".png")) {
|
||||
Image orig = LoadImage(zpl_bprintf("art/%s", path));
|
||||
texed_new(orig.width, orig.height);
|
||||
is_repaint_locked = true;
|
||||
texed_add_op(TOP_DRAW_IMAGE);
|
||||
td_param *params = ctx.ops[1].params;
|
||||
zpl_strcpy(params[0].str, path);
|
||||
is_repaint_locked = false;
|
||||
texed_compose_image();
|
||||
zpl_strcpy(filename, zpl_bprintf("%s.ecotex", GetFileNameWithoutExt(path)));
|
||||
ctx.filepath = filename;
|
||||
texed_save();
|
||||
} else {
|
||||
zpl_printf("%s\n", "provided file does not exist!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (zpl_opts_has_arg(&opts, "texed-export-cc")) {
|
||||
zpl_string path = zpl_opts_string(&opts, "texed-export-cc", "");
|
||||
if (FileExists(zpl_bprintf("art/%s.ecotex", path))) {
|
||||
zpl_array_init(ctx.ops, zpl_heap());
|
||||
zpl_strcpy(filename, zpl_bprintf("%s.ecotex", path));
|
||||
ctx.filepath = filename;
|
||||
texed_load();
|
||||
texed_export_cc(path);
|
||||
} else {
|
||||
zpl_printf("%s\n", "provided file does not exist!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (zpl_opts_has_arg(&opts, "texed-export-png")) {
|
||||
zpl_string path = zpl_opts_string(&opts, "texed-export-png", "");
|
||||
if (FileExists(zpl_bprintf("art/%s.ecotex", path))) {
|
||||
zpl_array_init(ctx.ops, zpl_heap());
|
||||
zpl_strcpy(filename, zpl_bprintf("%s.ecotex", path));
|
||||
ctx.filepath = filename;
|
||||
texed_load();
|
||||
texed_export_png(path);
|
||||
} else {
|
||||
zpl_printf("%s\n", "provided file does not exist!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "eco2d - texture editor");
|
||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
||||
SetTargetFPS(60);
|
||||
|
||||
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
|
||||
|
||||
while (1) {
|
||||
zpl_aabb2 screen = {
|
||||
.min = (zpl_vec2) {.x = 0.0f, .y = 0.0f},
|
||||
.max = (zpl_vec2) {.x = GetScreenWidth(), .y = GetScreenHeight()},
|
||||
};
|
||||
zpl_aabb2 orig_screen = screen;
|
||||
|
||||
zpl_aabb2 topbar = zpl_aabb2_cut_top(&screen, 20.0f);
|
||||
zpl_aabb2 oplist_pane = zpl_aabb2_cut_right(&screen, screenWidth / 2.0f);
|
||||
zpl_aabb2 property_pane = zpl_aabb2_cut_bottom(&screen, screenHeight / 2.0f);
|
||||
zpl_aabb2 preview_window = screen;
|
||||
|
||||
// NOTE(zaklaus): contract all panes for a clean UI separation
|
||||
oplist_pane = zpl_aabb2_contract(&oplist_pane, TD_UI_PADDING);
|
||||
preview_window = zpl_aabb2_contract(&preview_window, TD_UI_PADDING);
|
||||
property_pane = zpl_aabb2_contract(&property_pane, TD_UI_PADDING);
|
||||
|
||||
Rectangle preview_rect = aabb2_ray(preview_window);
|
||||
|
||||
if (old_screen_w != GetScreenWidth() || old_screen_h != GetScreenHeight()) {
|
||||
old_screen_w = GetScreenWidth();
|
||||
old_screen_h = GetScreenHeight();
|
||||
Image checkerboard = GenImageChecked(preview_rect.width, preview_rect.height, 16, 16, BLACK, ColorAlpha(GRAY, 0.2f));
|
||||
checker_tex = LoadTextureFromImage(checkerboard);
|
||||
UnloadImage(checkerboard);
|
||||
ctx.fileDialog = InitGuiFileDialog(420, 310, zpl_bprintf("%s/art", GetWorkingDirectory()), false);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): ensure we reset styling to our defaults each frame
|
||||
{
|
||||
GuiSetStyle(TEXTBOX, TEXT_COLOR_NORMAL, ColorToInt(RAYWHITE));
|
||||
GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0x012e33ff);
|
||||
GuiSetStyle(BUTTON, BASE, 0x202020ff);
|
||||
GuiSetStyle(BUTTON, BASE + GUI_STATE_DISABLED*3, 0x303030ff);
|
||||
GuiSetStyle(BUTTON, TEXT + GUI_STATE_FOCUSED*3, 0x303030ff);
|
||||
GuiSetStyle(BUTTON, BORDER, 0xffffffff);
|
||||
GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0xffffffff);
|
||||
GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_LEFT_SIDE);
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(GetColor(0x222034));
|
||||
{
|
||||
if (ctx.fileDialog.fileDialogActive) GuiLock();
|
||||
if (ctx.msgbox.visible) GuiLock();
|
||||
|
||||
DrawTextureEx(checker_tex, (Vector2){ preview_window.min.x, preview_window.min.y}, 0.0f, 1.0f, WHITE);
|
||||
|
||||
Rectangle tex_rect = aabb2_ray(preview_window);
|
||||
float tile_x = tex_rect.x + zpl_max(0.0f, tex_rect.width/2.0f - (ctx.tex.width*zoom)/2.0f);
|
||||
float tile_y = tex_rect.y + zpl_max(0.0f, tex_rect.height/2.0f - (ctx.tex.height*zoom)/2.0f);
|
||||
|
||||
for (int x = -render_tiles; x <= render_tiles; x++) {
|
||||
for (int y = -render_tiles; y <= render_tiles; y++) {
|
||||
DrawTextureEx(ctx.tex, (Vector2){tile_x + (ctx.tex.width*zoom) * x, tile_y + (ctx.tex.height*zoom)*y}, 0.0f, zoom, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
DrawAABB(topbar, BLACK);
|
||||
DrawAABB(property_pane, GetColor(0x422060));
|
||||
DrawAABB(oplist_pane, GetColor(0x425060));
|
||||
|
||||
texed_draw_topbar(topbar);
|
||||
texed_draw_props_pane(property_pane);
|
||||
texed_draw_oplist_pane(oplist_pane);
|
||||
|
||||
if (ctx.fileDialog.fileDialogActive) GuiUnlock();
|
||||
if (ctx.msgbox.visible) GuiUnlock();
|
||||
|
||||
GuiFileDialog(&ctx.fileDialog);
|
||||
texed_draw_msgbox(orig_screen);
|
||||
}
|
||||
EndDrawing();
|
||||
|
||||
static bool exit_pending = false;
|
||||
if (WindowShouldClose()) {
|
||||
if (!ctx.is_saved) {
|
||||
texed_msgbox_init("Discard unsaved work?", "You have an unsaved work! Do you want to proceed?", "OK;Cancel");
|
||||
exit_pending = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exit_pending && ctx.msgbox.result != -1) {
|
||||
exit_pending = false;
|
||||
if (ctx.msgbox.result == 1) {
|
||||
break;
|
||||
}
|
||||
ctx.msgbox.result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
UnloadTexture(checker_tex);
|
||||
zpl_opts_free(&opts);
|
||||
texed_destroy();
|
||||
}
|
||||
|
||||
void texed_new(int32_t w, int32_t h) {
|
||||
ctx.img_pos = -1;
|
||||
ctx.selected_op = -1;
|
||||
zpl_memset(ctx.img, 0, sizeof(Image)*TD_IMAGES_MAX_STACK);
|
||||
ctx.filepath = NULL;
|
||||
ctx.msgbox.result = -1;
|
||||
zpl_array_init(ctx.ops, zpl_heap());
|
||||
is_repaint_locked = true;
|
||||
texed_add_op(TOP_NEW_IMAGE);
|
||||
zpl_i64_to_str(w, ctx.ops[0].params[0].str, 10);
|
||||
zpl_i64_to_str(h, ctx.ops[0].params[1].str, 10);
|
||||
is_repaint_locked = false;
|
||||
texed_repaint_preview();
|
||||
|
||||
ctx.fileDialog = InitGuiFileDialog(420, 310, zpl_bprintf("%s/art", GetWorkingDirectory()), false);
|
||||
ctx.is_saved = true;
|
||||
}
|
||||
|
||||
void texed_clear(void) {
|
||||
zpl_array_clear(ctx.ops);
|
||||
for (int i = 0; i <= ctx.img_pos; i+=1)
|
||||
UnloadImage(ctx.img[i]);
|
||||
ctx.img_pos = -1;
|
||||
ctx.selected_op = -1;
|
||||
}
|
||||
|
||||
void texed_destroy(void) {
|
||||
texed_clear();
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
void texed_export_cc(char const *path) {
|
||||
zpl_printf("Building texture %s ...\n", zpl_bprintf("art/gen/%s.h", GetFileNameWithoutExt(path)));
|
||||
ExportImageAsCode(ctx.img[ctx.img_pos], zpl_bprintf("art/gen/%s.h", GetFileNameWithoutExt(path)));
|
||||
}
|
||||
|
||||
void texed_export_png(char const *path) {
|
||||
zpl_printf("Exporting texture %s ...\n", zpl_bprintf("art/gen/%s.png", GetFileNameWithoutExt(path)));
|
||||
ExportImage(ctx.img[ctx.img_pos], zpl_bprintf("art/gen/%s.png", GetFileNameWithoutExt(path)));
|
||||
}
|
||||
|
||||
void texed_img_push(int w, int h, Color color) {
|
||||
if (ctx.img_pos == TD_IMAGES_MAX_STACK)
|
||||
return;
|
||||
|
||||
ctx.img_pos += 1;
|
||||
ctx.img[ctx.img_pos] = GenImageColor(w, h, color);
|
||||
}
|
||||
|
||||
void texed_img_pop(int x, int y, int w, int h, Color tint) {
|
||||
if (ctx.img_pos == 0)
|
||||
return;
|
||||
|
||||
Image *oi = &ctx.img[ctx.img_pos];
|
||||
Image *di = &ctx.img[ctx.img_pos-1];
|
||||
|
||||
Rectangle src = {
|
||||
0, 0,
|
||||
oi->width, oi->height
|
||||
};
|
||||
|
||||
w = (w == 0) ? di->width : w;
|
||||
h = (h == 0) ? di->height : h;
|
||||
|
||||
Rectangle dst = {
|
||||
x, y,
|
||||
w, h,
|
||||
};
|
||||
|
||||
ImageDraw(di, *oi, src, dst, tint);
|
||||
UnloadImage(ctx.img[ctx.img_pos]);
|
||||
ctx.img_pos -= 1;
|
||||
}
|
||||
|
||||
void texed_repaint_preview(void) {
|
||||
if (is_repaint_locked) return;
|
||||
texed_compose_image();
|
||||
|
||||
if (!IsWindowReady()) return;
|
||||
UnloadTexture(ctx.tex);
|
||||
ctx.tex = LoadTextureFromImage(ctx.img[ctx.img_pos]);
|
||||
}
|
||||
|
||||
void texed_compose_image(void) {
|
||||
if (is_repaint_locked) return;
|
||||
ctx.is_saved = false;
|
||||
texed_process_params();
|
||||
texed_process_ops();
|
||||
}
|
||||
|
||||
void texed_msgbox_init(char const *title, char const *message, char const *buttons) {
|
||||
ctx.msgbox.result = -1;
|
||||
ctx.msgbox.visible = true;
|
||||
ctx.msgbox.title = title;
|
||||
ctx.msgbox.message = message;
|
||||
ctx.msgbox.buttons = buttons;
|
||||
}
|
||||
|
||||
int texed_find_op(int kind) {
|
||||
for (int i = 0; i < DEF_OPS_LEN; i += 1) {
|
||||
if (default_ops[i].kind == kind) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void texed_add_op(int kind) {
|
||||
int idx = texed_find_op(kind);
|
||||
assert(idx >= 0);
|
||||
td_op *dop = &default_ops[idx];
|
||||
|
||||
td_op op = {
|
||||
.kind = dop->kind,
|
||||
.name = dop->name,
|
||||
.is_locked = dop->is_locked,
|
||||
.num_params = dop->num_params,
|
||||
.params = (td_param*)zpl_malloc(sizeof(td_param)*dop->num_params)
|
||||
};
|
||||
|
||||
zpl_memcopy(op.params, dop->params, sizeof(td_param)*dop->num_params);
|
||||
|
||||
//TODO(zaklaus): weird stuff down there
|
||||
//zpl_array_append_at(ctx.ops, op, ctx.selected_op+1);
|
||||
int ind = ctx.selected_op+1;
|
||||
do {
|
||||
if (ind >= zpl_array_count(ctx.ops)) { zpl_array_append(ctx.ops, op); break; }
|
||||
if (zpl_array_capacity(ctx.ops) < zpl_array_count(ctx.ops) + 1) zpl_array_grow(ctx.ops, 0);
|
||||
zpl_memmove(&(ctx.ops)[ind + 1], (ctx.ops + ind), zpl_size_of(td_op) * (zpl_array_count(ctx.ops) - ind));
|
||||
ctx.ops[ind] = op;
|
||||
zpl_array_count(ctx.ops)++;
|
||||
} while (0);
|
||||
ctx.selected_op++;
|
||||
|
||||
texed_repaint_preview();
|
||||
}
|
||||
|
||||
void texed_swp_op(int idx, int idx2) {
|
||||
assert(idx >= 0 && idx < (int)zpl_array_count(ctx.ops));
|
||||
assert(idx2 >= 0 && idx2 < (int)zpl_array_count(ctx.ops));
|
||||
|
||||
td_op tmp = ctx.ops[idx2];
|
||||
ctx.ops[idx2] = ctx.ops[idx];
|
||||
ctx.ops[idx] = tmp;
|
||||
if (idx == ctx.selected_op) ctx.selected_op = idx2;
|
||||
texed_repaint_preview();
|
||||
}
|
||||
|
||||
void texed_rem_op(int idx) {
|
||||
assert(idx >= 0 && idx < (int)zpl_array_count(ctx.ops));
|
||||
zpl_mfree(ctx.ops[idx].params);
|
||||
zpl_array_remove_at(ctx.ops, idx);
|
||||
|
||||
if (zpl_array_count(ctx.ops) > 0 && idx <= ctx.selected_op) ctx.selected_op -= 1;
|
||||
texed_repaint_preview();
|
||||
}
|
|
@ -1,367 +0,0 @@
|
|||
static inline
|
||||
float texed_map_value(float v, float min, float max);
|
||||
|
||||
static inline
|
||||
Image texed_generate_noise(uint32_t seed, int width, int height, float factor);
|
||||
|
||||
static inline
|
||||
Image texed_generate_cellular(uint32_t seed, int width, int height, int tileSize);
|
||||
|
||||
void texed_process_ops(void) {
|
||||
for (int i = 0; i <= ctx.img_pos; i+=1)
|
||||
UnloadImage(ctx.img[i]);
|
||||
ctx.img_pos = -1;
|
||||
|
||||
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||
td_op *op = &ctx.ops[i];
|
||||
if (op->is_hidden) continue;
|
||||
//zpl_printf("processing op: %s ... \n", op->name);
|
||||
|
||||
switch (op->kind) {
|
||||
case TOP_PUSH_IMAGE:
|
||||
case TOP_NEW_IMAGE: {
|
||||
texed_img_push(op->params[0].i32, op->params[1].i32, op->params[2].color);
|
||||
}break;
|
||||
case TOP_POP_IMAGE: {
|
||||
texed_img_pop(op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].i32,
|
||||
op->params[3].i32,
|
||||
op->params[4].color);
|
||||
}break;
|
||||
case TOP_IMAGE_ALPHA_MASK: {
|
||||
if (ctx.img_pos == 0) break;
|
||||
|
||||
Image *oi = &ctx.img[ctx.img_pos];
|
||||
Image *di = &ctx.img[ctx.img_pos-1];
|
||||
ImageAlphaMask(di, *oi);
|
||||
ctx.img_pos--;
|
||||
}break;
|
||||
case TOP_IMAGE_ALPHA_MASK_CLEAR: {
|
||||
ImageAlphaClear(&ctx.img[ctx.img_pos], op->params[0].color, op->params[1].flt);
|
||||
}break;
|
||||
case TOP_DRAW_RECT: {
|
||||
ImageDrawRectangle(&ctx.img[ctx.img_pos],
|
||||
op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].i32,
|
||||
op->params[3].i32,
|
||||
op->params[4].color);
|
||||
}break;
|
||||
case TOP_DRAW_LINE: {
|
||||
ImageDrawLine(&ctx.img[ctx.img_pos],
|
||||
op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].i32,
|
||||
op->params[3].i32,
|
||||
op->params[4].color);
|
||||
}break;
|
||||
case TOP_DITHER: {
|
||||
ImageDither(&ctx.img[ctx.img_pos],
|
||||
op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].i32,
|
||||
op->params[3].i32);
|
||||
}break;
|
||||
case TOP_DRAW_IMAGE: {
|
||||
char const *str = zpl_bprintf("art/%s", op->params[0].str);
|
||||
if (FileExists(str)) {
|
||||
Image img = LoadImage(str);
|
||||
int x = op->params[1].i32;
|
||||
int y = op->params[2].i32;
|
||||
int w = op->params[3].i32;
|
||||
int h = op->params[4].i32;
|
||||
int flip = op->params[5].i32;
|
||||
int rotate = op->params[6].i32;
|
||||
|
||||
if (w != 0 || h != 0) {
|
||||
ImageResize(&img, w != 0 ? w : img.width, h != 0 ? h : img.height);
|
||||
}
|
||||
|
||||
if (flip == 1) {
|
||||
ImageFlipVertical(&img);
|
||||
} else if (flip == 2) {
|
||||
ImageFlipHorizontal(&img);
|
||||
}
|
||||
|
||||
if (rotate == 1) {
|
||||
ImageRotateCW(&img);
|
||||
} else if (rotate == 2) {
|
||||
ImageRotateCCW(&img);
|
||||
}
|
||||
|
||||
ImageDraw(&ctx.img[ctx.img_pos], img,
|
||||
(Rectangle){0.0f, 0.0f, img.width, img.height},
|
||||
(Rectangle){x, y, img.width, img.height},
|
||||
op->params[5].color);
|
||||
|
||||
UnloadImage(img);
|
||||
} else {
|
||||
zpl_printf("TOP_LOAD_IMAGE: src %s not found!\n", str);
|
||||
}
|
||||
}break;
|
||||
case TOP_DRAW_TEXT: {
|
||||
char const *str = op->params[0].str;
|
||||
int x = op->params[1].i32;
|
||||
int y = op->params[2].i32;
|
||||
int size = op->params[3].i32;
|
||||
Color color = op->params[4].color;
|
||||
ImageDrawText(&ctx.img[ctx.img_pos], str, x, y, size, color);
|
||||
}break;
|
||||
case TOP_RESIZE_IMAGE: {
|
||||
if (ctx.img[ctx.img_pos].width == 0) break;
|
||||
int w = op->params[0].i32;
|
||||
int h = op->params[1].i32;
|
||||
int mode = op->params[2].i32;
|
||||
if (mode) {
|
||||
ImageResize(&ctx.img[ctx.img_pos], w, h);
|
||||
} else {
|
||||
ImageResizeNN(&ctx.img[ctx.img_pos], w, h);
|
||||
}
|
||||
}break;
|
||||
case TOP_COLOR_TWEAKS: {
|
||||
ImageColorContrast(&ctx.img[ctx.img_pos], texed_map_value(op->params[0].flt, -100.0f, 100.0f));
|
||||
ImageColorBrightness(&ctx.img[ctx.img_pos], (int)texed_map_value(op->params[1].flt, -255.0f, 255.0f));
|
||||
ImageColorTint(&ctx.img[ctx.img_pos], op->params[2].color);
|
||||
|
||||
if (op->params[3].i32) {
|
||||
ImageColorInvert(&ctx.img[ctx.img_pos]);
|
||||
}
|
||||
if (op->params[4].i32) {
|
||||
ImageColorGrayscale(&ctx.img[ctx.img_pos]);
|
||||
}
|
||||
}break;
|
||||
case TOP_COLOR_REPLACE: {
|
||||
ImageColorReplace(&ctx.img[ctx.img_pos], op->params[0].color, op->params[1].color);
|
||||
}break;
|
||||
case TOP_IMAGE_GRAD_V: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = GenImageGradientV(w, h, op->params[0].color, op->params[1].color);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_GRAD_H: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = GenImageGradientH(w, h, op->params[0].color, op->params[1].color);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_GRAD_RAD: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = GenImageGradientRadial(w, h,
|
||||
op->params[0].flt,
|
||||
op->params[1].color,
|
||||
op->params[2].color);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_CHECKED: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = GenImageChecked(w, h,
|
||||
op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].color,
|
||||
op->params[3].color);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_NOISE_WHITE: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = texed_generate_noise(op->params[0].u32,
|
||||
w, h,
|
||||
op->params[1].flt);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_NOISE_PERLIN: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = GenImagePerlinNoise(w, h,
|
||||
op->params[0].i32,
|
||||
op->params[1].i32,
|
||||
op->params[2].flt);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
case TOP_IMAGE_CELLULAR: {
|
||||
Image *dst = &ctx.img[ctx.img_pos];
|
||||
int w = dst->width;
|
||||
int h = dst->height;
|
||||
Image img = texed_generate_cellular(op->params[0].u32,
|
||||
w, h,
|
||||
op->params[1].i32);
|
||||
Rectangle rec = {0, 0, w, h};
|
||||
ImageDraw(dst, img, rec, rec, WHITE);
|
||||
UnloadImage(img);
|
||||
}break;
|
||||
default: {
|
||||
zpl_printf("%s\n", "unsupported op: %s!", op->name);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void texed_process_params(void) {
|
||||
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||
td_op *op = &ctx.ops[i];
|
||||
|
||||
for (int j = 0; j < op->num_params; j += 1) {
|
||||
td_param *p = &op->params[j];
|
||||
|
||||
switch (p->kind) {
|
||||
case TPARAM_SLIDER:
|
||||
case TPARAM_FLOAT: {
|
||||
p->old_flt = p->flt = (float)zpl_str_to_f64(p->str, NULL);
|
||||
}break;
|
||||
case TPARAM_INT:
|
||||
case TPARAM_COORD: {
|
||||
p->i32 = (int32_t)zpl_str_to_i64(p->str, NULL, 10);
|
||||
}break;
|
||||
case TPARAM_COLOR: {
|
||||
uint32_t color = (uint32_t)zpl_str_to_u64(p->str, NULL, 16);
|
||||
p->color = GetColor(color);
|
||||
}break;
|
||||
case TPARAM_STRING: {
|
||||
// NOTE(zaklaus): no-op
|
||||
}break;
|
||||
default: {
|
||||
zpl_printf("%s\n", "unsupported param!");
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
float texed_map_value(float v, float min, float max) {
|
||||
float slope = max-min;
|
||||
return min + zpl_round(slope * v);
|
||||
}
|
||||
|
||||
/* This algorithm is mentioned in the ISO C standard, here extended
|
||||
for 32 bits. */
|
||||
static inline
|
||||
int _rand_r(unsigned int *seed) {
|
||||
unsigned int next = *seed;
|
||||
int result;
|
||||
|
||||
next *= 1103515245;
|
||||
next += 12345;
|
||||
result = (unsigned int) (next / 65536) % 2048;
|
||||
|
||||
next *= 1103515245;
|
||||
next += 12345;
|
||||
result <<= 10;
|
||||
result ^= (unsigned int) (next / 65536) % 1024;
|
||||
|
||||
next *= 1103515245;
|
||||
next += 12345;
|
||||
result <<= 10;
|
||||
result ^= (unsigned int) (next / 65536) % 1024;
|
||||
|
||||
*seed = next;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline
|
||||
Image texed_generate_noise(uint32_t seed, int width, int height, float factor) {
|
||||
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
|
||||
|
||||
for (int i = 0; i < width*height; i++) {
|
||||
if ((_rand_r(&seed)%99) < (int)(factor*100.0f)) pixels[i] = WHITE;
|
||||
else pixels[i] = BLACK;
|
||||
}
|
||||
|
||||
Image image = {
|
||||
.data = pixels,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
|
||||
.mipmaps = 1
|
||||
};
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static inline
|
||||
Image texed_generate_cellular(uint32_t seed, int width, int height, int tileSize)
|
||||
{
|
||||
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
|
||||
|
||||
int seedsPerRow = width/tileSize;
|
||||
int seedsPerCol = height/tileSize;
|
||||
int seedsCount = seedsPerRow*seedsPerCol;
|
||||
|
||||
Vector2 *seeds = (Vector2 *)RL_MALLOC(seedsCount*sizeof(Vector2));
|
||||
|
||||
for (int i = 0; i < seedsCount; i++)
|
||||
{
|
||||
int y = (i/seedsPerRow)*tileSize + _rand_r(&seed)%(tileSize - 1);
|
||||
int x = (i%seedsPerRow)*tileSize + _rand_r(&seed)%(tileSize - 1);
|
||||
seeds[i] = (Vector2){ (float)x, (float)y};
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
int tileY = y/tileSize;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int tileX = x/tileSize;
|
||||
|
||||
float minDistance = (float)strtod("Inf", NULL);
|
||||
|
||||
// Check all adjacent tiles
|
||||
for (int i = -1; i < 2; i++)
|
||||
{
|
||||
if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
|
||||
|
||||
for (int j = -1; j < 2; j++)
|
||||
{
|
||||
if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
|
||||
|
||||
Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
|
||||
|
||||
float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
|
||||
minDistance = (float)fmin(minDistance, dist);
|
||||
}
|
||||
}
|
||||
|
||||
// I made this up but it seems to give good results at all tile sizes
|
||||
int intensity = (int)(minDistance*256.0f/tileSize);
|
||||
if (intensity > 255) intensity = 255;
|
||||
|
||||
pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
|
||||
}
|
||||
}
|
||||
|
||||
RL_FREE(seeds);
|
||||
|
||||
Image image = {
|
||||
.data = pixels,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
|
||||
.mipmaps = 1
|
||||
};
|
||||
|
||||
return image;
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
|
||||
#define PARAM(k,n,v) { .kind = k, .name = n, .str = v }
|
||||
#define PARAMS(n) .num_params = n, .params = (td_param[])
|
||||
#define PARAM_DEF_COLOR "000000ff"
|
||||
|
||||
static td_op default_ops[] = {
|
||||
{
|
||||
OP(TOP_NEW_IMAGE),
|
||||
.cat = TCAT_STACK,
|
||||
.is_locked = true,
|
||||
PARAMS(3) {
|
||||
PARAM(TPARAM_COORD, "w", "64"),
|
||||
PARAM(TPARAM_COORD, "h", "64"),
|
||||
PARAM(TPARAM_COLOR, "color", "ffffffff"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_PUSH_IMAGE),
|
||||
.cat = TCAT_STACK,
|
||||
PARAMS(3) {
|
||||
PARAM(TPARAM_COORD, "w", "64"),
|
||||
PARAM(TPARAM_COORD, "h", "64"),
|
||||
PARAM(TPARAM_COLOR, "color", "ffffffff"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_POP_IMAGE),
|
||||
.cat = TCAT_STACK,
|
||||
PARAMS(5) {
|
||||
PARAM(TPARAM_COORD, "x", "0"),
|
||||
PARAM(TPARAM_COORD, "y", "0"),
|
||||
PARAM(TPARAM_COORD, "w", "0"),
|
||||
PARAM(TPARAM_COORD, "h", "0"),
|
||||
PARAM(TPARAM_COLOR, "tint", "ffffffff"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_ALPHA_MASK),
|
||||
.cat = TCAT_STACK,
|
||||
},{
|
||||
OP(TOP_IMAGE_ALPHA_MASK_CLEAR),
|
||||
.cat = TCAT_STACK,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COLOR, "color", "ffffffff"),
|
||||
PARAM(TPARAM_FLOAT, "threshold", "1.0"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_DRAW_RECT),
|
||||
.cat = TCAT_DRAW,
|
||||
PARAMS(5) {
|
||||
PARAM(TPARAM_COORD, "x", "0"),
|
||||
PARAM(TPARAM_COORD, "y", "0"),
|
||||
PARAM(TPARAM_COORD, "w", "10"),
|
||||
PARAM(TPARAM_COORD, "h", "10"),
|
||||
PARAM(TPARAM_COLOR, "color", PARAM_DEF_COLOR),
|
||||
}
|
||||
},{
|
||||
OP(TOP_DRAW_LINE),
|
||||
.cat = TCAT_DRAW,
|
||||
PARAMS(5) {
|
||||
PARAM(TPARAM_COORD, "x1", "0"),
|
||||
PARAM(TPARAM_COORD, "y1", "0"),
|
||||
PARAM(TPARAM_COORD, "x2", "64"),
|
||||
PARAM(TPARAM_COORD, "y2", "64"),
|
||||
PARAM(TPARAM_COLOR, "color", PARAM_DEF_COLOR),
|
||||
}
|
||||
},{
|
||||
OP(TOP_DRAW_IMAGE),
|
||||
.cat = TCAT_DRAW,
|
||||
PARAMS(8) {
|
||||
PARAM(TPARAM_STRING, "src", "samples/test.png"),
|
||||
PARAM(TPARAM_COORD, "x", "0"),
|
||||
PARAM(TPARAM_COORD, "y", "0"),
|
||||
PARAM(TPARAM_COORD, "w", "0"),
|
||||
PARAM(TPARAM_COORD, "h", "0"),
|
||||
PARAM(TPARAM_COLOR, "tint", "ffffffff"),
|
||||
PARAM(TPARAM_INT, "flip?", "0"),
|
||||
PARAM(TPARAM_INT, "rotate?", "0"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_DRAW_TEXT),
|
||||
.cat = TCAT_DRAW,
|
||||
PARAMS(5) {
|
||||
PARAM(TPARAM_STRING, "text", "hello world"),
|
||||
PARAM(TPARAM_COORD, "x", "0"),
|
||||
PARAM(TPARAM_COORD, "y", "0"),
|
||||
PARAM(TPARAM_COORD, "size", "16"),
|
||||
PARAM(TPARAM_COLOR, "color", PARAM_DEF_COLOR),
|
||||
}
|
||||
},{
|
||||
OP(TOP_DITHER),
|
||||
.cat = TCAT_MOD,
|
||||
PARAMS(4) {
|
||||
PARAM(TPARAM_INT, "r_bpp", "4"),
|
||||
PARAM(TPARAM_INT, "g_bpp", "4"),
|
||||
PARAM(TPARAM_INT, "b_bpp", "4"),
|
||||
PARAM(TPARAM_INT, "a_bpp", "4"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_RESIZE_IMAGE),
|
||||
.cat = TCAT_MOD,
|
||||
PARAMS(3) {
|
||||
PARAM(TPARAM_COORD, "w", "64"),
|
||||
PARAM(TPARAM_COORD, "h", "64"),
|
||||
PARAM(TPARAM_COORD, "mode (0=nearest,1=bicubic)", "0"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_COLOR_TWEAKS),
|
||||
.cat = TCAT_MOD,
|
||||
PARAMS(5) {
|
||||
PARAM(TPARAM_SLIDER, "contrast", "0.5"),
|
||||
PARAM(TPARAM_SLIDER, "brightness", "0.5"),
|
||||
PARAM(TPARAM_COLOR, "tint", "FFFFFFFF"),
|
||||
PARAM(TPARAM_INT, "invert?", "0"),
|
||||
PARAM(TPARAM_INT, "grayscale?", "0"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_COLOR_REPLACE),
|
||||
.cat = TCAT_MOD,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COLOR, "original", "FFFFFFFF"),
|
||||
PARAM(TPARAM_COLOR, "new", "FF0000FF"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_GRAD_V),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COLOR, "top", "ffffffff"),
|
||||
PARAM(TPARAM_COLOR, "bottom", "00000000"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_GRAD_H),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COLOR, "left", "ffffffff"),
|
||||
PARAM(TPARAM_COLOR, "right", "00000000"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_GRAD_RAD),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(3) {
|
||||
PARAM(TPARAM_FLOAT, "density", "0.5"),
|
||||
PARAM(TPARAM_COLOR, "inner", "ffffffff"),
|
||||
PARAM(TPARAM_COLOR, "outer", "00000000"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_CHECKED),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(4) {
|
||||
PARAM(TPARAM_COORD, "checks_x", "16"),
|
||||
PARAM(TPARAM_COORD, "checks_y", "16"),
|
||||
PARAM(TPARAM_COLOR, "color1", "ffffffff"),
|
||||
PARAM(TPARAM_COLOR, "color2", "00000000"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_NOISE_WHITE),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COORD, "seed", "1"),
|
||||
PARAM(TPARAM_FLOAT, "factor", "0.5"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_NOISE_PERLIN),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(3) {
|
||||
PARAM(TPARAM_COORD, "offset_x", "0"),
|
||||
PARAM(TPARAM_COORD, "offset_y", "0"),
|
||||
PARAM(TPARAM_FLOAT, "scale", "1.0"),
|
||||
}
|
||||
},{
|
||||
OP(TOP_IMAGE_CELLULAR),
|
||||
.cat = TCAT_GEN,
|
||||
PARAMS(2) {
|
||||
PARAM(TPARAM_COORD, "seed", "1"),
|
||||
PARAM(TPARAM_COORD, "tile_size", "16"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define DEF_OPS_LEN (int)(sizeof(default_ops) / (sizeof(default_ops[0])))
|
||||
|
||||
static tcat_desc default_cats[] = {
|
||||
{.kind = TCAT_STACK, .icon = "#197#", .color = RED},
|
||||
{.kind = TCAT_GEN, .icon = "#197#", .color = BLUE},
|
||||
{.kind = TCAT_DRAW, .icon = "#197#", .color = GREEN},
|
||||
{.kind = TCAT_MOD, .icon = "#197#", .color = ORANGE},
|
||||
};
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
//~ NOTE(zaklaus): DATA SERIALISATION
|
||||
|
||||
#define ECOTEX_VERSION 3
|
||||
|
||||
#define UNPACK(kind) cw_unpack_next(&uc); assert(uc.item.type == kind);
|
||||
|
||||
void texed_load(void) {
|
||||
assert(ctx.filepath);
|
||||
zpl_printf("Loading %s ...\n", ctx.filepath);
|
||||
is_repaint_locked = true;
|
||||
texed_clear();
|
||||
uint32_t size = 0;
|
||||
uint8_t *databuf = LoadFileData(zpl_bprintf("art/%s.ecotex", ctx.filepath), &size);
|
||||
|
||||
cw_unpack_context uc;
|
||||
cw_unpack_context_init(&uc, databuf, (size_t)size, NULL);
|
||||
|
||||
UNPACK(CWP_ITEM_POSITIVE_INTEGER);
|
||||
assert(uc.item.as.u64 == ECOTEX_VERSION);
|
||||
|
||||
UNPACK(CWP_ITEM_POSITIVE_INTEGER);
|
||||
int selected_op = (int)uc.item.as.u64;
|
||||
|
||||
UNPACK(CWP_ITEM_FLOAT);
|
||||
old_zoom = zoom = uc.item.as.real;
|
||||
|
||||
UNPACK(CWP_ITEM_ARRAY);
|
||||
int arrsize = (int)uc.item.as.array.size;
|
||||
for (int i = 0; i < arrsize; i += 1) {
|
||||
UNPACK(CWP_ITEM_POSITIVE_INTEGER);
|
||||
int kind = (int)uc.item.as.u64;
|
||||
texed_add_op(kind);
|
||||
td_op *op = zpl_array_end(ctx.ops);
|
||||
UNPACK(CWP_ITEM_BOOLEAN);
|
||||
op->is_locked = uc.item.as.boolean;
|
||||
UNPACK(CWP_ITEM_BOOLEAN);
|
||||
op->is_hidden = uc.item.as.boolean;
|
||||
|
||||
UNPACK(CWP_ITEM_ARRAY);
|
||||
int idx = texed_find_op(kind);
|
||||
op->num_params = default_ops[idx].num_params;
|
||||
op->params = zpl_malloc(sizeof(td_param)*op->num_params);
|
||||
int parmarrsize = (int)uc.item.as.array.size;
|
||||
for (int j = 0; j < parmarrsize; j += 1) {
|
||||
td_param *p = &op->params[j];
|
||||
UNPACK(CWP_ITEM_STR);
|
||||
zpl_memcopy(p->str, uc.item.as.str.start, uc.item.as.str.length);
|
||||
|
||||
// NOTE(zaklaus): fix up other metadata
|
||||
p->name = default_ops[idx].params[j].name;
|
||||
p->kind = default_ops[idx].params[j].kind;
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): resolve missing params
|
||||
for (int j = parmarrsize; j < default_ops[idx].num_params; j += 1) {
|
||||
td_param *p = &op->params[j];
|
||||
p->name = default_ops[idx].params[j].name;
|
||||
p->kind = default_ops[idx].params[j].kind;
|
||||
zpl_strcpy(p->str, default_ops[idx].params[j].str);
|
||||
}
|
||||
}
|
||||
|
||||
assert(uc.return_code == CWP_RC_OK);
|
||||
cw_unpack_next(&uc);
|
||||
assert(uc.return_code == CWP_RC_END_OF_INPUT);
|
||||
|
||||
ctx.selected_op = selected_op;
|
||||
is_repaint_locked = false;
|
||||
texed_repaint_preview();
|
||||
UnloadFileData(databuf);
|
||||
ctx.is_saved = true;
|
||||
}
|
||||
|
||||
void texed_save(void) {
|
||||
assert(ctx.filepath);
|
||||
zpl_printf("Saving %s ...\n", ctx.filepath);
|
||||
|
||||
static uint8_t databuf[400000] = {0};
|
||||
|
||||
cw_pack_context pc;
|
||||
cw_pack_context_init(&pc, databuf, sizeof(databuf), NULL);
|
||||
|
||||
cw_pack_unsigned(&pc, ECOTEX_VERSION);
|
||||
cw_pack_unsigned(&pc, ctx.selected_op);
|
||||
cw_pack_float(&pc, zoom);
|
||||
|
||||
cw_pack_array_size(&pc, zpl_array_count(ctx.ops));
|
||||
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||
td_op *op = &ctx.ops[i];
|
||||
cw_pack_unsigned(&pc, op->kind);
|
||||
cw_pack_boolean(&pc, (bool)op->is_locked);
|
||||
cw_pack_boolean(&pc, (bool)op->is_hidden);
|
||||
cw_pack_array_size(&pc, op->num_params);
|
||||
for (int j = 0; j < op->num_params; j += 1) {
|
||||
td_param *p = &op->params[j];
|
||||
cw_pack_str(&pc, p->str, zpl_strlen(p->str));
|
||||
}
|
||||
}
|
||||
|
||||
SaveFileData(zpl_bprintf("art/%s.ecotex", ctx.filepath), databuf, pc.current - pc.start);
|
||||
ctx.is_saved = true;
|
||||
}
|
|
@ -1,687 +0,0 @@
|
|||
static inline
|
||||
void int_to_hex_color(uint32_t color, char *text);
|
||||
static inline
|
||||
int GuiDropdownBoxEco(Rectangle bounds, char const *text, char const *caption, int *active, bool editMode);
|
||||
static inline
|
||||
bool GuiValueBoxEco(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode);
|
||||
|
||||
static inline
|
||||
bool IsCtrlAcceleratorPressed(char key);
|
||||
|
||||
static inline
|
||||
char const *prettify_op_name(int idx);
|
||||
|
||||
void texed_draw_topbar(zpl_aabb2 r) {
|
||||
zpl_aabb2 zoom_ctrl_r = zpl_aabb2_cut_left(&r, 150.0f);
|
||||
|
||||
zoom = GuiSlider(aabb2_ray(zoom_ctrl_r), "zoom: ", zpl_bprintf("%.02f x", zoom), zoom, 1.0f, 16.0f);
|
||||
if (zoom != old_zoom) {
|
||||
ctx.is_saved = false;
|
||||
old_zoom = zoom;
|
||||
}
|
||||
|
||||
zpl_aabb2_cut_left(&r, 100.0f);
|
||||
|
||||
zpl_aabb2 render_tiles_ctrl_r = zpl_aabb2_cut_left(&r, 150.0f);
|
||||
|
||||
render_tiles = (int)GuiSlider(aabb2_ray(render_tiles_ctrl_r), "tiles: ", zpl_bprintf("%d", render_tiles+1), render_tiles, 0.0f, 50.0f);
|
||||
|
||||
zpl_aabb2_cut_left(&r, 100.0f);
|
||||
|
||||
zpl_aabb2 new_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||
static bool new_pending = false;
|
||||
|
||||
if (GuiButton(aabb2_ray(new_prj_r), "NEW") || IsCtrlAcceleratorPressed('n')) {
|
||||
if (ctx.is_saved) {
|
||||
texed_clear();
|
||||
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
|
||||
} else {
|
||||
new_pending = true;
|
||||
texed_msgbox_init("Discard unsaved work?", "You have an unsaved work! Do you want to proceed?", "OK;Cancel");
|
||||
}
|
||||
}
|
||||
|
||||
if (new_pending && ctx.msgbox.result != -1) {
|
||||
new_pending = false;
|
||||
if (ctx.msgbox.result == 1) {
|
||||
texed_clear();
|
||||
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
|
||||
}
|
||||
ctx.msgbox.result = -1; // NOTE(zaklaus): ensure we don't re-trigger this branch next frame
|
||||
}
|
||||
|
||||
zpl_aabb2 load_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||
static bool load_pending = false;
|
||||
|
||||
if (GuiButton(aabb2_ray(load_prj_r), "LOAD") || IsCtrlAcceleratorPressed('o')) {
|
||||
load_pending = true;
|
||||
if (ctx.is_saved) {
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
} else {
|
||||
texed_msgbox_init("Discard unsaved work?", "You have an unsaved work! Do you want to proceed?", "OK;Cancel");
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.fileDialog.SelectFilePressed && load_pending) {
|
||||
ctx.fileDialog.SelectFilePressed = false;
|
||||
if (IsFileExtension(ctx.fileDialog.fileNameText, ".ecotex")) {
|
||||
strcpy(filename, GetFileNameWithoutExt(ctx.fileDialog.fileNameText));
|
||||
ctx.filepath = filename;
|
||||
load_pending = false;
|
||||
texed_load();
|
||||
} else {
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (load_pending && ctx.msgbox.result != -1) {
|
||||
if (ctx.msgbox.result == 1) {
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
}
|
||||
else load_pending = false;
|
||||
ctx.msgbox.result = -1; // NOTE(zaklaus): ensure we don't re-trigger this branch next frame
|
||||
}
|
||||
|
||||
zpl_aabb2 save_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||
static bool save_as_pending = false;
|
||||
|
||||
if (GuiButton(aabb2_ray(save_prj_r), "SAVE") || IsCtrlAcceleratorPressed('s')) {
|
||||
if (ctx.filepath == NULL) {
|
||||
save_as_pending = true;
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
} else {
|
||||
texed_save();
|
||||
}
|
||||
}
|
||||
|
||||
zpl_aabb2 save_as_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||
|
||||
if (GuiButton(aabb2_ray(save_as_prj_r), "SAVE AS")) {
|
||||
save_as_pending = true;
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
}
|
||||
|
||||
if (ctx.fileDialog.SelectFilePressed && save_as_pending) {
|
||||
ctx.fileDialog.SelectFilePressed = false;
|
||||
if (TextLength(ctx.fileDialog.fileNameText)) {
|
||||
strcpy(filename, GetFileNameWithoutExt(ctx.fileDialog.fileNameText));
|
||||
ctx.filepath = filename;
|
||||
save_as_pending = false;
|
||||
texed_save();
|
||||
} else {
|
||||
ctx.fileDialog.fileDialogActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
zpl_aabb2 split_r = zpl_aabb2_cut_left(&r, 5.0f);
|
||||
split_r = zpl_aabb2_contract(&split_r, 2.0f);
|
||||
DrawAABB(split_r, BLACK);
|
||||
|
||||
zpl_aabb2 exports_r = zpl_aabb2_cut_left(&r, 240.0f);
|
||||
GuiSetState(ctx.filepath ? GUI_STATE_NORMAL : GUI_STATE_DISABLED);
|
||||
zpl_aabb2 export_code_r = zpl_aabb2_cut_left(&exports_r, 120.0f);
|
||||
|
||||
if (GuiButton(aabb2_ray(export_code_r), "BUILD TEXTURE")) {
|
||||
texed_export_cc(ctx.filepath);
|
||||
}
|
||||
|
||||
zpl_aabb2 export_img_r = zpl_aabb2_cut_left(&exports_r, 120.0f);
|
||||
|
||||
if (GuiButton(aabb2_ray(export_img_r), "EXPORT AS IMAGE")) {
|
||||
texed_export_png(ctx.filepath);
|
||||
}
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
|
||||
zpl_aabb2 prj_name_r = zpl_aabb2_cut_right(&r, 200.0f);
|
||||
zpl_aabb2_cut_right(&prj_name_r, 15.0f);
|
||||
GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_RIGHT);
|
||||
GuiDrawText(zpl_bprintf("Project: %s%s", ctx.filepath ? ctx.filepath : "(unnamed)", ctx.is_saved ? "" : "*"), GetTextBounds(LABEL, aabb2_ray(prj_name_r)), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GREEN, guiAlpha));
|
||||
GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
|
||||
}
|
||||
|
||||
void texed_draw_oplist_pane(zpl_aabb2 r) {
|
||||
// NOTE(zaklaus): add operator
|
||||
{
|
||||
zpl_aabb2 add_op_r = zpl_aabb2_cut_right(&r, 200.0f);
|
||||
DrawAABB(add_op_r, GetColor(0x122025));
|
||||
add_op_r = zpl_aabb2_contract(&add_op_r, 3.0f);
|
||||
Rectangle panel_rec = aabb2_ray(add_op_r);
|
||||
static Vector2 panel_scroll = {99, -20};
|
||||
float list_y = (DEF_OPS_LEN) * 22.5f;
|
||||
if (list_y >= (add_op_r.max.y-add_op_r.min.y)) add_op_r.min.x += 12.0f;
|
||||
else add_op_r.min.x += 2.0f;
|
||||
add_op_r.max.y = add_op_r.min.y + list_y;
|
||||
|
||||
Rectangle view = GuiScrollPanel(panel_rec, aabb2_ray(add_op_r), &panel_scroll);
|
||||
|
||||
BeginScissorMode(view.x, view.y, view.width, view.height);
|
||||
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
|
||||
|
||||
for (int i = 0; i < DEF_OPS_LEN; i += 1) {
|
||||
if (default_ops[i].is_locked) continue;
|
||||
tcat_desc cat_info = default_cats[default_ops[i].cat];
|
||||
Color color = cat_info.color;
|
||||
|
||||
zpl_aabb2 add_op_btn_r = zpl_aabb2_cut_top(&add_op_r, 22.5f);
|
||||
add_op_btn_r.min.y += panel_scroll.y;
|
||||
add_op_btn_r.max.y += panel_scroll.y;
|
||||
add_op_btn_r.max.x -= 2.0f;
|
||||
zpl_aabb2_cut_bottom(&add_op_btn_r, 2.5f);
|
||||
|
||||
GuiSetStyle(BUTTON, BORDER, ColorToInt(Fade(color, 0.89f)));
|
||||
GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
|
||||
|
||||
if (GuiButton(aabb2_ray(add_op_btn_r), prettify_op_name(i))) {
|
||||
texed_add_op(default_ops[i].kind);
|
||||
}
|
||||
|
||||
GuiSetStyle(BUTTON, BORDER, 0x838383ff);
|
||||
GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
|
||||
}
|
||||
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
|
||||
|
||||
EndScissorMode();
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): recalculate height based on ops count
|
||||
Rectangle panel_rec = aabb2_ray(r);
|
||||
static Vector2 panel_scroll = {99, -20};
|
||||
float list_y = zpl_array_count(ctx.ops)*25.0f;
|
||||
if (list_y >= (r.max.y-r.min.y)) r.min.x += 12.0f;
|
||||
else r.min.x += 2.0f;
|
||||
r.max.y = r.min.y + list_y;
|
||||
|
||||
Rectangle view = GuiScrollPanel(panel_rec, aabb2_ray(r), &panel_scroll);
|
||||
|
||||
BeginScissorMode(view.x, view.y, view.width, view.height);
|
||||
|
||||
// NOTE(zaklaus): operator list
|
||||
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||
td_op *op = &ctx.ops[i];
|
||||
zpl_aabb2 op_item_r = zpl_aabb2_cut_top(&r, 25.0f);
|
||||
op_item_r.min.y += panel_scroll.y;
|
||||
op_item_r.max.y += panel_scroll.y;
|
||||
zpl_aabb2_cut_top(&op_item_r, 2.5f);
|
||||
zpl_aabb2_cut_bottom(&op_item_r, 2.5f);
|
||||
Rectangle list_item = aabb2_ray(op_item_r);
|
||||
tcat_desc cat_info = default_cats[default_ops[texed_find_op(op->kind)].cat];
|
||||
Color bg_color = cat_info.color;
|
||||
|
||||
DrawRectangleRec(list_item, ColorAlpha(bg_color, ctx.selected_op == i ? 0.6f : 0.325f));
|
||||
zpl_aabb2 swap_r = zpl_aabb2_cut_left(&op_item_r, 50.0f);
|
||||
Rectangle list_text = aabb2_ray(op_item_r);
|
||||
|
||||
zpl_aabb2_cut_right(&swap_r, 5.0f);
|
||||
zpl_aabb2 swap_top = zpl_aabb2_cut_left(&swap_r, aabb2_ray(swap_r).width/2.0f);
|
||||
zpl_aabb2 swap_bottom = swap_r;
|
||||
|
||||
if (i == 0 || op->is_locked || (i > 0 && ctx.ops[i-1].is_locked)) GuiSetState(GUI_STATE_DISABLED);
|
||||
if (GuiButton(aabb2_ray(swap_top), "#121#")) {
|
||||
texed_swp_op(i, i-1);
|
||||
}
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
if (op->is_locked || (i+1 < zpl_array_count(ctx.ops) && ctx.ops[i+1].is_locked) || i+1 >= zpl_array_count(ctx.ops)) GuiSetState(GUI_STATE_DISABLED);
|
||||
if (GuiButton(aabb2_ray(swap_bottom), "#120#")) {
|
||||
texed_swp_op(i, i+1);
|
||||
}
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
zpl_aabb2 theme_stripe = zpl_aabb2_add_right(&swap_r, 3.0f);
|
||||
if (op->is_locked && op->is_hidden) {
|
||||
zpl_aabb2 stripe_bottom = zpl_aabb2_cut_bottom(&theme_stripe, (theme_stripe.max.y-theme_stripe.min.y)/2.0f);
|
||||
DrawAABB(theme_stripe, SKYBLUE);
|
||||
DrawAABB(stripe_bottom, RED);
|
||||
} else if (op->is_locked) {
|
||||
DrawAABB(theme_stripe, SKYBLUE);
|
||||
} else if (op->is_hidden) {
|
||||
DrawAABB(theme_stripe, RED);
|
||||
}
|
||||
|
||||
zpl_aabb2 remove_r = zpl_aabb2_cut_right(&op_item_r, 20.0f);
|
||||
|
||||
if (op->is_locked) GuiSetState(GUI_STATE_DISABLED);
|
||||
if (GuiButton(aabb2_ray(remove_r), "#143#")) {
|
||||
texed_rem_op(i);
|
||||
}
|
||||
|
||||
zpl_aabb2 hidden_r = zpl_aabb2_cut_right(&op_item_r, 20.0f);
|
||||
if (!default_ops[texed_find_op(op->kind)].is_locked) GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
if (op->is_hidden) {
|
||||
GuiSetStyle(BUTTON, BASE, ColorToInt(RED));
|
||||
}
|
||||
if (GuiButton(aabb2_ray(hidden_r), op->is_hidden ? "#45#" : "#44#")) {
|
||||
op->is_hidden = !op->is_hidden;
|
||||
texed_repaint_preview();
|
||||
}
|
||||
GuiSetStyle(BUTTON, BASE, 0x202020ff);
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
zpl_aabb2 lock_r = zpl_aabb2_cut_right(&op_item_r, 20.0f);
|
||||
|
||||
if (default_ops[texed_find_op(op->kind)].is_locked) GuiSetState(GUI_STATE_DISABLED);
|
||||
|
||||
if (op->is_locked) {
|
||||
GuiSetStyle(BUTTON, BASE, ColorToInt(BLUE));
|
||||
}
|
||||
if (GuiButton(aabb2_ray(lock_r), op->is_locked ? "#137#" : "#138#")) {
|
||||
op->is_locked = !op->is_locked;
|
||||
ctx.is_saved = false;
|
||||
}
|
||||
GuiSetStyle(BUTTON, BASE, 0x202020ff);
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
if (ctx.selected_op == i) GuiSetState(GUI_STATE_DISABLED);
|
||||
zpl_aabb2 select_r = zpl_aabb2_cut_right(&op_item_r, 20.0f);
|
||||
|
||||
if (GuiButton(aabb2_ray(select_r), "#141#")) {
|
||||
ctx.selected_op = i;
|
||||
ctx.is_saved = false;
|
||||
}
|
||||
GuiSetState(GUI_STATE_NORMAL);
|
||||
|
||||
GuiDrawText(zpl_bprintf("%s %s", prettify_op_name(texed_find_op(op->kind)), op->is_locked ? "(locked)" : ""), GetTextBounds(LABEL, list_text), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(RAYWHITE, guiAlpha));
|
||||
}
|
||||
|
||||
EndScissorMode();
|
||||
}
|
||||
|
||||
void texed_draw_props_pane(zpl_aabb2 r) {
|
||||
if (zpl_array_count(ctx.ops) == 0) {
|
||||
GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
|
||||
GuiDrawText("No operation is selected!", GetTextBounds(LABEL, aabb2_ray(r)), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(RAYWHITE, guiAlpha));
|
||||
GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
|
||||
return;
|
||||
}
|
||||
|
||||
td_op *op = &ctx.ops[ctx.selected_op];
|
||||
Rectangle dims = aabb2_ray(r);
|
||||
|
||||
zpl_aabb2 column_1_r = zpl_aabb2_cut_left(&r, dims.width/2.0f);
|
||||
zpl_aabb2 column_2_r = r;
|
||||
float prop_height = 25.0f;
|
||||
int prop_column_treshold = (int)zpl_floor(dims.height / prop_height);
|
||||
|
||||
for (int i = 0; i < op->num_params; i += 1) {
|
||||
td_param *p = &op->params[i];
|
||||
zpl_aabb2 *c = (i >= prop_column_treshold) ? &column_2_r : &column_1_r;
|
||||
zpl_aabb2 item = zpl_aabb2_cut_top(c, prop_height);
|
||||
zpl_aabb2_cut_bottom(&item, 5.0f);
|
||||
zpl_aabb2 label_r = zpl_aabb2_cut_left(&item, dims.width/6.0f);
|
||||
zpl_aabb2 tbox_r = item;
|
||||
|
||||
GuiDrawText(zpl_bprintf("%s: ", p->name ? p->name : "prop"), GetTextBounds(LABEL, aabb2_ray(label_r)), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(RAYWHITE, guiAlpha));
|
||||
|
||||
static bool is_color_editing = false;
|
||||
if (is_color_editing) GuiLock();
|
||||
|
||||
switch (p->kind) {
|
||||
case TPARAM_COLOR: {
|
||||
if (is_color_editing) GuiUnlock();
|
||||
if (GuiTextBoxEx(aabb2_ray(tbox_r), p->str, 1000, p->edit_mode)) {
|
||||
p->edit_mode = true;
|
||||
is_color_editing = true;
|
||||
}
|
||||
|
||||
if (p->edit_mode) {
|
||||
zpl_aabb2 extra_r = zpl_aabb2_cut_top(c, prop_height*4.0f + 50.0f);
|
||||
zpl_aabb2_cut_bottom(&extra_r, 50.0f);
|
||||
zpl_aabb2_cut_left(&extra_r, dims.width/6.0f);
|
||||
DrawRectangleRec(aabb2_ray(extra_r), GRAY);
|
||||
|
||||
zpl_aabb2 ok_r = zpl_aabb2_cut_left(&extra_r, 50.0f);
|
||||
p->color = GuiColorPicker(aabb2_ray(extra_r), p->color);
|
||||
|
||||
if (GuiButton(aabb2_ray(ok_r), "OK")) {
|
||||
GuiUnlock();
|
||||
p->edit_mode = false;
|
||||
is_color_editing = false;
|
||||
int_to_hex_color(ColorToInt(p->color), p->str);
|
||||
texed_repaint_preview();
|
||||
}
|
||||
}
|
||||
if (is_color_editing) GuiLock();
|
||||
}break;
|
||||
case TPARAM_SLIDER: {
|
||||
p->flt = GuiSlider(aabb2_ray(tbox_r), NULL, zpl_bprintf("%.02f", p->flt), p->flt, 0.0f, 1.0f);
|
||||
if (p->old_flt != p->flt) {
|
||||
sprintf(p->str, "%f", p->flt);
|
||||
p->old_flt = p->flt;
|
||||
texed_repaint_preview();
|
||||
}
|
||||
}break;
|
||||
case TPARAM_INT:
|
||||
case TPARAM_COORD: {
|
||||
if (GuiValueBoxEco(aabb2_ray(tbox_r), NULL, &p->i32, INT32_MIN, INT32_MAX, p->edit_mode)) {
|
||||
p->edit_mode = !p->edit_mode;
|
||||
|
||||
if (!p->edit_mode) {
|
||||
sprintf(p->str, "%d", p->i32);
|
||||
texed_repaint_preview();
|
||||
} else if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||
p->i32 = 0;
|
||||
}
|
||||
};
|
||||
}break;
|
||||
default: {
|
||||
if (GuiTextBoxEx(aabb2_ray(tbox_r), p->str, 1000, p->edit_mode)) {
|
||||
p->edit_mode = !p->edit_mode;
|
||||
|
||||
if (!p->edit_mode)
|
||||
texed_repaint_preview();
|
||||
}
|
||||
}break;
|
||||
};
|
||||
|
||||
if (is_color_editing) GuiUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
zpl_global const char zpl__num_to_char_table[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"@$";
|
||||
|
||||
|
||||
static inline
|
||||
void int_to_hex_color(uint32_t value, char *string) {
|
||||
char *buf = string;
|
||||
|
||||
if (value) {
|
||||
while (value > 0) {
|
||||
*buf++ = zpl__num_to_char_table[value % 16];
|
||||
value /= 16;
|
||||
}
|
||||
} else {
|
||||
*buf++ = '0';
|
||||
}
|
||||
*buf = '\0';
|
||||
|
||||
zpl_strrev(string);
|
||||
}
|
||||
|
||||
|
||||
// Dropdown Box control
|
||||
// NOTE: Returns mouse click
|
||||
static inline
|
||||
int GuiDropdownBoxEco(Rectangle bounds, char const *text, char const *caption, int *active, bool editMode)
|
||||
{
|
||||
GuiControlState state = guiState;
|
||||
int itemSelected = *active;
|
||||
int itemFocused = -1;
|
||||
|
||||
// Get substrings items from text (items pointers, lengths and count)
|
||||
int itemsCount = 0;
|
||||
const char **items = GuiTextSplit(text, &itemsCount, NULL);
|
||||
|
||||
Rectangle boundsOpen = bounds;
|
||||
boundsOpen.height = (itemsCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING));
|
||||
|
||||
Rectangle itemBounds = bounds;
|
||||
|
||||
bool pressed = false; // Check mouse button pressed
|
||||
|
||||
// Update control
|
||||
//--------------------------------------------------------------------
|
||||
if ((state != GUI_STATE_DISABLED) && !guiLocked && (itemsCount > 1))
|
||||
{
|
||||
Vector2 mousePoint = GetMousePosition();
|
||||
|
||||
if (editMode)
|
||||
{
|
||||
state = GUI_STATE_PRESSED;
|
||||
|
||||
// Check if already selected item has been pressed again
|
||||
if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
|
||||
|
||||
// Check focused and selected item
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
{
|
||||
// Update item rectangle y position for next item
|
||||
itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING));
|
||||
|
||||
if (CheckCollisionPointRec(mousePoint, itemBounds))
|
||||
{
|
||||
itemFocused = i;
|
||||
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
|
||||
{
|
||||
itemSelected = i;
|
||||
pressed = true; // Item selected, change to editMode = false
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
itemBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CheckCollisionPointRec(mousePoint, bounds))
|
||||
{
|
||||
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
|
||||
{
|
||||
pressed = true;
|
||||
state = GUI_STATE_PRESSED;
|
||||
}
|
||||
else state = GUI_STATE_FOCUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Draw control
|
||||
//--------------------------------------------------------------------
|
||||
if (editMode) GuiPanel(boundsOpen);
|
||||
|
||||
GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)), guiAlpha));
|
||||
|
||||
GuiDrawText(caption, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)), guiAlpha));
|
||||
|
||||
if (editMode)
|
||||
{
|
||||
// Draw visible items
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
{
|
||||
// Update item rectangle y position for next item
|
||||
itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING));
|
||||
|
||||
if (i == itemSelected)
|
||||
{
|
||||
GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), guiAlpha));
|
||||
GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), guiAlpha));
|
||||
}
|
||||
else if (i == itemFocused)
|
||||
{
|
||||
GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), guiAlpha));
|
||||
GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), guiAlpha));
|
||||
}
|
||||
else GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Avoid this function, use icon instead or 'v'
|
||||
DrawTriangle(RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2 },
|
||||
RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 },
|
||||
RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 10, bounds.y + bounds.height/2 - 2 },
|
||||
Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
|
||||
|
||||
//GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
|
||||
// GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
Vector2 mousePoint = GetMousePosition();
|
||||
|
||||
// Check if mouse has been pressed or released outside limits
|
||||
if (!CheckCollisionPointRec(mousePoint, boundsOpen))
|
||||
{
|
||||
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
*active = itemSelected;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
#define TD_UI_MSGBOX_WIDTH 320
|
||||
#define TD_UI_MSGBOX_HEIGHT 200
|
||||
|
||||
void texed_draw_msgbox(zpl_aabb2 r) {
|
||||
if (!ctx.msgbox.visible) return;
|
||||
DrawRectangle(r.min.x, r.min.y, r.max.x, r.max.y, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), 0.85f));
|
||||
|
||||
Rectangle rec = {
|
||||
r.max.x/2.0f - TD_UI_MSGBOX_WIDTH/2.0f,
|
||||
r.max.y/2.0f - TD_UI_MSGBOX_HEIGHT/2.0f,
|
||||
TD_UI_MSGBOX_WIDTH,
|
||||
TD_UI_MSGBOX_HEIGHT,
|
||||
};
|
||||
|
||||
ctx.msgbox.result = GuiMessageBox(rec, ctx.msgbox.title, ctx.msgbox.message, ctx.msgbox.buttons);
|
||||
if (ctx.msgbox.result != -1) {
|
||||
ctx.msgbox.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
bool IsCtrlAcceleratorPressed(char key) {
|
||||
return (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)) && (char)GetKeyPressed() == key;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool GuiValueBoxEco(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) {
|
||||
#if !defined(VALUEBOX_MAX_CHARS)
|
||||
#define VALUEBOX_MAX_CHARS 32
|
||||
#endif
|
||||
|
||||
static int framesCounter = 0; // Required for blinking cursor
|
||||
|
||||
GuiControlState state = guiState;
|
||||
bool pressed = false;
|
||||
|
||||
char textValue[VALUEBOX_MAX_CHARS + 1] = "\0";
|
||||
sprintf(textValue, "%i", *value);
|
||||
|
||||
Rectangle textBounds = { 0 };
|
||||
if (text != NULL)
|
||||
{
|
||||
textBounds.width = (float)GetTextWidth(text);
|
||||
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
|
||||
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
|
||||
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
|
||||
if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
|
||||
}
|
||||
|
||||
// Update control
|
||||
//--------------------------------------------------------------------
|
||||
if ((state != GUI_STATE_DISABLED) && !guiLocked)
|
||||
{
|
||||
Vector2 mousePoint = GetMousePosition();
|
||||
|
||||
bool valueHasChanged = false;
|
||||
|
||||
if (editMode)
|
||||
{
|
||||
state = GUI_STATE_PRESSED;
|
||||
|
||||
framesCounter++;
|
||||
|
||||
int keyCount = (int)strlen(textValue);
|
||||
|
||||
// Only allow keys in range [48..57]
|
||||
if (keyCount < VALUEBOX_MAX_CHARS)
|
||||
{
|
||||
if (GetTextWidth(textValue) < bounds.width)
|
||||
{
|
||||
int key = GetCharPressed();
|
||||
if ((key >= 48) && (key <= 57))
|
||||
{
|
||||
textValue[keyCount] = (char)key;
|
||||
keyCount++;
|
||||
valueHasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete text
|
||||
if (keyCount > 0)
|
||||
{
|
||||
if (IsKeyPressed(KEY_BACKSPACE))
|
||||
{
|
||||
keyCount--;
|
||||
textValue[keyCount] = '\0';
|
||||
framesCounter = 0;
|
||||
if (keyCount < 0) keyCount = 0;
|
||||
valueHasChanged = true;
|
||||
}
|
||||
else if (IsKeyDown(KEY_BACKSPACE))
|
||||
{
|
||||
if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--;
|
||||
textValue[keyCount] = '\0';
|
||||
if (keyCount < 0) keyCount = 0;
|
||||
valueHasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valueHasChanged) *value = TextToInteger(textValue);
|
||||
|
||||
if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*value > maxValue) *value = maxValue;
|
||||
else if (*value < minValue) *value = minValue;
|
||||
|
||||
if (CheckCollisionPointRec(mousePoint, bounds))
|
||||
{
|
||||
state = GUI_STATE_FOCUSED;
|
||||
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pressed) framesCounter = 0;
|
||||
}
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Draw control
|
||||
//--------------------------------------------------------------------
|
||||
Color baseColor = BLANK;
|
||||
if (state == GUI_STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
|
||||
else if (state == GUI_STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
|
||||
|
||||
// WARNING: BLANK color does not work properly with Fade()
|
||||
GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor);
|
||||
GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha));
|
||||
|
||||
// Draw blinking cursor
|
||||
if ((state == GUI_STATE_PRESSED) && (editMode && ((framesCounter/20)%2 == 0)))
|
||||
{
|
||||
// NOTE: ValueBox internal text is always centered
|
||||
Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 2, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 1, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
|
||||
GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha));
|
||||
}
|
||||
|
||||
// Draw text label if provided
|
||||
if (text != NULL) GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT)? GUI_TEXT_ALIGN_LEFT : GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
static inline
|
||||
char const *prettify_op_name(int idx) {
|
||||
static char name[200] = {0};
|
||||
zpl_snprintf(name, 200, "%s %s", default_cats[default_ops[idx].cat].icon, default_ops[idx].name+4);
|
||||
zpl_str_to_lower(name);
|
||||
|
||||
char *p = (char*)zpl_str_skip(name, ' ')+1;
|
||||
do {
|
||||
*p = zpl_char_to_upper(*p);
|
||||
p = (char*)zpl_str_skip(p, '_');
|
||||
if (*p) *p = ' ';
|
||||
} while(*p++);
|
||||
|
||||
return name;
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
#include "game.h"
|
||||
#include "entity.h"
|
||||
#include "utils/options.h"
|
||||
#include "editors/texed.h"
|
||||
#include "signal_handling.h"
|
||||
#include "profiler.h"
|
||||
|
||||
|
@ -36,14 +35,6 @@ int main(int argc, char** argv) {
|
|||
zpl_opts_add(&opts, "ws", "world-size", "amount of chunks within a world (single axis)", ZPL_OPTS_INT);
|
||||
zpl_opts_add(&opts, "n", "npc-count", "amount of demo npcs to spawn", ZPL_OPTS_INT);
|
||||
|
||||
zpl_opts_add(&opts, "td", "texed", "run texture editor", ZPL_OPTS_FLAG);
|
||||
{
|
||||
// NOTE(zaklaus): here to satisfy cli parser, otherwise we handle it inside texed
|
||||
zpl_opts_add(&opts, "td-i", "texed-import", "convert an image to ecotex format", ZPL_OPTS_STRING);
|
||||
zpl_opts_add(&opts, "td-ec", "texed-export-cc", "export ecotex image to C header file", ZPL_OPTS_STRING);
|
||||
zpl_opts_add(&opts, "td-ep", "texed-export-png", "export ecotex image to PNG format", ZPL_OPTS_STRING);
|
||||
}
|
||||
|
||||
uint32_t ok = zpl_opts_compile(&opts, argc, argv);
|
||||
|
||||
if (!ok) {
|
||||
|
@ -60,11 +51,6 @@ int main(int argc, char** argv) {
|
|||
uint16_t world_size = zpl_opts_integer(&opts, "world-size", DEFAULT_WORLD_SIZE);
|
||||
uint32_t npc_count = zpl_opts_integer(&opts, "npc-count", 1000);
|
||||
|
||||
if (zpl_opts_has_arg(&opts, "texed")) {
|
||||
texed_run(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (zpl_opts_has_arg(&opts, "random-seed")) {
|
||||
zpl_random rnd={0};
|
||||
zpl_random_init(&rnd);
|
||||
|
|
Loading…
Reference in New Issue