move texed to its own repo

isolation_bkp/dynres
Dominik Madarász 2021-05-19 11:57:49 +02:00
parent a059b9826b
commit 4ea2ba6188
14 changed files with 412 additions and 2702 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
art/gen/road.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
art/lava.ecotex 100644

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -1,4 +0,0 @@
#pragma once
#include "system.h"
void texed_run(int,char**);

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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},
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);