commit c9e78fd81016fa17c4425b29630255246861c39e Author: Sen Date: Tue Sep 2 14:43:36 2025 +0200 first and last commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed7a2a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/skcblitz +/skc.cfg diff --git a/alsasnd.h b/alsasnd.h new file mode 100644 index 0000000..d8456b7 --- /dev/null +++ b/alsasnd.h @@ -0,0 +1,207 @@ + +#define SND_MSGUNDER + +typedef struct { + snd_pcm_t *handle; + void *buffer; + int *conv; + uint samplerate; + uint blocksize; + uint channels; + uint width; + uint smppos; + uint devbufsize; +#ifdef SND_MSGUNDER + uint underruns; + uint lastunder; + uint pushed; + uint pad; +#else + uint pad[4]; +#endif +} aud_handle; + +void aud_prepare(aud_handle *dev) { + if(dev->handle == NULL) { + return; + } + memset(dev->buffer, 0, dev->blocksize * dev->width * dev->channels); + dev->smppos = 0; + int rc = 0; + uint tries = 0; + while(rc != dev->blocksize) { + if(tries++ >= 256) { + snd_logw(STR_AUD_PREPERR); + return; + } + rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize); + if(rc == -EAGAIN || (rc >= 0 && (uint)rc < dev->blocksize)) { + snd_pcm_wait(dev->handle, 100); + } + else if(rc == -EPIPE) { + snd_pcm_prepare(dev->handle); + } + } + snd_pcm_nonblock(dev->handle, 1); + for(int z = 0; z < 16; z++) { + rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize); + if(rc == -EAGAIN || (rc >= 0 && (uint)rc < dev->blocksize)) { + snd_pcm_wait(dev->handle, 100); + } + else if(rc == -EPIPE) { + snd_pcm_prepare(dev->handle); + } + } + snd_pcm_nonblock(dev->handle, 0); +} + +void aud_write(aud_handle *dev, void *data, uint samples) { + int rc; + uint blocksize = dev->blocksize; + while(samples > 0) { + blocksize = samples < blocksize ? samples : blocksize; + if(dev->handle) { + rc = snd_pcm_writei(dev->handle, data, blocksize); + } + data += blocksize * dev->channels; + samples -= blocksize; + if(dev->handle) { + if(rc == -EAGAIN || (rc >= 0 && (uint)rc < blocksize)) { + snd_pcm_wait(dev->handle, 100); + } + else if(rc == -EPIPE) { +#ifdef SND_MSGUNDER + if(dev->underruns == 0) { + dev->lastunder = dev->pushed; + } + dev->underruns += 1; +#endif + snd_pcm_prepare(dev->handle); + } + else if(rc < 0) { + snd_loge(STR_AUD_WRITERR, snd_strerror(rc), rc); + } +#ifdef SND_MSGUNDER + else { + dev->pushed += 1; + } + if((dev->underruns > 0) && ((dev->pushed - dev->lastunder) >= (dev->samplerate / dev->blocksize))) { + snd_logw(STR_AUD_UNDERR, dev->underruns); + dev->underruns = 0; + } +#endif + } + } +} + +uint aud_open_dev(aud_handle *dev, const char *devname, uint samplerate, byte channels, byte width, uint frames, uint bufsize) { + dev->channels = (uint)channels; + dev->samplerate = samplerate; + dev->width = (uint)width; + dev->smppos = 0; +#ifdef SND_MSGUNDER + dev->underruns = 0; + dev->lastunder = 0; + dev->pushed = 0; +#endif + dev->handle = NULL; + dev->buffer = NULL; + dev->conv = NULL; + int rc; + int subdir = 0; + snd_pcm_hw_params_t *params = NULL; + rc = snd_pcm_open(&dev->handle, devname, SND_PCM_STREAM_PLAYBACK, 0); + if(rc < 0) { + snd_loge(STR_AUD_OPNERR, devname, snd_strerror(rc), rc); + return 0; + } + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(dev->handle, params); + snd_pcm_hw_params_set_access(dev->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if(snd_pcm_hw_params_set_format(dev->handle, params, (dev->width == 4) ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S16_LE) != 0) { + dev->width = (width == 4) ? 2 : 4; + if(snd_pcm_hw_params_set_format(dev->handle, params, (dev->width == 4) ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S16_LE) != 0) { + snd_loge(STR_AUD_FMTERR, devname); + snd_pcm_close(dev->handle); + return 0; + } + else { + snd_logw(STR_AUD_FMTCH, devname, width * 8, dev->width * 8); + } + } + snd_pcm_hw_params_set_channels(dev->handle, params, dev->channels); + snd_pcm_hw_params_set_rate_near(dev->handle, params, &dev->samplerate, &subdir); + snd_pcm_uframes_t framesize = frames; + snd_pcm_hw_params_set_period_size_near(dev->handle, params, &framesize, &subdir); + snd_pcm_uframes_t buffersize = bufsize; + if(bufsize > 0) { + snd_pcm_hw_params_set_buffer_size_near(dev->handle, params, &buffersize); + } + rc = snd_pcm_hw_params(dev->handle, params); + if(rc < 0) { + snd_loge(STR_AUD_HWERR, devname, snd_strerror(rc), rc); + snd_pcm_close(dev->handle); + return 0; + } + snd_pcm_hw_params_get_period_size(params, &framesize, &subdir); + snd_pcm_hw_params_get_buffer_size(params, &buffersize); + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(dev->handle, swparams); + // snd_pcm_sw_params_set_avail_min(dev->handle, swparams, framesize); + // snd_pcm_sw_params_set_start_threshold(dev->handle, swparams, 0); + // snd_pcm_sw_params_set_stop_threshold(dev->handle, swparams, buffersize); + rc = snd_pcm_sw_params(dev->handle, swparams); + if(rc < 0) { + snd_loge(STR_AUD_SWERR, devname, snd_strerror(rc), rc); + snd_pcm_close(dev->handle); + return 0; + } + + dev->blocksize = framesize; + dev->devbufsize = buffersize; + dev->buffer = malloc(dev->blocksize * dev->width * dev->channels); + if(dev->width == 4) + dev->conv = malloc(sizeof(int) * dev->channels); + snd_pcm_wait(dev->handle, -1); + aud_prepare(dev); + return dev->samplerate; +} + +void aud_push_raw(aud_handle *dev, void *data) { + memcpy(&((byte*)(dev->buffer))[dev->smppos*dev->channels*dev->width], data, dev->width * dev->channels); + dev->smppos++; + if(dev->smppos == dev->blocksize) { + aud_write(dev, dev->buffer, dev->blocksize); + dev->smppos = 0; + } +} + +void aud_push(aud_handle *dev, short *data) { + if(dev->width == 4) { + for(int h = 0; h < dev->channels; h++) { + dev->conv[h] = ((int)data[h]) << 16; + } + aud_push_raw(dev, dev->conv); + } + else { + aud_push_raw(dev, data); + } +} + +void aud_flush(aud_handle *dev) { + if(dev->smppos != 0) { + aud_write(dev, dev->buffer, dev->smppos); + dev->smppos = 0; + } +} + +void aud_close(aud_handle *dev) { + if(dev->handle) { + snd_pcm_drain(dev->handle); + snd_pcm_close(dev->handle); + } + free(dev->buffer); + if(dev->conv) + free(dev->conv); +} diff --git a/argparse.h b/argparse.h new file mode 100644 index 0000000..8fc694b --- /dev/null +++ b/argparse.h @@ -0,0 +1,208 @@ + +void arg_help(argp_t *argspec, char *prog) { + arg_t *arg_vec = argspec->arg_vec; + fprintf(stderr, STR_ARG_USAGE": %s\n [-? ("STR_ARG_HELP")]\n", prog); + for(int h = 0; h < argspec->arg_count; h++) { + arg_t *arg = &arg_vec[h]; + if(arg->spec) { + if(arg->arg) { + if((arg->min == 0) && (arg->max == 0)) { + fprintf(stderr, " [-%c <%s>]\n", arg->spec, arg->name); + } + else { + fprintf(stderr, " [-%c <%s (%d-%d)>]\n", arg->spec, arg->name, arg->min, arg->max); + } + } + else { + fprintf(stderr, " [-%c (%s)]\n", arg->spec, arg->name); + } + } + } + for(int h = 0; h < argspec->arg_max; h++) { + arg_t *arg = &arg_vec[h]; + char nb = (h < argspec->arg_min) ? '<' : '['; + char eb = (h < argspec->arg_min) ? '>' : ']'; + if((arg->min == 0) && (arg->max == 0)) { + fprintf(stderr, " %c%s%s%c\n", nb, arg->name, (arg->value == NULL) ? "" : ", ...", eb); + } + else { + fprintf(stderr, " %c%s (%d-%d)%c\n", nb, arg->name, arg->min, arg->max, eb); + } + } +} + +char arg_process(arg_t *arg_vec, char *arg, int off) { + if((arg_vec[off].min == 0) && (arg_vec[off].max == 0)) { + if(arg_vec[off].value != NULL) { + arg_vec[off].str[*(arg_vec[off].value)] = arg; + *(arg_vec[off].value) += 1; + } + else { + *(arg_vec[off].str) = arg; + } + } + else { + errno = 0; + char *endptr; + *(arg_vec[off].value) = strtol(arg, &endptr, 10); + if((errno != 0) || (endptr[0] != 0)) { + if(arg_vec[off].spec) { + fprintf(stderr, STR_ARG_EINT_V"\n", arg_vec[off].name, arg_vec[off].spec); + } + else { + fprintf(stderr, STR_ARG_EINT_P"\n", arg_vec[off].name, off + 1); + } + return 0; + } + if((*(arg_vec[off].value) < arg_vec[off].min) || (*(arg_vec[off].value) > arg_vec[off].max)) { + if(arg_vec[off].spec) { + fprintf(stderr, STR_ARG_ERANGE_V"\n", arg_vec[off].name, arg_vec[off].spec, arg_vec[off].min, arg_vec[off].max); + } + else { + fprintf(stderr, STR_ARG_ERANGE_P"\n", arg_vec[off].name, off + 1, arg_vec[off].min, arg_vec[off].max); + } + return 0; + } + } + return 1; +} + +char arg_parse(argp_t *argspec, int argc, char **argv) { + uint arg_min = argspec->arg_min; + uint arg_max = argspec->arg_max; + uint arg_count = argspec->arg_count; + arg_t *arg_vec = argspec->arg_vec; + uint idx = 0; + uint sp = 0; + char sa = 0; + for(int h = 1; h < argc; h++) { + char *arg = argv[h]; + if((sa == 0) && (arg[0] == '-') && (arg[1] != 0)) { + arg += 1; + if(arg[0] == '?') { + arg_help(argspec, argv[0]); + free(argspec); + return 0; + } + while((arg[0] != 0) && (sa == 0)) { + int f = 0; + for(int h = 0; h < arg_count; h++) { + if((arg_vec[h].arg ^ 1) && (arg[0] == arg_vec[h].spec)) { + *(arg_vec[h].value) = 1; + arg += 1; + f++; + break; + } + else if(arg[0] == arg_vec[h].spec) { + sa = arg[0]; + sp = h; + arg += 1; + f++; + break; + } + } + if(f == 0) { + fprintf(stderr, STR_ARG_UNKNOWN"\n", arg[0]); + free(argspec); + return 0; + } + } + if(arg[0] == 0) { + continue; + } + } + else if(sa == 0) { + if((arg[0] == '\\') && (arg[1] == '-')) { + arg += 1; + } + if((idx < arg_max) || ((arg_max > 0) && (arg_vec[arg_max-1].min == 0) && (arg_vec[arg_max-1].max == 0) && (arg_vec[arg_max-1].value != NULL))) { + if(!arg_process(arg_vec, arg, (idx >= arg_max) ? (arg_max-1) : idx)) { + free(argspec); + return 0; + } + idx += 1; + continue; + } + else { + fprintf(stderr, STR_ARG_EMORE"\n", arg_max); + free(argspec); + return 0; + } + } + if(!arg_process(arg_vec, arg, sp)) { + free(argspec); + return 0; + } + sa = 0; + } + if(sa != 0) { + fprintf(stderr, STR_ARG_EREQARG"\n", arg_vec[sp].name, sa); + free(argspec); + return 0; + } + if(idx < arg_min) { + fprintf(stderr, STR_ARG_ELESS"\n", arg_min); + free(argspec); + return 0; + } + free(argspec); + return 1; +} + +void arg_add_gen(argp_t *argspec, char spec, const char *name, int min, int max, char argreq, int *value, char **str) { + arg_t *arg = &argspec->arg_vec[(argspec->arg_count)++]; + arg->spec = spec; + arg->arg = argreq; + arg->min = min; + arg->max = max; + arg->value = value; + arg->str = str; + arg->name = name; +} + +void arg_add_int(argp_t *argspec, char spec, const char *name, int min, int max, int *value) { + arg_add_gen(argspec, spec, name, min, max, 1, value, NULL); +} + +void arg_add_str(argp_t *argspec, char spec, const char *name, char **value) { + arg_add_gen(argspec, spec, name, 0, 0, 1, NULL, value); +} + +void arg_add_bool(argp_t *argspec, char spec, const char *name, int *value) { + arg_add_gen(argspec, spec, name, 0, 0, 0, value, NULL); +} + +void arg_add_nint(argp_t *argspec, const char *name, char req, int min, int max, int *value) { + arg_add_gen(argspec, 0, name, min, max, 0, value, NULL); + argspec->arg_max += 1; + argspec->arg_min += req ? 1 : 0; +} + +void arg_add_nstr(argp_t *argspec, const char *name, char req, char **value) { + arg_add_gen(argspec, 0, name, 0, 0, 0, NULL, value); + argspec->arg_max += 1; + argspec->arg_min += req ? 1 : 0; +} + +void arg_add_nbool(argp_t *argspec, const char *name, char req, int *value) { + arg_add_gen(argspec, 0, name, 0, 1, 0, value, NULL); + argspec->arg_max += 1; + argspec->arg_min += req ? 1 : 0; +} + +void arg_add_vstr(argp_t *argspec, const char *name, char req, char **svalue, int *ivalue) { + arg_add_gen(argspec, 0, name, 0, 0, 0, ivalue, svalue); + argspec->arg_max = 1; + argspec->arg_min += req ? 1 : 0; +} + +argp_t *arg_alloc(uint count) { + char *data = malloc(sizeof(argp_t) + (count * sizeof(arg_t))); + argp_t *argspec = (argp_t*)data; + data += sizeof(argp_t); + argspec->arg_vec = (arg_t*)data; + argspec->arg_count = 0; + argspec->arg_min = 0; + argspec->arg_max = 0; + return argspec; +} diff --git a/axe_ork.h b/axe_ork.h new file mode 100644 index 0000000..6818029 --- /dev/null +++ b/axe_ork.h @@ -0,0 +1,4110 @@ +/* + axe_ork v.X - X11 GLX windows + based on GLFW 3.3.8 - www.glfw.org - A library for OpenGL, window and input + ** original license text below ** + + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2019 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ + +void win_fbsize(window_t *win, int x, int y); +void win_pos(window_t *win, int x, int y); +void win_mouse(window_t *win, int x, int y); +void win_scroll(window_t *win, int x, int y); +void win_button(window_t *win, int btn, int act); +void win_key(window_t *win, int key, int act); +void win_char(window_t *win, uint code); +void win_drop(window_t *win, int num, const char **paths); +void win_redraw(window_t *win); +void win_closed(window_t *win); +void win_focus(window_t *win, byte focus); + +int ork_getglxattr(GLXFBConfig fbconfig, int attrib) +{ + int value; + glXGetFBConfigAttrib(wcf.display, fbconfig, attrib, &value); + return value; +} + +// by default, making a context non-current implicitly forces a pipeline flush +void wcf_selcontext(window_t* window) +{ + if (window) + { + if (!glXMakeCurrent(wcf.display, + window->window, + wcf.glx)) + { + loge(LOG_GLX, "Failed to make context current"); + return; + } + } + else + { + if (!glXMakeCurrent(wcf.display, None, NULL)) + { + loge(LOG_GLX, "Failed to clear current context"); + return; + } + } + + wcf.current = window; +} + +void wcf_swap(window_t* window) +{ + glXSwapBuffers(wcf.display, window->window); +} + +byte ork_isinstr(const char* string, const char* extensions) +{ + const char* start = extensions; + + for (;;) + { + const char* where; + const char* terminator; + + where = strstr(start, string); + if (!where) + return 0; + + terminator = where + strlen(string); + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + break; + } + + start = terminator; + } + + return 1; +} + +byte ork_ext_available(const char* extension) +{ + const char* extensions = + glXQueryExtensionsString(wcf.display, wcf.screen); + if (extensions) + { + if (ork_isinstr(extension, extensions)) + return 1; + } + + return 0; +} + +void *ork_glxproc(const char* procname) +{ + if (wcf.GetProcAddress) + return wcf.GetProcAddress((const byte*) procname); + else if (wcf.GetProcAddressARB) + return wcf.GetProcAddressARB((const byte*) procname); + else + return dlsym(wcf.handle, procname); +} + +int ork_error_handler(Display *display, XErrorEvent* event) { + if(wcf.display != display) + return 0; + wcf.error_code = event->error_code; + return 0; +} + +void ork_set_errhandler() { + wcf.error_code = Success; + wcf.error_handler = XSetErrorHandler(ork_error_handler); +} + +void ork_unset_errhandler() { + XSync(wcf.display, False); + XSetErrorHandler(wcf.error_handler); + wcf.error_handler = NULL; +} + +#define set_attrib(a, v) \ +{ \ + sys_assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +byte ork_create_glx(GLXFBConfig native) { + int attribs[40]; + + ork_set_errhandler(); + + int index = 0; + + if(wcf.debug) { + set_attrib(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB); + } + // else if(wcf.ARB_create_context_no_error) { + // set_attrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, 1); + // } + + set_attrib(None, None); + + wcf.glx = + wcf.CreateContextAttribsARB(wcf.display, + native, + NULL, + True, + attribs); + + ork_unset_errhandler(); + + if (!wcf.glx) + { + char buffer[ERR_LOG_SIZE]; + XGetErrorText(wcf.display, wcf.error_code, buffer, ERR_LOG_SIZE); + loge(LOG_GLX, "Failed to create context: %s", buffer); + return 0; + } + + return 1; +} + +#undef set_attrib + +byte ork_sel_glxcfg(Visual **visual, GLXFBConfig *native, int *depth) { + XVisualInfo *result; + GLXFBConfig *configs; + int i, count; + configs = glXGetFBConfigs(wcf.display, wcf.screen, &count); + if(!configs || !count) { + loge(LOG_GLX, "Konnte keine (0) FB-Konfiguration finden"); + return 0; + } + for(i = 0; i < count; i++) { + const GLXFBConfig n = configs[i]; + if(!(ork_getglxattr(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) + continue; + if(!(ork_getglxattr(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) + continue; + if(!ork_getglxattr(n, GLX_DOUBLEBUFFER)) + continue; + if(ork_getglxattr(n, GLX_RED_SIZE) != 8) + continue; + if(ork_getglxattr(n, GLX_GREEN_SIZE) != 8) + continue; + if(ork_getglxattr(n, GLX_BLUE_SIZE) != 8) + continue; + if(ork_getglxattr(n, GLX_ALPHA_SIZE) != 8) + continue; + if(ork_getglxattr(n, GLX_DEPTH_SIZE) != 24) + continue; + if(ork_getglxattr(n, GLX_STENCIL_SIZE) != 8) + continue; + if(wcf.ARB_multisample && ork_getglxattr(n, GLX_SAMPLE_BUFFERS)) + continue; + result = glXGetVisualFromFBConfig(wcf.display, n); + if(!result) + continue; + XRenderPictFormat* pf = XRenderFindVisualFormat(wcf.display, result->visual); + if(!pf || !pf->direct.alphaMask) { + XFree(result); + continue; + } + *native = n; + break; + } + XFree(configs); + if(i < count) { + logd(LOG_GLX, "FB-Konfiguration #%d Ausgewählt", i); + } + else { + loge(LOG_GLX, "Konnte keine geeignete FB-Konfiguration finden"); + return 0; + } + if(result->visual) { + *visual = result->visual; + *depth = result->depth; + } + else { + *visual = DefaultVisual(wcf.display, wcf.screen); + *depth = DefaultDepth(wcf.display, wcf.screen); + } + if((*depth) != 24 && (*depth) != 32) { + loge(LOG_X11, "Ungültige Farbtiefe: %d Bits", *depth); + XFree(result); + return 0; + } + logd(LOG_X11, "Farbtiefe: %d Bits", *depth); + XFree(result); + return 1; +} + +// Based on cutef8 by Jeff Bezanson (Public Domain) +size_t ork_encode_utf(char* s, uint codepoint) +{ + size_t count = 0; + + if (codepoint < 0x80) + s[count++] = (char) codepoint; + else if (codepoint < 0x800) + { + s[count++] = (codepoint >> 6) | 0xc0; + s[count++] = (codepoint & 0x3f) | 0x80; + } + else if (codepoint < 0x10000) + { + s[count++] = (codepoint >> 12) | 0xe0; + s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; + s[count++] = (codepoint & 0x3f) | 0x80; + } + else if (codepoint < 0x110000) + { + s[count++] = (codepoint >> 18) | 0xf0; + s[count++] = ((codepoint >> 12) & 0x3f) | 0x80; + s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; + s[count++] = (codepoint & 0x3f) | 0x80; + } + + return count; +} + +// Based on cutef8 by Jeff Bezanson (Public Domain) +uint ork_decode_utf(const char** s) +{ + uint codepoint = 0, count = 0; + static const uint offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + codepoint = (codepoint << 6) + (byte) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + sys_assert(count <= 6); + return codepoint - offsets[count - 1]; +} + +char* ork_latin1_utf(const char* source) +{ + size_t size = 1; + const char* sp; + + for (sp = source; *sp; sp++) + size += (*sp & 0x80) ? 2 : 1; + + char* target = mem_zeros(size, MEM_MISC); + char* tp = target; + + for (sp = source; *sp; sp++) + tp += ork_encode_utf(tp, *sp); + + return target; +} + +char** ork_parse_uri(char* text, int* count) +{ + const char* prefix = "file://"; + char** paths = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + char* path; + + text = NULL; + + if (line[0] == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + { + line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } + + (*count)++; + + path = mem_zeros(strlen(line) + 1, MEM_MISC); + paths = mem_realloc(paths, *count * sizeof(char*), MEM_MISC); + paths[*count - 1] = path; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *path = (char) strtol(digits, NULL, 16); + line += 2; + } + else + *path = *line; + + path++; + line++; + } + } + + return paths; +} + +char* ork_strdup(const char* source) +{ + const size_t length = strlen(source); + char* result = mem_zeros(length + 1, MEM_MISC); + strcpy(result, source); + return result; +} + +// Returns whether the event is a selection event +Bool ork_is_selevent(Display* display, XEvent* event, XPointer pointer) +{ + if (event->xany.window != wcf.helper_window) + return False; + + return event->type == SelectionRequest || + event->type == SelectionNotify || + event->type == SelectionClear; +} + +// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window +Bool ork_is_frextevent(Display* display, XEvent* event, XPointer pointer) +{ + window_t* window = (window_t*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == window->handle && + event->xproperty.atom == wcf.NET_FRAME_EXTENTS; +} + +// Returns whether it is a property event for the specified selection transfer +Bool ork_is_selnewvalue(Display* display, XEvent* event, XPointer pointer) +{ + XEvent* notification = (XEvent*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == notification->xselection.requestor && + event->xproperty.atom == notification->xselection.property; +} + +void ork_key(window_t* window, int key, int action) +{ + if (key >= 0 && key <= KEYSYM_LAST) + { + byte repeated = 0; + + if (action == KEY_RELEASE && window->keys[key] == KEY_RELEASE) + return; + + if (action == KEY_PRESS && window->keys[key] == KEY_PRESS) + repeated = 1; + + window->keys[key] = (char) action; + + if (repeated) + action = KEY_REPEAT; + } + + win_key(window, key, action); +} + +void ork_button(window_t* window, int button, int action) +{ + if (button < 0 || button >= MOUSE_BTNS) + return; + + window->buttons[button] = (char) action; + + win_button(window, button, action); +} + +void ork_mouse(window_t* window, int xpos, int ypos) +{ + if (window->virtual_cur_x == xpos && window->virtual_cur_y == ypos) + return; + + window->virtual_cur_x = xpos; + window->virtual_cur_y = ypos; + + win_mouse(window, xpos, ypos); +} + +void ork_getcur(window_t* window, int* xpos, int* ypos) +{ + Window root, child; + int rootX, rootY, childX, childY; + uint mask; + + XQueryPointer(wcf.display, window->handle, + &root, &child, + &rootX, &rootY, &childX, &childY, + &mask); + + if (xpos) + *xpos = childX; + if (ypos) + *ypos = childY; +} + +void ork_setcur(window_t* window, int x, int y) +{ + // Store the new position so it can be recognized later + window->warp_cur_x = x; + window->warp_cur_y = y; + + XWarpPointer(wcf.display, None, window->handle, + 0,0,0,0, x, y); + XFlush(wcf.display); +} + +byte ork_focused(window_t* window) +{ + Window focused; + int state; + + XGetInputFocus(wcf.display, &focused, &state); + return window->handle == focused; +} + +void ork_updatecur(window_t* window) +{ + if (window->cursor_set) + { + if (window->cursor) + { + XDefineCursor(wcf.display, window->handle, + window->cursor->handle); + } + else + XUndefineCursor(wcf.display, window->handle); + } + else + { + XDefineCursor(wcf.display, window->handle, + wcf.empty_cursor); + } +} + +void wcf_getsize(window_t* window, int* width, int* height) +{ + XWindowAttributes attribs; + XGetWindowAttributes(wcf.display, window->handle, &attribs); + + if (width) + *width = attribs.width; + if (height) + *height = attribs.height; +} + +void ork_enablecur(window_t* window) +{ + wcf.grab_cur_window = NULL; + XUngrabPointer(wcf.display, CurrentTime); + ork_setcur(window, + wcf.restore_cur_x, + wcf.restore_cur_y); + ork_updatecur(window); +} + +void ork_disablecur(window_t* window) +{ + wcf.grab_cur_window = window; + ork_getcur(window, + &wcf.restore_cur_x, + &wcf.restore_cur_y); + ork_updatecur(window); + int width, height; + wcf_getsize(window, &width, &height); + ork_setcur(window, width / 2.0, height / 2.0); + XGrabPointer(wcf.display, window->handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->handle, + wcf.empty_cursor, + CurrentTime); +} + +void wcf_grab(window_t* window, byte value) +{ + if (window->cursor_set == value) + return; + + window->cursor_set = value; + + ork_getcur(window, + &window->virtual_cur_x, + &window->virtual_cur_y); + if (!value) + { + if (ork_focused(window)) + ork_disablecur(window); + } + else if (wcf.grab_cur_window == window) + ork_enablecur(window); + else + ork_updatecur(window); + + XFlush(wcf.display); +} + +int wcf_getkey(window_t* window, int key) { + if(key < 0 || key > KEYSYM_LAST) + return KEY_RELEASE; + return (int)window->keys[key]; +} + +int wcf_getbutton(window_t* window, int button) { + if(button < 0 || button >= MOUSE_BTNS) + return KEY_RELEASE; + return (int)window->buttons[button]; +} + +void wcf_getcursor(window_t* window, int* xpos, int* ypos) +{ + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + if (window->cursor_set ^ 1) + { + if (xpos) + *xpos = window->virtual_cur_x; + if (ypos) + *ypos = window->virtual_cur_y; + } + else + ork_getcur(window, xpos, ypos); +} + +Cursor ork_createcur(const byte *image, int width, int height, int xhot, int yhot) +{ + int i; + Cursor cursor; + + if (!wcf.xcursor.handle) + return None; + + XcursorImage* native = XcursorImageCreate(width, height); + if (native == NULL) + return None; + + native->xhot = xhot; + native->yhot = yhot; + + const byte* source = image; + XcursorPixel* target = native->pixels; + + for (i = 0; i < width * height; i++, target++, source += 4) + { + uint alpha = source[3]; + + *target = (alpha << 24) | + ((byte) ((source[0] * alpha) / 255) << 16) | + ((byte) ((source[1] * alpha) / 255) << 8) | + ((byte) ((source[2] * alpha) / 255) << 0); + } + + cursor = XcursorImageLoadCursor(wcf.display, native); + XcursorImageDestroy(native); + + return cursor; +} + +void wcf_set_curimg(window_t* window, cursor_t* cursor) +{ + window->cursor = cursor; + + if (window->cursor_set) + { + ork_updatecur(window); + XFlush(wcf.display); + } +} + +void wcf_destroy_curimg(cursor_t* cursor) +{ + if (cursor == NULL) + return; + + // Make sure the cursor is not being used by any window + { + window_t* window; + + for (window = wcf.window_list; window; window = window->next) + { + if (window->cursor == cursor) + wcf_set_curimg(window, NULL); + } + } + + if (cursor->handle) + XFreeCursor(wcf.display, cursor->handle); + + // Unlink cursor from global linked list + { + cursor_t** prev = &wcf.cursor_list; + + while (*prev != cursor) + prev = &((*prev)->next); + + *prev = cursor->next; + } + + mem_free(cursor); +} + +/*! + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The cursor hotspot is specified in pixels, relative to the upper-left corner + * of the cursor image. The X-axis points to the right and the Y-axis points down. + */ +cursor_t* wcf_create_curimg(const byte *image, int width, int height, int xhot, int yhot) +{ + cursor_t* cursor = mem_zeros(sizeof(cursor_t), MEM_MISC); + cursor->next = wcf.cursor_list; + wcf.cursor_list = cursor; + + cursor->handle = ork_createcur(image, width, height, xhot, yhot); + if (!cursor->handle) { + wcf_destroy_curimg(cursor); + return NULL; + } + + return cursor; +} + +byte ork_wait_poll(struct pollfd* fds, nfds_t count, double* timeout) +{ + for (;;) + { + if (timeout) + { + const ulong base = tmr_time(); + // const time_t seconds = (time_t) *timeout; + // const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + // const struct timespec ts = { seconds, nanoseconds }; + // const int result = ppoll(fds, count, &ts, NULL); + const int milliseconds = (int) (*timeout * 1e3); + const int result = poll(fds, count, milliseconds); + const int error = errno; // clock_gettime may overwrite our error + + *timeout -= (tmr_time() - base) / 1000000.0; + + if (result > 0) + return 1; + else if (result == -1 && error != EINTR && error != EAGAIN) + return 0; + else if (*timeout <= 0.0) + return 0; + } + else + { + const int result = poll(fds, count, -1); + if (result > 0) + return 1; + else if (result == -1 && errno != EINTR && errno != EAGAIN) + return 0; + } + } +} + +byte ork_wait_xevent(double* timeout) +{ + struct pollfd fd = { ConnectionNumber(wcf.display), POLLIN }; + + while (!XPending(wcf.display)) + { + if (!ork_wait_poll(&fd, 1, timeout)) + return 0; + } + + return 1; +} + +void wcf_setclip(const char* string) +{ + char* copy = ork_strdup(string); + mem_free(wcf.clipboard_str); + wcf.clipboard_str = copy; + + XSetSelectionOwner(wcf.display, + wcf.CLIPBOARD, + wcf.helper_window, + CurrentTime); + + if (XGetSelectionOwner(wcf.display, wcf.CLIPBOARD) != + wcf.helper_window) + { + loge(LOG_X11, "Failed to become owner of clipboard selection"); + } +} + +const char* wcf_getclip() +{ + char** selectionString = &wcf.clipboard_str; + const Atom targets[] = { wcf.UTF8_STRING, XA_STRING }; + const size_t targetCount = sizeof(targets) / sizeof(targets[0]); + + if (XGetSelectionOwner(wcf.display, wcf.CLIPBOARD) == + wcf.helper_window) + { + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return *selectionString; + } + + mem_free(*selectionString); + *selectionString = NULL; + + for (size_t i = 0; i < targetCount; i++) + { + char* data; + Atom actualType; + int actualFormat; + ulong itemCount, bytesAfter; + XEvent notification, dummy; + + XConvertSelection(wcf.display, + wcf.CLIPBOARD, + targets[i], + wcf.WCF_SELECTION, + wcf.helper_window, + CurrentTime); + + while (!XCheckTypedWindowEvent(wcf.display, + wcf.helper_window, + SelectionNotify, + ¬ification)) + { + ork_wait_xevent(NULL); + } + + if (notification.xselection.property == None) + continue; + + XCheckIfEvent(wcf.display, + &dummy, + ork_is_selnewvalue, + (XPointer) ¬ification); + + XGetWindowProperty(wcf.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (byte**) &data); + + if (actualType == wcf.INCR) + { + size_t size = 1; + char* string = NULL; + + for (;;) + { + while (!XCheckIfEvent(wcf.display, + &dummy, + ork_is_selnewvalue, + (XPointer) ¬ification)) + { + ork_wait_xevent(NULL); + } + + XFree(data); + XGetWindowProperty(wcf.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (byte**) &data); + + if (itemCount) + { + size += itemCount; + string = mem_realloc(string, size, MEM_MISC); + string[size - itemCount - 1] = '\0'; + strcat(string, data); + } + + if (!itemCount) + { + if (string) + { + if (targets[i] == XA_STRING) + { + *selectionString = ork_latin1_utf(string); + mem_free(string); + } + else + *selectionString = string; + } + + break; + } + } + } + else if (actualType == targets[i]) + { + if (targets[i] == XA_STRING) + *selectionString = ork_latin1_utf(data); + else + *selectionString = ork_strdup(data); + } + + XFree(data); + + if (*selectionString) + break; + } + + if (!*selectionString) + { + loge(LOG_X11, "Failed to convert selection to string"); + } + + return *selectionString; +} + +void wcf_sync(int interval) { + if (!wcf.current) + return; + if (wcf.EXT_swap_control) + { + wcf.SwapIntervalEXT(wcf.display, + wcf.current->window, + interval); + } + else if (wcf.MESA_swap_control) + wcf.SwapIntervalMESA(interval); + else if (wcf.SGI_swap_control) + { + if (interval > 0) + wcf.SwapIntervalSGI(interval); + } +} + +int ork_compmodes(const void* fp, const void* sp) +{ + const vidmode_t* fm = fp; + const vidmode_t* sm = sp; + const int farea = fm->width * fm->height; + const int sarea = sm->width * sm->height; + + // First sort on screen area + if (farea != sarea) + return farea - sarea; + + // Then sort on width + if (fm->width != sm->width) + return fm->width - sm->width; + + // Lastly sort on refresh rate + return fm->refresh - sm->refresh; +} + +const XRRModeInfo* ork_modeinfo(const XRRScreenResources* sr, RRMode id) +{ + for (int i = 0; i < sr->nmode; i++) + { + if (sr->modes[i].id == id) + return sr->modes + i; + } + + return NULL; +} + +vidmode_t ork_convmode(const XRRModeInfo* mi, + const XRRCrtcInfo* ci) +{ + vidmode_t mode; + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + mode.width = mi->height; + mode.height = mi->width; + } + else + { + mode.width = mi->width; + mode.height = mi->height; + } + + mode.refresh = (mi->hTotal && mi->vTotal) ? (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)) : 0; + + return mode; +} + +void ork_getmode(monitor_t* monitor, vidmode_t* mode) +{ + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(wcf.display, wcf.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, monitor->crtc); + + if (ci) + { + const XRRModeInfo* mi = ork_modeinfo(sr, ci->mode); + if (mi) // mi can be NULL if the monitor has been disconnected + *mode = ork_convmode(mi, ci); + + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + } + else + { + mode->width = DisplayWidth(wcf.display, wcf.screen); + mode->height = DisplayHeight(wcf.display, wcf.screen); + mode->refresh = 0; + } +} + +byte ork_getmodes(monitor_t* monitor) +{ + int count; + vidmode_t* modes; + + if (monitor->modes) + return 1; + + count = 0; + + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(wcf.display, wcf.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, monitor->crtc); + XRROutputInfo* oi = XRRGetOutputInfo(wcf.display, sr, monitor->output); + + modes = mem_zeros(oi->nmode * sizeof(vidmode_t), MEM_MISC); + + for (int i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = ork_modeinfo(sr, oi->modes[i]); + if ((mi->modeFlags & RR_Interlace) != 0) + continue; + + const vidmode_t mode = ork_convmode(mi, ci); + int j; + + for (j = 0; j < count; j++) + { + if (ork_compmodes(modes + j, &mode) == 0) + break; + } + + // Skip duplicate modes + if (j < count) + continue; + + count++; + modes[count - 1] = mode; + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + count = 1; + modes = mem_zeros(sizeof(vidmode_t), MEM_MISC); + ork_getmode(monitor, modes); + } + if (!modes) + return 0; + + qsort(modes, count, sizeof(vidmode_t), ork_compmodes); + + mem_free(monitor->modes); + monitor->modes = modes; + monitor->n_modes = count; + + return 1; +} + +ulong ork_getwinprop(Window window, + Atom property, + Atom type, + byte** value) +{ + Atom actualType; + int actualFormat; + ulong itemCount, bytesAfter; + + XGetWindowProperty(wcf.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + + return itemCount; +} + +byte ork_winvisible(window_t* window) +{ + XWindowAttributes wa; + XGetWindowAttributes(wcf.display, window->handle, &wa); + return wa.map_state == IsViewable; +} + +void ork_sendevent(window_t* window, Atom type, + long a, long b, long c, long d, long e) +{ + XEvent event = { ClientMessage }; + event.xclient.window = window->handle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(wcf.display, wcf.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +void ork_winfloating(window_t* window, byte enabled) +{ + if (!wcf.NET_WM_STATE || !wcf.NET_WM_STATE_ABOVE) + return; + + if (ork_winvisible(window)) + { + const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + ork_sendevent(window, + wcf.NET_WM_STATE, + action, + wcf.NET_WM_STATE_ABOVE, + 0, 1, 0); + } + else + { + Atom* states = NULL; + ulong i, count; + + count = ork_getwinprop(window->handle, + wcf.NET_WM_STATE, + XA_ATOM, + (byte**) &states); + + // NOTE: We don't check for failure as this property may not exist yet + // and that's fine (and we'll create it implicitly with append) + + if (enabled) + { + for (i = 0; i < count; i++) + { + if (states[i] == wcf.NET_WM_STATE_ABOVE) + break; + } + + if (i == count) + { + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (byte*) &wcf.NET_WM_STATE_ABOVE, + 1); + } + } + else if (states) + { + for (i = 0; i < count; i++) + { + if (states[i] == wcf.NET_WM_STATE_ABOVE) + break; + } + + if (i < count) + { + states[i] = states[count - 1]; + count--; + + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (byte*) states, count); + } + } + + if (states) + XFree(states); + } + + XFlush(wcf.display); +} + +void ork_updatemode(window_t* window) +{ + if (window->monitor) + { + if (wcf.xinerama.available && + wcf.NET_WM_FULLSCREEN_MONITORS) + { + ork_sendevent(window, + wcf.NET_WM_FULLSCREEN_MONITORS, + window->monitor->index, + window->monitor->index, + window->monitor->index, + window->monitor->index, + 0); + } + + if (wcf.NET_WM_STATE && wcf.NET_WM_STATE_FULLSCREEN) + { + ork_sendevent(window, + wcf.NET_WM_STATE, + _NET_WM_STATE_ADD, + wcf.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + // This is the butcher's way of removing window decorations + // Setting the override-redirect attribute on a window makes the + // window manager ignore the window completely (ICCCM, section 4) + // The good thing is that this makes undecorated full screen windows + // easy to do; the bad thing is that we have to do everything + // manually and some things (like iconify/restore) won't work at + // all, as those are tasks usually performed by the window manager + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes(wcf.display, + window->handle, + CWOverrideRedirect, + &attributes); + + window->override_redir = 1; + } + + // Enable compositor bypass + // if (!window->transparent) + // { + // const ulong value = 1; + + // XChangeProperty(wcf.display, window->handle, + // wcf.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + // PropModeReplace, (byte*) &value, 1); + // } + } + else + { + if (wcf.xinerama.available && + wcf.NET_WM_FULLSCREEN_MONITORS) + { + XDeleteProperty(wcf.display, window->handle, + wcf.NET_WM_FULLSCREEN_MONITORS); + } + + if (wcf.NET_WM_STATE && wcf.NET_WM_STATE_FULLSCREEN) + { + ork_sendevent(window, + wcf.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + wcf.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + XSetWindowAttributes attributes; + attributes.override_redirect = False; + XChangeWindowAttributes(wcf.display, + window->handle, + CWOverrideRedirect, + &attributes); + + window->override_redir = 0; + } + + // Disable compositor bypass + // if (!window->transparent) + // { + // XDeleteProperty(wcf.display, window->handle, + // wcf.NET_WM_BYPASS_COMPOSITOR); + // } + } +} + +void ork_updatehints(window_t* window, int width, int height) +{ + XSizeHints* hints = XAllocSizeHints(); + + if (!window->monitor) + { + if (window->resizable) + { + if (window->minwidth != -1 && + window->minheight != -1) + { + hints->flags |= PMinSize; + hints->min_width = window->minwidth; + hints->min_height = window->minheight; + } + + if (window->maxwidth != -1 && + window->maxheight != -1) + { + hints->flags |= PMaxSize; + hints->max_width = window->maxwidth; + hints->max_height = window->maxheight; + } + } + else + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + } + + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + + XSetWMNormalHints(wcf.display, window->handle, hints); + XFree(hints); +} + +void ork_disabledecor(window_t* window) +{ + struct + { + ulong flags; + ulong functions; + ulong decorations; + long input_mode; + ulong status; + } hints = {0}; + + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = 0; + + XChangeProperty(wcf.display, window->handle, + wcf.MOTIF_WM_HINTS, + wcf.MOTIF_WM_HINTS, 32, + PropModeReplace, + (byte*) &hints, + sizeof(hints) / sizeof(long)); +} + +void ork_unset_monitor(window_t* window) +{ + if (window->monitor->window != window) + return; + window->monitor->window = NULL; + monitor_t* monitor = window->monitor; + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + if (monitor->old_mode == None) + return; + + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(wcf.display, wcf.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, monitor->crtc); + + XRRSetCrtcConfig(wcf.display, + sr, monitor->crtc, + CurrentTime, + ci->x, ci->y, + monitor->old_mode, + ci->rotation, + ci->outputs, + ci->noutput); + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + + monitor->old_mode = None; + } +} + +void wcf_windowed(window_t* window, + int xpos, int ypos, + int width, int height) +{ + if(window->monitor) { + ork_disabledecor(window); + ork_winfloating(window, window->floating); + ork_unset_monitor(window); + window->monitor = NULL; + ork_updatehints(window, width, height); + ork_updatemode(window); + } + else if (!window->resizable) { + ork_updatehints(window, width, height); + } + + XMoveResizeWindow(wcf.display, window->handle, + xpos, ypos, width, height); + + XFlush(wcf.display); +} + +void wcf_setpos(window_t* window, int xpos, int ypos) +{ + if (window->monitor) + return; + // HACK: Explicitly setting PPosition to any value causes some WMs, notably + // Compiz and Metacity, to honor the position of unmapped windows + if (!ork_winvisible(window)) + { + long supplied; + XSizeHints* hints = XAllocSizeHints(); + + if (XGetWMNormalHints(wcf.display, window->handle, hints, &supplied)) + { + hints->flags |= PPosition; + hints->x = hints->y = 0; + + XSetWMNormalHints(wcf.display, window->handle, hints); + } + + XFree(hints); + } + + XMoveWindow(wcf.display, window->handle, xpos, ypos); + XFlush(wcf.display); +} + +void ork_update_monitor(monitor_t* monitor, byte added, byte first) +{ + if (added) + { + wcf.n_monitors++; + wcf.monitors = + mem_realloc(wcf.monitors, sizeof(monitor_t*) * wcf.n_monitors, MEM_SYSTEM); + + if (first) + { + memmove(wcf.monitors + 1, + wcf.monitors, + ((size_t) wcf.n_monitors - 1) * sizeof(monitor_t*)); + wcf.monitors[0] = monitor; + } + else + wcf.monitors[wcf.n_monitors - 1] = monitor; + } + else + { + int i; + window_t* window; + + for (window = wcf.window_list; window; window = window->next) + { + if (window->monitor == monitor) + { + int width, height; + wcf_getsize(window, &width, &height); + wcf_windowed(window, 0, 0, width, height); + wcf_setpos(window, 0, 0); + } + } + + for (i = 0; i < wcf.n_monitors; i++) + { + if (wcf.monitors[i] == monitor) + { + wcf.n_monitors--; + memmove(wcf.monitors + i, + wcf.monitors + i + 1, + ((size_t) wcf.n_monitors - i) * sizeof(monitor_t*)); + break; + } + } + + if(monitor) { + mem_free(monitor->modes); + mem_free(monitor); + } + } +} + +const vidmode_t* ork_select_mode(monitor_t* monitor, + int width, int height, int refresh) +{ + int i; + uint sizeDiff, leastSizeDiff = UINT_MAX; + uint rateDiff, leastRateDiff = UINT_MAX; + const vidmode_t* current; + const vidmode_t* closest = NULL; + + if (!ork_getmodes(monitor)) + return NULL; + + for (i = 0; i < monitor->n_modes; i++) + { + current = monitor->modes + i; + + sizeDiff = abs((current->width - width) * + (current->width - width) + + (current->height - height) * + (current->height - height)); + + if (refresh != -1) + rateDiff = abs(current->refresh - refresh); + else + rateDiff = UINT_MAX - current->refresh; + + if ((sizeDiff < leastSizeDiff) || + (sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) + { + closest = current; + leastSizeDiff = sizeDiff; + leastRateDiff = rateDiff; + } + } + + return closest; +} + +monitor_t *ork_getmonitor() { + if(!wcf.n_monitors) + return NULL; + return wcf.monitors[0]; +} + +void ork_monitorpos(monitor_t* monitor, int* xpos, int* ypos) +{ + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(wcf.display, wcf.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, monitor->crtc); + + if (ci) + { + if (xpos) + *xpos = ci->x; + if (ypos) + *ypos = ci->y; + + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + } +} + +void wcf_getscrpos(int* xpos, int* ypos) +{ + monitor_t* monitor = ork_getmonitor(); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + ork_monitorpos(monitor, xpos, ypos); +} + +const vidmode_t* wcf_getmodes(int* count) +{ + monitor_t* monitor = ork_getmonitor(); + *count = 0; + if (!ork_getmodes(monitor)) + return NULL; + *count = monitor->n_modes; + return monitor->modes; +} + +const vidmode_t* wcf_getmode() +{ + monitor_t* monitor = ork_getmonitor(); + ork_getmode(monitor, &monitor->current_mode); + return &monitor->current_mode; +} + +void ork_resetkeys(window_t* window) +{ + int key, button; + + for (key = 0; key <= KEYSYM_LAST; key++) + { + if (window->keys[key] == KEY_PRESS) + { + ork_key(window, key, KEY_RELEASE); + } + } + + for (button = 0; button < MOUSE_BTNS; button++) + { + if (window->buttons[button] == KEY_PRESS) + ork_button(window, button, KEY_RELEASE); + } +} + +void wcf_title(window_t* window, const char* title) +{ + Xutf8SetWMProperties(wcf.display, + window->handle, + title, title, + NULL, 0, + NULL, NULL, NULL); + + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_NAME, wcf.UTF8_STRING, 8, + PropModeReplace, + (byte*) title, strlen(title)); + + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_ICON_NAME, wcf.UTF8_STRING, 8, + PropModeReplace, + (byte*) title, strlen(title)); + + XFlush(wcf.display); +} + +void wcf_getpos(window_t* window, int* xpos, int* ypos) +{ + Window dummy; + int x, y; + + XTranslateCoordinates(wcf.display, window->handle, wcf.root, + 0, 0, &x, &y, &dummy); + + if (xpos) + *xpos = x; + if (ypos) + *ypos = y; +} + +byte ork_create_xwin(window_t* window, const char* title, Visual* visual, int depth) +{ + // Create a colormap based on the visual used by the current context + window->colormap = XCreateColormap(wcf.display, + wcf.root, + visual, + AllocNone); + + // window->transparent = ork_istransparent(visual); + + XSetWindowAttributes wa = { 0 }; + wa.colormap = window->colormap; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + ork_set_errhandler(); + + window->parent = wcf.root; + window->handle = XCreateWindow(wcf.display, + wcf.root, + 0, 0, // Position + 16, 16, + 0, // Border width + depth, // Color depth + InputOutput, + visual, + CWBorderPixel | CWColormap | CWEventMask, + &wa); + + ork_unset_errhandler(); + + if (!window->handle) + { + char buffer[ERR_LOG_SIZE]; + XGetErrorText(wcf.display, wcf.error_code, buffer, ERR_LOG_SIZE); + loge(LOG_X11, "Failed to create window: %s", buffer); + return 0; + } + + XSaveContext(wcf.display, + window->handle, + wcf.context, + (XPointer) window); + + ork_disabledecor(window); + + if (wcf.NET_WM_STATE && !window->monitor) + { + Atom states[3]; + int count = 0; + + if (window->floating) + { + if (wcf.NET_WM_STATE_ABOVE) + states[count++] = wcf.NET_WM_STATE_ABOVE; + } + + if (count) + { + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (byte*) states, count); + } + } + + // Declare the WM protocols supported + { + Atom protocols[] = + { + wcf.WM_DELETE_WINDOW, + wcf.NET_WM_PING + }; + + XSetWMProtocols(wcf.display, window->handle, + protocols, sizeof(protocols) / sizeof(Atom)); + } + + // Declare our PID + { + const long pid = getpid(); + + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_PID, XA_CARDINAL, 32, + PropModeReplace, + (byte*) &pid, 1); + } + + if (wcf.NET_WM_WINDOW_TYPE && wcf.NET_WM_WINDOW_TYPE_NORMAL) + { + Atom type = wcf.NET_WM_WINDOW_TYPE_NORMAL; + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, (byte*) &type, 1); + } + + // Set ICCCM WM_HINTS property + { + XWMHints* hints = XAllocWMHints(); + // if (!hints) OOM? + + hints->flags = StateHint; + hints->initial_state = NormalState; + + XSetWMHints(wcf.display, window->handle, hints); + XFree(hints); + } + + ork_updatehints(window, 16, 16); + + // Set ICCCM WM_CLASS property + { + XClassHint* hint = XAllocClassHint(); + + if (strlen(title)) { + hint->res_name = (char*)title; + hint->res_class = (char*)title; + } + else { + hint->res_name = (char*) "swcf-application"; + hint->res_class = (char*) "SWCF-Application"; + } + + XSetClassHint(wcf.display, window->handle, hint); + XFree(hint); + } + + // Announce support for Xdnd (drag and drop) + { + const Atom version = WCF_XDND_VERSION; + XChangeProperty(wcf.display, window->handle, + wcf.XdndAware, XA_ATOM, 32, + PropModeReplace, (byte*) &version, 1); + } + + wcf_title(window, title); + + window->ic = XCreateIC(wcf.im, + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + window->handle, + XNFocusWindow, + window->handle, + NULL); + + if (window->ic) + { + ulong filter = 0; + if (XGetICValues(window->ic, XNFilterEvents, &filter, NULL) == NULL) + XSelectInput(wcf.display, window->handle, wa.event_mask | filter); + } + + wcf_getpos(window, &window->xpos, &window->ypos); + wcf_getsize(window, &window->width, &window->height); + + return 1; +} + +/*! + * No further callbacks will be called for that window. + * + * If the context of the specified window is current on the main thread, it is + * detached before being destroyed. + * + * The context of the specified window must not be current on any other + * thread when this function is called. + */ +void wcf_destroy(window_t* window) +{ + // Clear all callbacks to avoid exposing a half torn-down window object + // TODO: add deinit + + // The window's context must not be current on another thread when the window is destroyed + if (window == wcf.current) + wcf_selcontext(NULL); + + if (wcf.grab_cur_window == window) + wcf.grab_cur_window = NULL; + + if (window->monitor) + ork_unset_monitor(window); + + if (window->ic) + { + XDestroyIC(window->ic); + window->ic = NULL; + } + + + if (window->window) + { + glXDestroyWindow(wcf.display, window->window); + window->window = None; + } + + if (wcf.glx && (wcf.window_list == window) && !window->next) + { + glXDestroyContext(wcf.display, wcf.glx); + wcf.glx = NULL; + } + + if (window->handle) + { + XDeleteContext(wcf.display, window->handle, wcf.context); + XUnmapWindow(wcf.display, window->handle); + XDestroyWindow(wcf.display, window->handle); + window->handle = (Window) 0; + } + + if (window->colormap) + { + XFreeColormap(wcf.display, window->colormap); + window->colormap = (Colormap) 0; + } + + XFlush(wcf.display); + + // Unlink window from global linked list + { + window_t** prev = &wcf.window_list; + + while (*prev != window) + prev = &((*prev)->next); + + *prev = window->next; + } + + mem_free(window); +} + +/*! + * Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. + */ +window_t* wcf_create(const char* title, byte resizable) { + window_t* window = mem_zeros(sizeof(window_t), MEM_SYSTEM); + window->next = wcf.window_list; + wcf.window_list = window; + + window->video_width = 0; + window->video_height = 0; + window->video_refresh = -1; + + window->monitor = NULL; + window->resizable = resizable; // (flags & WIN_FLAG_RESIZEABLE) != 0; + window->floating = 0; // (flags & WIN_FLAG_FLOATING) != 0; + window->cursor_set = 1; + + window->minwidth = -1; + window->minheight = -1; + window->maxwidth = -1; + window->maxheight = -1; + + if (!ork_create_xwin(window, title, wcf.visual, wcf.depth)) { + wcf_destroy(window); + return NULL; + } + + if (!window->ic) { + loge(LOG_X11, "Kein XIC für Fenster vorhanden"); + wcf_destroy(window); + return NULL; + } + + window->window = glXCreateWindow(wcf.display, wcf.native, window->handle, NULL); + if (!window->window) { + loge(LOG_GLX, "Konnte kein GLX-Fenster erstellen"); + wcf_destroy(window); + return 0; + } + + wcf_selcontext(window); + + if(!window->next && !gl_init((void * (*)(const char *))ork_glxproc, wcf.debug)) { + wcf_destroy(window); + return 0; + } + + glClear(GL_COLOR_BUFFER_BIT); + wcf_swap(window); + + XFlush(wcf.display); + return window; +} + +Atom ork_writeprop(const XSelectionRequestEvent* request) +{ + int i; + char* selectionString = NULL; + const Atom formats[] = { wcf.UTF8_STRING, XA_STRING }; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->selection == wcf.PRIMARY) + selectionString = wcf.primary_str; + else + selectionString = wcf.clipboard_str; + + if (request->property == None) + { + // The requester is a legacy client (ICCCM section 2.2) + // We don't support legacy clients, so fail here + return None; + } + + if (request->target == wcf.TARGETS) + { + // The list of supported targets was requested + + const Atom targets[] = { wcf.TARGETS, + wcf.MULTIPLE, + wcf.UTF8_STRING, + XA_STRING }; + + XChangeProperty(wcf.display, + request->requestor, + request->property, + XA_ATOM, + 32, + PropModeReplace, + (byte*) targets, + sizeof(targets) / sizeof(targets[0])); + + return request->property; + } + + if (request->target == wcf.MULTIPLE) + { + // Multiple conversions were requested + + Atom* targets; + ulong i, count; + + count = ork_getwinprop(request->requestor, + request->property, + wcf.ATOM_PAIR, + (byte**) &targets); + + for (i = 0; i < count; i += 2) + { + int j; + + for (j = 0; j < formatCount; j++) + { + if (targets[i] == formats[j]) + break; + } + + if (j < formatCount) + { + XChangeProperty(wcf.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (byte *) selectionString, + strlen(selectionString)); + } + else + targets[i + 1] = None; + } + + XChangeProperty(wcf.display, + request->requestor, + request->property, + wcf.ATOM_PAIR, + 32, + PropModeReplace, + (byte*) targets, + count); + + XFree(targets); + + return request->property; + } + + if (request->target == wcf.SAVE_TARGETS) + { + // The request is a check whether we support SAVE_TARGETS + // It should be handled as a no-op side effect target + + XChangeProperty(wcf.display, + request->requestor, + request->property, + wcf.NULL_, + 32, + PropModeReplace, + NULL, + 0); + + return request->property; + } + + // Conversion to a data target was requested + + for (i = 0; i < formatCount; i++) + { + if (request->target == formats[i]) + { + // The requested target is one we support + + XChangeProperty(wcf.display, + request->requestor, + request->property, + request->target, + 8, + PropModeReplace, + (byte *) selectionString, + strlen(selectionString)); + + return request->property; + } + } + + // The requested target is not supported + + return None; +} + +void ork_handle_select(XEvent* event) +{ + const XSelectionRequestEvent* request = &event->xselectionrequest; + + XEvent reply = { SelectionNotify }; + reply.xselection.property = ork_writeprop(request); + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; + + XSendEvent(wcf.display, request->requestor, False, 0, &reply); +} + +void ork_push_selection() +{ + XConvertSelection(wcf.display, + wcf.CLIPBOARD_MANAGER, + wcf.SAVE_TARGETS, + None, + wcf.helper_window, + CurrentTime); + + for (;;) + { + XEvent event; + + while (XCheckIfEvent(wcf.display, &event, ork_is_selevent, NULL)) + { + switch (event.type) + { + case SelectionRequest: + ork_handle_select(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == wcf.SAVE_TARGETS) + { + // This means one of two things; either the selection + // was not owned, which means there is no clipboard + // manager, or the transfer to the clipboard manager has + // completed + // In either case, it means we are done here + return; + } + + break; + } + } + } + + ork_wait_xevent(NULL); + } +} + +void wcf_end() { + int i; + + // TODO: deinit callback? + + while (wcf.window_list) + wcf_destroy(wcf.window_list); + + while (wcf.cursor_list) + wcf_destroy_curimg(wcf.cursor_list); + + for (i = 0; i < wcf.n_monitors; i++) + { + monitor_t* monitor = wcf.monitors[i]; + + if(monitor) { + mem_free(monitor->modes); + mem_free(monitor); + } + } + + mem_free(wcf.monitors); + wcf.monitors = NULL; + wcf.n_monitors = 0; + + if (wcf.helper_window) + { + if (XGetSelectionOwner(wcf.display, wcf.CLIPBOARD) == + wcf.helper_window) + { + ork_push_selection(); + } + + XDestroyWindow(wcf.display, wcf.helper_window); + wcf.helper_window = None; + } + + if (wcf.empty_cursor) + { + XFreeCursor(wcf.display, wcf.empty_cursor); + wcf.empty_cursor = (Cursor) 0; + } + + mem_free(wcf.primary_str); + mem_free(wcf.clipboard_str); + + if (wcf.im) + { + XCloseIM(wcf.im); + wcf.im = NULL; + } + + if (wcf.display) + { + XCloseDisplay(wcf.display); + wcf.display = NULL; + } + + if (wcf.x11xcb.handle) + { + dlclose(wcf.x11xcb.handle); + wcf.x11xcb.handle = NULL; + } + + if (wcf.xcursor.handle) + { + dlclose(wcf.xcursor.handle); + wcf.xcursor.handle = NULL; + } + + if (wcf.randr.handle) + { + dlclose(wcf.randr.handle); + wcf.randr.handle = NULL; + } + + if (wcf.xinerama.handle) + { + dlclose(wcf.xinerama.handle); + wcf.xinerama.handle = NULL; + } + + if (wcf.xrender.handle) + { + dlclose(wcf.xrender.handle); + wcf.xrender.handle = NULL; + } + + if (wcf.vidmode.handle) + { + dlclose(wcf.vidmode.handle); + wcf.vidmode.handle = NULL; + } + + if (wcf.xi.handle) + { + dlclose(wcf.xi.handle); + wcf.xi.handle = NULL; + } + + // NOTE: These need to be unloaded after XCloseDisplay, as they register + // cleanup callbacks that get called by that function + if (wcf.handle) + { + dlclose(wcf.handle); + wcf.handle = NULL; + } + + memset(&wcf, 0, sizeof(wcf_t)); +} + +byte ork_glx_init() { + wcf.handle = DL_LOPEN("libGL.so.1"); + if(!wcf.handle) + wcf.handle = DL_LOPEN("libGL.so"); + if(!wcf.handle) { + loge(LOG_GLX, "Failed to load GLX"); + return 0; + } + + wcf.GetFBConfigs = + dlsym(wcf.handle, "glXGetFBConfigs"); + wcf.GetFBConfigAttrib = + dlsym(wcf.handle, "glXGetFBConfigAttrib"); + wcf.GetClientString = + dlsym(wcf.handle, "glXGetClientString"); + wcf.QueryExtension = + dlsym(wcf.handle, "glXQueryExtension"); + wcf.QueryVersion = + dlsym(wcf.handle, "glXQueryVersion"); + wcf.DestroyContext = + dlsym(wcf.handle, "glXDestroyContext"); + wcf.MakeCurrent = + dlsym(wcf.handle, "glXMakeCurrent"); + wcf.SwapBuffers = + dlsym(wcf.handle, "glXSwapBuffers"); + wcf.QueryExtensionsString = + dlsym(wcf.handle, "glXQueryExtensionsString"); + wcf.CreateNewContext = + dlsym(wcf.handle, "glXCreateNewContext"); + wcf.CreateWindow = + dlsym(wcf.handle, "glXCreateWindow"); + wcf.DestroyWindow = + dlsym(wcf.handle, "glXDestroyWindow"); + wcf.GetVisualFromFBConfig = + dlsym(wcf.handle, "glXGetVisualFromFBConfig"); + + if (!wcf.GetFBConfigs || + !wcf.GetFBConfigAttrib || + !wcf.GetClientString || + !wcf.QueryExtension || + !wcf.QueryVersion || + !wcf.DestroyContext || + !wcf.MakeCurrent || + !wcf.SwapBuffers || + !wcf.QueryExtensionsString || + !wcf.CreateNewContext || + !wcf.CreateWindow || + !wcf.DestroyWindow || + !wcf.GetVisualFromFBConfig) + { + loge(LOG_GLX, "Failed to load required entry points"); + return 0; + } + + // NOTE: Unlike GLX 1.3 entry points these are not required to be present + wcf.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) + dlsym(wcf.handle, "glXGetProcAddress"); + wcf.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) + dlsym(wcf.handle, "glXGetProcAddressARB"); + + if (!glXQueryExtension(wcf.display, + &wcf.error_base, + &wcf.event_base)) + { + loge(LOG_GLX, "GLX extension not found"); + return 0; + } + + if (!glXQueryVersion(wcf.display, &wcf.major, &wcf.minor)) + { + loge(LOG_GLX, "Failed to query GLX version"); + return 0; + } + + if (wcf.major == 1 && wcf.minor < 3) + { + loge(LOG_GLX, "GLX version 1.3 is required"); + return 0; + } + + if (ork_ext_available("GLX_EXT_swap_control")) + { + wcf.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) + ork_glxproc("glXSwapIntervalEXT"); + + if (wcf.SwapIntervalEXT) + wcf.EXT_swap_control = 1; + } + + if (ork_ext_available("GLX_SGI_swap_control")) + { + wcf.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) + ork_glxproc("glXSwapIntervalSGI"); + + if (wcf.SwapIntervalSGI) + wcf.SGI_swap_control = 1; + } + + if (ork_ext_available("GLX_MESA_swap_control")) + { + wcf.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) + ork_glxproc("glXSwapIntervalMESA"); + + if (wcf.SwapIntervalMESA) + wcf.MESA_swap_control = 1; + } + + if (ork_ext_available("GLX_ARB_multisample")) + wcf.ARB_multisample = 1; + + if (ork_ext_available("GLX_ARB_create_context")) + wcf.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + ork_glxproc("glXCreateContextAttribsARB"); + if(!wcf.CreateContextAttribsARB) { + loge(LOG_GLX, "GLX_ARB_create_context ist nicht verfügbar"); + return 0; + } + + // if (ork_ext_available("GLX_ARB_create_context_no_error")) + // wcf.ARB_create_context_no_error = 1; + + return 1; +} + +void ork_refresh_monitors() +{ + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + int disconnectedCount, screenCount = 0; + monitor_t** disconnected = NULL; + XineramaScreenInfo* screens = NULL; + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(wcf.display, + wcf.root); + RROutput primary = XRRGetOutputPrimary(wcf.display, + wcf.root); + + if (wcf.xinerama.available) + screens = XineramaQueryScreens(wcf.display, &screenCount); + + disconnectedCount = wcf.n_monitors; + if (disconnectedCount) + { + disconnected = mem_zeros(wcf.n_monitors * sizeof(monitor_t*), MEM_MISC); + memcpy(disconnected, + wcf.monitors, + wcf.n_monitors * sizeof(monitor_t*)); + } + + for (int i = 0; i < sr->noutput; i++) + { + int j; + + XRROutputInfo* oi = XRRGetOutputInfo(wcf.display, sr, sr->outputs[i]); + if (oi->connection != RR_Connected || oi->crtc == None) + { + XRRFreeOutputInfo(oi); + continue; + } + + for (j = 0; j < disconnectedCount; j++) + { + if (disconnected[j] && + disconnected[j]->output == sr->outputs[i]) + { + disconnected[j] = NULL; + break; + } + } + + if (j < disconnectedCount) + { + XRRFreeOutputInfo(oi); + continue; + } + + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, oi->crtc); + + monitor_t* monitor = mem_zeros(sizeof(monitor_t), MEM_SYSTEM); + monitor->output = sr->outputs[i]; + monitor->crtc = oi->crtc; + + for (j = 0; j < screenCount; j++) + { + if (screens[j].x_org == ci->x && + screens[j].y_org == ci->y && + screens[j].width == ci->width && + screens[j].height == ci->height) + { + monitor->index = j; + break; + } + } + + ork_update_monitor(monitor, 1, monitor->output == primary); + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + + if (screens) + XFree(screens); + + for (int i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + ork_update_monitor(disconnected[i], 0, 0); + } + + mem_free(disconnected); + } + else + { + ork_update_monitor(mem_zeros(sizeof(monitor_t), MEM_SYSTEM), + 1, + 1); + } +} + +byte ork_xinput_valid() +{ + byte found = 0; + XIMStyles* styles = NULL; + + if (XGetIMValues(wcf.im, XNQueryInputStyle, &styles, NULL) != NULL) + return 0; + + for (uint i = 0; i < styles->count_styles; i++) + { + if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) + { + found = 1; + break; + } + } + + XFree(styles); + return found; +} + +void ork_create_tables() +{ + int scancode, scancodeMin, scancodeMax; + + memset(wcf.keycodes, (char)-1, sizeof(wcf.keycodes)); + + // Use XKB to determine physical key locations independently of the + // current keyboard layout + + XkbDescPtr desc = XkbGetMap(wcf.display, 0, XkbUseCoreKbd); + XkbGetNames(wcf.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); + + scancodeMin = desc->min_key_code; + scancodeMax = desc->max_key_code; + + const struct + { + char key; + char* name; + } keymap[] = + { + { KEYSYM_CIRCUMFLEX, "TLDE" }, + { KEYSYM_1, "AE01" }, + { KEYSYM_2, "AE02" }, + { KEYSYM_3, "AE03" }, + { KEYSYM_4, "AE04" }, + { KEYSYM_5, "AE05" }, + { KEYSYM_6, "AE06" }, + { KEYSYM_7, "AE07" }, + { KEYSYM_8, "AE08" }, + { KEYSYM_9, "AE09" }, + { KEYSYM_0, "AE10" }, + { KEYSYM_SHARP_S, "AE11" }, + { KEYSYM_ACUTE, "AE12" }, + { KEYSYM_Q, "AD01" }, + { KEYSYM_W, "AD02" }, + { KEYSYM_E, "AD03" }, + { KEYSYM_R, "AD04" }, + { KEYSYM_T, "AD05" }, + { KEYSYM_Z, "AD06" }, + { KEYSYM_U, "AD07" }, + { KEYSYM_I, "AD08" }, + { KEYSYM_O, "AD09" }, + { KEYSYM_P, "AD10" }, + { KEYSYM_UE, "AD11" }, + { KEYSYM_PLUS, "AD12" }, + { KEYSYM_A, "AC01" }, + { KEYSYM_S, "AC02" }, + { KEYSYM_D, "AC03" }, + { KEYSYM_F, "AC04" }, + { KEYSYM_G, "AC05" }, + { KEYSYM_H, "AC06" }, + { KEYSYM_J, "AC07" }, + { KEYSYM_K, "AC08" }, + { KEYSYM_L, "AC09" }, + { KEYSYM_OE, "AC10" }, + { KEYSYM_AE, "AC11" }, + { KEYSYM_Y, "AB01" }, + { KEYSYM_X, "AB02" }, + { KEYSYM_C, "AB03" }, + { KEYSYM_V, "AB04" }, + { KEYSYM_B, "AB05" }, + { KEYSYM_N, "AB06" }, + { KEYSYM_M, "AB07" }, + { KEYSYM_COMMA, "AB08" }, + { KEYSYM_PERIOD, "AB09" }, + { KEYSYM_HYPHEN, "AB10" }, + { KEYSYM_NUMBER_SIGN, "BKSL" }, + { KEYSYM_LESS_THAN, "LSGT" }, + { KEYSYM_SPACE, "SPCE" }, + { KEYSYM_ESCAPE, "ESC" }, + { KEYSYM_RETURN, "RTRN" }, + { KEYSYM_TAB, "TAB" }, + { KEYSYM_BACKSPACE, "BKSP" }, + { KEYSYM_INSERT, "INS" }, + { KEYSYM_DELETE, "DELE" }, + { KEYSYM_RIGHT, "RGHT" }, + { KEYSYM_LEFT, "LEFT" }, + { KEYSYM_DOWN, "DOWN" }, + { KEYSYM_UP, "UP" }, + { KEYSYM_PAGE_UP, "PGUP" }, + { KEYSYM_PAGE_DOWN, "PGDN" }, + { KEYSYM_HOME, "HOME" }, + { KEYSYM_END, "END" }, + { KEYSYM_CAPS_LOCK, "CAPS" }, + { KEYSYM_SCROLL_LOCK, "SCLK" }, + { KEYSYM_NUM_LOCK, "NMLK" }, + { KEYSYM_PRINT_SCREEN, "PRSC" }, + { KEYSYM_PAUSE, "PAUS" }, + { KEYSYM_F1, "FK01" }, + { KEYSYM_F2, "FK02" }, + { KEYSYM_F3, "FK03" }, + { KEYSYM_F4, "FK04" }, + { KEYSYM_F5, "FK05" }, + { KEYSYM_F6, "FK06" }, + { KEYSYM_F7, "FK07" }, + { KEYSYM_F8, "FK08" }, + { KEYSYM_F9, "FK09" }, + { KEYSYM_F10, "FK10" }, + { KEYSYM_F11, "FK11" }, + { KEYSYM_F12, "FK12" }, + { KEYSYM_KP_0, "KP0" }, + { KEYSYM_KP_1, "KP1" }, + { KEYSYM_KP_2, "KP2" }, + { KEYSYM_KP_3, "KP3" }, + { KEYSYM_KP_4, "KP4" }, + { KEYSYM_KP_5, "KP5" }, + { KEYSYM_KP_6, "KP6" }, + { KEYSYM_KP_7, "KP7" }, + { KEYSYM_KP_8, "KP8" }, + { KEYSYM_KP_9, "KP9" }, + { KEYSYM_KP_DECIMAL, "KPDL" }, + { KEYSYM_KP_DIVIDE, "KPDV" }, + { KEYSYM_KP_MULTIPLY, "KPMU" }, + { KEYSYM_KP_SUBTRACT, "KPSU" }, + { KEYSYM_KP_ADD, "KPAD" }, + { KEYSYM_KP_ENTER, "KPEN" }, + { KEYSYM_KP_EQUAL, "KPEQ" }, + { KEYSYM_LEFT_SHIFT, "LFSH" }, + { KEYSYM_LEFT_CONTROL, "LCTL" }, + { KEYSYM_ALT, "LALT" }, + { KEYSYM_LEFT_META, "LWIN" }, + { KEYSYM_RIGHT_SHIFT, "RTSH" }, + { KEYSYM_RIGHT_CONTROL, "RCTL" }, + { KEYSYM_ALT_GRAPH, "RALT" }, + { KEYSYM_ALT_GRAPH, "LVL3" }, + { KEYSYM_ALT_GRAPH, "MDSW" }, + { KEYSYM_RIGHT_META, "RWIN" }, + { KEYSYM_MENU, "MENU" } + }; + + // Find the X11 key code -> key code mapping + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) + { + char key = -1; + + // Map the key name to a key code. Note: We use the US + // keyboard layout. Because function keys aren't mapped correctly + // when using traditional KeySym translations, they are mapped + // here instead. + for (int i = 0; i < sizeof(keymap) / sizeof(keymap[0]); i++) + { + if (strncmp(desc->names->keys[scancode].name, + keymap[i].name, + XkbKeyNameLength) == 0) + { + key = keymap[i].key; + break; + } + } + + // Fall back to key aliases in case the key name did not match + for (int i = 0; i < desc->names->num_key_aliases; i++) + { + if (key != -1) + break; + + if (strncmp(desc->names->key_aliases[i].real, + desc->names->keys[scancode].name, + XkbKeyNameLength) != 0) + { + continue; + } + + for (int j = 0; j < sizeof(keymap) / sizeof(keymap[0]); j++) + { + if (strncmp(desc->names->key_aliases[i].alias, + keymap[j].name, + XkbKeyNameLength) == 0) + { + key = keymap[j].key; + break; + } + } + } + + wcf.keycodes[scancode] = key; + } + + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); + + int width; + KeySym* keysyms = XGetKeyboardMapping(wcf.display, + scancodeMin, + scancodeMax - scancodeMin + 1, + &width); + + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) + { + if (wcf.keycodes[scancode] < 0) + { + const size_t base = (scancode - scancodeMin) * width; + wcf.keycodes[scancode] = (width > 1 && keysyms[base+1] == XK_KP_Decimal) ? KEYSYM_KP_DECIMAL : ((keysyms[base] == XK_Print) ? KEYSYM_PRINT_SCREEN : -1); + } + } + + XFree(keysyms); +} + +Atom ork_getatom(Atom* supportedAtoms, + ulong atomCount, + const char* atomName) +{ + const Atom atom = XInternAtom(wcf.display, atomName, False); + + for (ulong i = 0; i < atomCount; i++) + { + if (supportedAtoms[i] == atom) + return atom; + } + + return None; +} + +void ork_check_ewmh() +{ + // First we read the _NET_SUPPORTING_WM_CHECK property on the root window + + Window* windowFromRoot = NULL; + if (!ork_getwinprop(wcf.root, + wcf.NET_SUPPORTING_WM_CHECK, + XA_WINDOW, + (byte**) &windowFromRoot)) + { + return; + } + + ork_set_errhandler(); + + // If it exists, it should be the XID of a top-level window + // Then we look for the same property on that window + + Window* windowFromChild = NULL; + if (!ork_getwinprop(*windowFromRoot, + wcf.NET_SUPPORTING_WM_CHECK, + XA_WINDOW, + (byte**) &windowFromChild)) + { + XFree(windowFromRoot); + return; + } + + ork_unset_errhandler(); + + // If the property exists, it should contain the XID of the window + + if (*windowFromRoot != *windowFromChild) + { + XFree(windowFromRoot); + XFree(windowFromChild); + return; + } + + XFree(windowFromRoot); + XFree(windowFromChild); + + // We are now fairly sure that an EWMH-compliant WM is currently running + // We can now start querying the WM about what features it supports by + // looking in the _NET_SUPPORTED property on the root window + // It should contain a list of supported EWMH protocol and state atoms + + Atom* supportedAtoms = NULL; + const ulong atomCount = + ork_getwinprop(wcf.root, + wcf.NET_SUPPORTED, + XA_ATOM, + (byte**) &supportedAtoms); + + // See which of the atoms we support that are supported by the WM + + wcf.NET_WM_STATE = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_STATE"); + wcf.NET_WM_STATE_ABOVE = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); + wcf.NET_WM_STATE_FULLSCREEN = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + wcf.NET_WM_FULLSCREEN_MONITORS = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); + wcf.NET_WM_WINDOW_TYPE = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); + wcf.NET_WM_WINDOW_TYPE_NORMAL = + ork_getatom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); + wcf.NET_CURRENT_DESKTOP = + ork_getatom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); + wcf.NET_ACTIVE_WINDOW = + ork_getatom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); + wcf.NET_FRAME_EXTENTS = + ork_getatom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); + wcf.NET_REQUEST_FRAME_EXTENTS = + ork_getatom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); + + if (supportedAtoms) + XFree(supportedAtoms); +} + +byte ork_init_extensions() { + wcf.vidmode.handle = DL_LOPEN("libXxf86vm.so.1"); + if (wcf.vidmode.handle) + { + wcf.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) + dlsym(wcf.vidmode.handle, "XF86VidModeQueryExtension"); + + wcf.vidmode.available = + XF86VidModeQueryExtension(wcf.display, + &wcf.vidmode.event_base, + &wcf.vidmode.error_base); + } + + wcf.xi.handle = DL_LOPEN("libXi.so.6"); + if (wcf.xi.handle) + { + wcf.xi.QueryVersion = (PFN_XIQueryVersion) + dlsym(wcf.xi.handle, "XIQueryVersion"); + wcf.xi.SelectEvents = (PFN_XISelectEvents) + dlsym(wcf.xi.handle, "XISelectEvents"); + + if (XQueryExtension(wcf.display, + "XInputExtension", + &wcf.xi.major_opcode, + &wcf.xi.event_base, + &wcf.xi.error_base)) + { + wcf.xi.major = 2; + wcf.xi.minor = 0; + + if (XIQueryVersion(wcf.display, + &wcf.xi.major, + &wcf.xi.minor) == Success) + { + wcf.xi.available = 1; + } + } + } + + wcf.randr.handle = DL_LOPEN("libXrandr.so.2"); + if (wcf.randr.handle) + { + wcf.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) + dlsym(wcf.randr.handle, "XRRFreeCrtcInfo"); + wcf.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) + dlsym(wcf.randr.handle, "XRRFreeOutputInfo"); + wcf.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) + dlsym(wcf.randr.handle, "XRRFreeScreenResources"); + wcf.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) + dlsym(wcf.randr.handle, "XRRGetCrtcInfo"); + wcf.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) + dlsym(wcf.randr.handle, "XRRGetOutputInfo"); + wcf.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) + dlsym(wcf.randr.handle, "XRRGetOutputPrimary"); + wcf.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) + dlsym(wcf.randr.handle, "XRRGetScreenResourcesCurrent"); + wcf.randr.QueryExtension = (PFN_XRRQueryExtension) + dlsym(wcf.randr.handle, "XRRQueryExtension"); + wcf.randr.QueryVersion = (PFN_XRRQueryVersion) + dlsym(wcf.randr.handle, "XRRQueryVersion"); + wcf.randr.SelectInput = (PFN_XRRSelectInput) + dlsym(wcf.randr.handle, "XRRSelectInput"); + wcf.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) + dlsym(wcf.randr.handle, "XRRSetCrtcConfig"); + wcf.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) + dlsym(wcf.randr.handle, "XRRUpdateConfiguration"); + + if (XRRQueryExtension(wcf.display, + &wcf.randr.event_base, + &wcf.randr.error_base)) + { + if (XRRQueryVersion(wcf.display, + &wcf.randr.major, + &wcf.randr.minor)) + { + // The RandR path requires at least version 1.3 + if (wcf.randr.major > 1 || wcf.randr.minor >= 3) + wcf.randr.available = 1; + } + else + { + loge(LOG_X11, "Failed to query RandR version"); + } + } + } + + if (wcf.randr.available) + { + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(wcf.display, + wcf.root); + + if (!sr->ncrtc) + { + // A system without CRTCs is likely a system with broken RandR + // Disable the RandR monitor path and fall back to core functions + wcf.randr.monitor_broken = 1; + } + + XRRFreeScreenResources(sr); + } + + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + XRRSelectInput(wcf.display, wcf.root, + RROutputChangeNotifyMask); + } + + wcf.xcursor.handle = DL_LOPEN("libXcursor.so.1"); + if (wcf.xcursor.handle) + { + wcf.xcursor.ImageCreate = (PFN_XcursorImageCreate) + dlsym(wcf.xcursor.handle, "XcursorImageCreate"); + wcf.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) + dlsym(wcf.xcursor.handle, "XcursorImageDestroy"); + wcf.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) + dlsym(wcf.xcursor.handle, "XcursorImageLoadCursor"); + } + + wcf.xinerama.handle = DL_LOPEN("libXinerama.so.1"); + if (wcf.xinerama.handle) + { + wcf.xinerama.IsActive = (PFN_XineramaIsActive) + dlsym(wcf.xinerama.handle, "XineramaIsActive"); + wcf.xinerama.QueryExtension = (PFN_XineramaQueryExtension) + dlsym(wcf.xinerama.handle, "XineramaQueryExtension"); + wcf.xinerama.QueryScreens = (PFN_XineramaQueryScreens) + dlsym(wcf.xinerama.handle, "XineramaQueryScreens"); + + if (XineramaQueryExtension(wcf.display, + &wcf.xinerama.major, + &wcf.xinerama.minor)) + { + if (XineramaIsActive(wcf.display)) + wcf.xinerama.available = 1; + } + } + + wcf.xkb.major = 1; + wcf.xkb.minor = 0; + wcf.xkb.available = + XkbQueryExtension(wcf.display, + &wcf.xkb.major_opcode, + &wcf.xkb.event_base, + &wcf.xkb.error_base, + &wcf.xkb.major, + &wcf.xkb.minor); + + if (wcf.xkb.available) + { + Bool supported; + + if (XkbSetDetectableAutoRepeat(wcf.display, True, &supported)) + { + if (supported) + wcf.xkb.detectable = 1; + } + + XkbStateRec state; + if (XkbGetState(wcf.display, XkbUseCoreKbd, &state) == Success) + wcf.xkb.group = (uint)state.group; + + XkbSelectEventDetails(wcf.display, XkbUseCoreKbd, XkbStateNotify, + XkbGroupStateMask, XkbGroupStateMask); + } + + wcf.x11xcb.handle = DL_LOPEN("libX11-xcb.so.1"); + if (wcf.x11xcb.handle) + { + wcf.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) + dlsym(wcf.x11xcb.handle, "XGetXCBConnection"); + } + + wcf.xrender.handle = DL_LOPEN("libXrender.so.1"); + if (wcf.xrender.handle) + { + wcf.xrender.QueryExtension = (PFN_XRenderQueryExtension) + dlsym(wcf.xrender.handle, "XRenderQueryExtension"); + wcf.xrender.QueryVersion = (PFN_XRenderQueryVersion) + dlsym(wcf.xrender.handle, "XRenderQueryVersion"); + wcf.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + dlsym(wcf.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(wcf.display, + &wcf.xrender.error_base, + &wcf.xrender.event_base)) + { + if (XRenderQueryVersion(wcf.display, + &wcf.xrender.major, + &wcf.xrender.minor)) + { + wcf.xrender.available = 1; + } + } + } + + // Update the key code LUT + // FIXME: We should listen to XkbMapNotify events to track changes to + // the keyboard mapping. + ork_create_tables(); + + // String format atoms + wcf.NULL_ = XInternAtom(wcf.display, "NULL", False); + wcf.UTF8_STRING = XInternAtom(wcf.display, "UTF8_STRING", False); + wcf.ATOM_PAIR = XInternAtom(wcf.display, "ATOM_PAIR", False); + + // Custom selection property atom + wcf.WCF_SELECTION = + XInternAtom(wcf.display, "WCF_SELECTION", False); + + // ICCCM standard clipboard atoms + wcf.TARGETS = XInternAtom(wcf.display, "TARGETS", False); + wcf.MULTIPLE = XInternAtom(wcf.display, "MULTIPLE", False); + wcf.PRIMARY = XInternAtom(wcf.display, "PRIMARY", False); + wcf.INCR = XInternAtom(wcf.display, "INCR", False); + wcf.CLIPBOARD = XInternAtom(wcf.display, "CLIPBOARD", False); + + // Clipboard manager atoms + wcf.CLIPBOARD_MANAGER = + XInternAtom(wcf.display, "CLIPBOARD_MANAGER", False); + wcf.SAVE_TARGETS = + XInternAtom(wcf.display, "SAVE_TARGETS", False); + + // Xdnd (drag and drop) atoms + wcf.XdndAware = XInternAtom(wcf.display, "XdndAware", False); + wcf.XdndEnter = XInternAtom(wcf.display, "XdndEnter", False); + wcf.XdndPosition = XInternAtom(wcf.display, "XdndPosition", False); + wcf.XdndStatus = XInternAtom(wcf.display, "XdndStatus", False); + wcf.XdndActionCopy = XInternAtom(wcf.display, "XdndActionCopy", False); + wcf.XdndDrop = XInternAtom(wcf.display, "XdndDrop", False); + wcf.XdndFinished = XInternAtom(wcf.display, "XdndFinished", False); + wcf.XdndSelection = XInternAtom(wcf.display, "XdndSelection", False); + wcf.XdndTypeList = XInternAtom(wcf.display, "XdndTypeList", False); + wcf.text_uri_list = XInternAtom(wcf.display, "text/uri-list", False); + + // ICCCM, EWMH and Motif window property atoms + // These can be set safely even without WM support + // The EWMH atoms that require WM support are handled in ork_check_ewmh + wcf.WM_PROTOCOLS = + XInternAtom(wcf.display, "WM_PROTOCOLS", False); + wcf.WM_STATE = + XInternAtom(wcf.display, "WM_STATE", False); + wcf.WM_DELETE_WINDOW = + XInternAtom(wcf.display, "WM_DELETE_WINDOW", False); + wcf.NET_SUPPORTED = + XInternAtom(wcf.display, "_NET_SUPPORTED", False); + wcf.NET_SUPPORTING_WM_CHECK = + XInternAtom(wcf.display, "_NET_SUPPORTING_WM_CHECK", False); + wcf.NET_WM_ICON = + XInternAtom(wcf.display, "_NET_WM_ICON", False); + wcf.NET_WM_PING = + XInternAtom(wcf.display, "_NET_WM_PING", False); + wcf.NET_WM_PID = + XInternAtom(wcf.display, "_NET_WM_PID", False); + wcf.NET_WM_NAME = + XInternAtom(wcf.display, "_NET_WM_NAME", False); + wcf.NET_WM_ICON_NAME = + XInternAtom(wcf.display, "_NET_WM_ICON_NAME", False); + wcf.NET_WM_BYPASS_COMPOSITOR = + XInternAtom(wcf.display, "_NET_WM_BYPASS_COMPOSITOR", False); + wcf.NET_WM_WINDOW_OPACITY = + XInternAtom(wcf.display, "_NET_WM_WINDOW_OPACITY", False); + wcf.MOTIF_WM_HINTS = + XInternAtom(wcf.display, "_MOTIF_WM_HINTS", False); + + // The compositing manager selection name contains the screen number + { + char name[32]; + snprintf(name, sizeof(name), "_NET_WM_CM_S%u", wcf.screen); + wcf.NET_WM_CM_Sx = XInternAtom(wcf.display, name, False); + } + + // Detect whether an EWMH-conformant window manager is running + ork_check_ewmh(); + + if(!wcf.xrender.available) + loge(LOG_X11, "Die XRender-Erweiterung wurde nicht gefunden"); + if(!wcf.xkb.available) + loge(LOG_X11, "Die XKB-Erweiterung wurde nicht gefunden"); + return wcf.xrender.available && wcf.xkb.available; +} + +byte wcf_init(byte debug) { + memset(&wcf, 0, sizeof(wcf_t)); + wcf.debug = debug; + + // HACK: If the application has left the locale as "C" then both wide + // character text input and explicit UTF-8 input via XIM will break + // This sets the CTYPE part of the current locale from the environment + // in the hope that it is set to something more sane than "C" + if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) + setlocale(LC_CTYPE, ""); + + XInitThreads(); + XrmInitialize(); + + wcf.display = XOpenDisplay(NULL); + if (!wcf.display) + { + const char* display = getenv("DISPLAY"); + if (display) + { + loge(LOG_X11, "Failed to open display %s", display); + } + else + { + loge(LOG_X11, "The DISPLAY environment variable is missing"); + } + + wcf_end(); + return 0; + } + + wcf.screen = DefaultScreen(wcf.display); + wcf.root = RootWindow(wcf.display, wcf.screen); + wcf.context = XUniqueContext(); + + if (!ork_init_extensions()) { + wcf_end(); + return 0; + } + + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + wcf.helper_window = XCreateWindow(wcf.display, wcf.root, + 0, 0, 1, 1, 0, 0, + InputOnly, + DefaultVisual(wcf.display, wcf.screen), + CWEventMask, &wa); + + byte pixels[16 * 16 * 4] = { 0 }; + wcf.empty_cursor = ork_createcur(pixels, 16, 16, 0, 0); + + if (XSupportsLocale()) + { + XSetLocaleModifiers(""); + + wcf.im = XOpenIM(wcf.display, 0, NULL, NULL); + if (wcf.im) + { + if (!ork_xinput_valid()) + { + loge(LOG_X11, "Keine gültige Eingabemethode gefunden"); + XCloseIM(wcf.im); + wcf.im = NULL; + } + } + else { + loge(LOG_X11, "Konnte Eingabemethode nicht öffnen"); + } + } + else { + loge(LOG_X11, "Erweiterte Eingabe wird vom X-Server nicht unterstützt"); + } + if (!wcf.im) { + wcf_end(); + return 0; + } + + if (!ork_glx_init()) { + wcf_end(); + return 0; + } + + ork_refresh_monitors(); + + if (!ork_sel_glxcfg(&wcf.visual, &wcf.native, &wcf.depth)) { + wcf_end(); + return 0; + } + + if (!ork_create_glx(wcf.native)) { + wcf_end(); + return 0; + } + + return 1; +} + +/*! + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + */ +void wcf_icon(window_t* window, + const byte *image, int width, int height) +{ + if (image) + { + int j; + int longCount = 2 + width * height; + + ulong* icon = mem_zeros(longCount * sizeof(ulong), MEM_IMAGE); + ulong* target = icon; + + *target++ = width; + *target++ = height; + + for (j = 0; j < width * height; j++) + { + *target++ = (((ulong) image[j * 4 + 0]) << 16) | + (((ulong) image[j * 4 + 1]) << 8) | + (((ulong) image[j * 4 + 2]) << 0) | + (((ulong) image[j * 4 + 3]) << 24); + } + + // NOTE: XChangeProperty expects 32-bit values like the image data above to be + // placed in the 32 least significant bits of individual longs. This is + // true even if long is 64-bit and a WM protocol calls for "packed" data. + // This is because of a historical mistake that then became part of the Xlib + // ABI. Xlib will pack these values into a regular array of 32-bit values + // before sending it over the wire. + XChangeProperty(wcf.display, window->handle, + wcf.NET_WM_ICON, + XA_CARDINAL, 32, + PropModeReplace, + (byte*) icon, + longCount); + + mem_free(icon); + } + else + { + XDeleteProperty(wcf.display, window->handle, + wcf.NET_WM_ICON); + } + + XFlush(wcf.display); +} + +void wcf_resize(window_t* window, int width, int height) +{ + if (window->monitor) + return; + if (!window->resizable) + ork_updatehints(window, width, height); + + XResizeWindow(wcf.display, window->handle, width, height); + + XFlush(wcf.display); +} + +void wcf_limits(window_t* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + window->minwidth = minwidth; + window->minheight = minheight; + window->maxwidth = maxwidth; + window->maxheight = maxheight; + + if (window->monitor || !window->resizable) + return; + int width, height; + wcf_getsize(window, &width, &height); + ork_updatehints(window, width, height); + XFlush(wcf.display); +} + +void wcf_minimize(window_t* window) +{ + if (window->override_redir) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + loge(LOG_X11, "Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + XIconifyWindow(wcf.display, window->handle, wcf.screen); + XFlush(wcf.display); +} + +int ork_getstate(window_t* window) +{ + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (ork_getwinprop(window->handle, + wcf.WM_STATE, + wcf.WM_STATE, + (byte**) &state) >= 2) + { + result = state->state; + } + + if (state) + XFree(state); + + return result; +} + +byte ork_wait_visibility(window_t* window) +{ + XEvent dummy; + double timeout = 0.1; + + while (!XCheckTypedWindowEvent(wcf.display, + window->handle, + VisibilityNotify, + &dummy)) + { + if (!ork_wait_xevent(&timeout)) + return 0; + } + + return 1; +} + +void wcf_restore(window_t* window) +{ + if (window->override_redir) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + loge(LOG_X11, "Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + if (ork_getstate(window) == IconicState) + { + XMapWindow(wcf.display, window->handle); + ork_wait_visibility(window); + } + + XFlush(wcf.display); +} + +void wcf_show(window_t* window, byte focus) +{ + if(window->monitor) + return; + if(!ork_winvisible(window)) { + XMapWindow(wcf.display, window->handle); + ork_wait_visibility(window); + } + if(focus) { + if (wcf.NET_ACTIVE_WINDOW) + ork_sendevent(window, wcf.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); + else if (ork_winvisible(window)) + { + XRaiseWindow(wcf.display, window->handle); + XSetInputFocus(wcf.display, window->handle, + RevertToParent, CurrentTime); + } + + XFlush(wcf.display); + } +} + +void wcf_hide(window_t* window) { + if(window->monitor) + return; + XUnmapWindow(wcf.display, window->handle); + XFlush(wcf.display); +} + +void wcf_floating(window_t* window, byte value) { + if(window->floating == value) + return; + window->floating = value; + if(!window->monitor) + ork_winfloating(window, value); +} + +void ork_setmode(monitor_t* monitor, int width, int height, int refresh) +{ + if (wcf.randr.available && !wcf.randr.monitor_broken) + { + vidmode_t current; + RRMode native = None; + + const vidmode_t* best = ork_select_mode(monitor, width, height, refresh); + ork_getmode(monitor, ¤t); + if (ork_compmodes(¤t, best) == 0) + return; + + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(wcf.display, wcf.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(wcf.display, sr, monitor->crtc); + XRROutputInfo* oi = XRRGetOutputInfo(wcf.display, sr, monitor->output); + + for (int i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = ork_modeinfo(sr, oi->modes[i]); + if ((mi->modeFlags & RR_Interlace) != 0) + continue; + + const vidmode_t mode = ork_convmode(mi, ci); + if (ork_compmodes(best, &mode) == 0) + { + native = mi->id; + break; + } + } + + if (native) + { + if (monitor->old_mode == None) + monitor->old_mode = ci->mode; + + XRRSetCrtcConfig(wcf.display, + sr, monitor->crtc, + CurrentTime, + ci->x, ci->y, + native, + ci->rotation, + ci->outputs, + ci->noutput); + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } +} + +void ork_set_monitor(window_t* window) +{ + ork_setmode(window->monitor, window->video_width, window->video_height, window->video_refresh); + + if (window->override_redir) + { + int xpos, ypos; + vidmode_t mode; + + // Manually position the window over its monitor + ork_monitorpos(window->monitor, &xpos, &ypos); + ork_getmode(window->monitor, &mode); + + XMoveResizeWindow(wcf.display, window->handle, + xpos, ypos, mode.width, mode.height); + } + + window->monitor->window = window; +} + +void wcf_fullscreen(window_t* window, int width, int height, int refresh) { + window->video_width = width; + window->video_height = height; + window->video_refresh = refresh; + + monitor_t* monitor = ork_getmonitor(); + if (window->monitor == monitor) + { + if (monitor->window == window) + ork_set_monitor(window); + + XFlush(wcf.display); + return; + } + + if (window->monitor) + { + ork_disabledecor(window); + ork_winfloating(window, window->floating); + ork_unset_monitor(window); + } + + window->monitor = monitor; + ork_updatehints(window, width, height); + + if (!ork_winvisible(window)) + { + XMapRaised(wcf.display, window->handle); + ork_wait_visibility(window); + } + + ork_updatemode(window); + ork_set_monitor(window); + + XFlush(wcf.display); +} + +byte wcf_hovered(window_t* window) +{ + Window w = wcf.root; + while (w) + { + Window root; + int rootX, rootY, childX, childY; + uint mask; + + ork_set_errhandler(); + + const Bool result = XQueryPointer(wcf.display, w, + &root, &w, &rootX, &rootY, + &childX, &childY, &mask); + + ork_unset_errhandler(); + + if (wcf.error_code == BadWindow) + w = wcf.root; + else if (!result) + return 0; + else if (w == window->handle) + return 1; + } + + return 0; +} + +void ork_event(XEvent *event) +{ + int keycode = 0; + Bool filtered = False; + + // HACK: Save scancode as some IMs clear the field in XFilterEvent + if (event->type == KeyPress || event->type == KeyRelease) + keycode = event->xkey.keycode; + + filtered = XFilterEvent(event, None); + + if (wcf.randr.available) + { + if (event->type == wcf.randr.event_base + RRNotify) + { + XRRUpdateConfiguration(event); + ork_refresh_monitors(); + return; + } + } + + if (event->type == wcf.xkb.event_base + XkbEventCode) + { + if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify && + (((XkbEvent*) event)->state.changed & XkbGroupStateMask)) + { + wcf.xkb.group = ((XkbEvent*) event)->state.group; + } + + return; + } + + if (event->type == GenericEvent) + { + return; + } + + if (event->type == SelectionRequest) + { + ork_handle_select(event); + return; + } + + window_t* window = NULL; + if (XFindContext(wcf.display, + event->xany.window, + wcf.context, + (XPointer*) &window) != 0) + { + // This is an event for a window that has already been destroyed + return; + } + + switch (event->type) + { + case ReparentNotify: + { + window->parent = event->xreparent.parent; + return; + } + + case KeyPress: + { + const char key = (keycode < 0 || keycode > 255) ? -1 : wcf.keycodes[keycode]; + const int state = event->xkey.state; + const byte plain = !(state & ControlMask) && !(state & Mod1Mask); + + // HACK: Do not report the key press events duplicated by XIM + // Duplicate key releases are filtered out implicitly by + // the key repeat logic in ork_key + // A timestamp per key is used to handle simultaneous keys + // NOTE: Always allow the first event for each key through + // (the server never sends a timestamp of zero) + // NOTE: Timestamp difference is compared to handle wrap-around + Time diff = event->xkey.time - window->key_times[keycode]; + if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31))) + { + if (keycode) + ork_key(window, key, KEY_PRESS); + + window->key_times[keycode] = event->xkey.time; + } + + if (!filtered) + { + int count; + Status status; + char buffer[100]; + char* chars = buffer; + + count = Xutf8LookupString(window->ic, + &event->xkey, + buffer, sizeof(buffer) - 1, + NULL, &status); + + if (status == XBufferOverflow) + { + chars = mem_zeros(count + 1, MEM_MISC); + count = Xutf8LookupString(window->ic, + &event->xkey, + chars, count, + NULL, &status); + } + + if (plain && (status == XLookupChars || status == XLookupBoth)) + { + const char* c = chars; + uint codepoint; + chars[count] = '\0'; + while (c - chars < count) { + codepoint = ork_decode_utf(&c); + if(codepoint >= 32 && (codepoint <= 126 || codepoint >= 160)) + win_char(window, codepoint); + } + } + + if (chars != buffer) + mem_free(chars); + } + + return; + } + + case KeyRelease: + { + const char key = (keycode < 0 || keycode > 255) ? -1 : wcf.keycodes[keycode]; + + if (!wcf.xkb.detectable) + { + // HACK: Key repeat events will arrive as KeyRelease/KeyPress + // pairs with similar or identical time stamps + // The key repeat logic in ork_key expects only key + // presses to repeat, so detect and discard release events + if (XEventsQueued(wcf.display, QueuedAfterReading)) + { + XEvent next; + XPeekEvent(wcf.display, &next); + + if (next.type == KeyPress && + next.xkey.window == event->xkey.window && + next.xkey.keycode == keycode) + { + // HACK: The time of repeat events sometimes doesn't + // match that of the press event, so add an + // epsilon + // Toshiyuki Takahashi can press a button + // 16 times per second so it's fairly safe to + // assume that no human is pressing the key 50 + // times per second (value is ms) + if ((next.xkey.time - event->xkey.time) < 20) + { + // This is very likely a server-generated key repeat + // event, so ignore it + return; + } + } + } + } + + ork_key(window, key, KEY_RELEASE); + return; + } + + case ButtonPress: + { + if (event->xbutton.button == Button1) + ork_button(window, 0, KEY_PRESS); + else if (event->xbutton.button == Button2) + ork_button(window, 2, KEY_PRESS); + else if (event->xbutton.button == Button3) + ork_button(window, 1, KEY_PRESS); + + // Modern X provides scroll events as mouse button presses + else if (event->xbutton.button == Button4) + win_scroll(window, 0, 1); + else if (event->xbutton.button == Button5) + win_scroll(window, 0, -1); + else if (event->xbutton.button == 6) + win_scroll(window, 1, 0); + else if (event->xbutton.button == 7) + win_scroll(window, -1, 0); + + else + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + ork_button(window, + event->xbutton.button - Button1 - 4, + KEY_PRESS); + } + + return; + } + + case ButtonRelease: + { + if (event->xbutton.button == Button1) + { + ork_button(window, + 0, + KEY_RELEASE); + } + else if (event->xbutton.button == Button2) + { + ork_button(window, + 2, + KEY_RELEASE); + } + else if (event->xbutton.button == Button3) + { + ork_button(window, + 1, + KEY_RELEASE); + } + else if (event->xbutton.button > 7) + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + ork_button(window, + event->xbutton.button - Button1 - 4, + KEY_RELEASE); + } + + return; + } + + case EnterNotify: + { + // XEnterWindowEvent is XCrossingEvent + const int x = event->xcrossing.x; + const int y = event->xcrossing.y; + + ork_mouse(window, x, y); + + window->last_cur_x = x; + window->last_cur_y = y; + return; + } + + case LeaveNotify: + { + return; + } + + case MotionNotify: + { + const int x = event->xmotion.x; + const int y = event->xmotion.y; + + if (x != window->warp_cur_x || + y != window->warp_cur_y) + { + // The cursor was moved by something else + + if (window->cursor_set ^ 1) + { + if (wcf.grab_cur_window != window) + return; + + const int dx = x - window->last_cur_x; + const int dy = y - window->last_cur_y; + + ork_mouse(window, + window->virtual_cur_x + dx, + window->virtual_cur_y + dy); + } + else + ork_mouse(window, x, y); + } + + window->last_cur_x = x; + window->last_cur_y = y; + return; + } + + case ConfigureNotify: + { + if (event->xconfigure.width != window->width || + event->xconfigure.height != window->height) + { + win_fbsize(window, + event->xconfigure.width, + event->xconfigure.height); + + // win_size(window, + // event->xconfigure.width, + // event->xconfigure.height); + + window->width = event->xconfigure.width; + window->height = event->xconfigure.height; + } + + int xpos = event->xconfigure.x; + int ypos = event->xconfigure.y; + + // NOTE: ConfigureNotify events from the server are in local + // coordinates, so if we are reparented we need to translate + // the position into root (screen) coordinates + if (!event->xany.send_event && window->parent != wcf.root) + { + ork_set_errhandler(); + + Window dummy; + XTranslateCoordinates(wcf.display, + window->parent, + wcf.root, + xpos, ypos, + &xpos, &ypos, + &dummy); + + ork_unset_errhandler(); + if (wcf.error_code == BadWindow) + return; + } + + if (xpos != window->xpos || ypos != window->ypos) + { + win_pos(window, xpos, ypos); + window->xpos = xpos; + window->ypos = ypos; + } + + return; + } + + case ClientMessage: + { + // Custom client message, probably from the window manager + + if (filtered) + return; + + if (event->xclient.message_type == None) + return; + + if (event->xclient.message_type == wcf.WM_PROTOCOLS) + { + const Atom protocol = event->xclient.data.l[0]; + if (protocol == None) + return; + + if (protocol == wcf.WM_DELETE_WINDOW) + { + // The window manager was asked to close the window, for + // example by the user pressing a 'close' window decoration + // button + win_closed(window); + } + else if (protocol == wcf.NET_WM_PING) + { + // The window manager is pinging the application to ensure + // it's still responding to events + + XEvent reply = *event; + reply.xclient.window = wcf.root; + + XSendEvent(wcf.display, wcf.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &reply); + } + } + else if (event->xclient.message_type == wcf.XdndEnter) + { + // A drag operation has entered the window + ulong i, count; + Atom* formats = NULL; + const byte list = event->xclient.data.l[1] & 1; + + wcf.xdnd.source = event->xclient.data.l[0]; + wcf.xdnd.version = event->xclient.data.l[1] >> 24; + wcf.xdnd.format = None; + + if (wcf.xdnd.version > WCF_XDND_VERSION) + return; + + if (list) + { + count = ork_getwinprop(wcf.xdnd.source, + wcf.XdndTypeList, + XA_ATOM, + (byte**) &formats); + } + else + { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + + for (i = 0; i < count; i++) + { + if (formats[i] == wcf.text_uri_list) + { + wcf.xdnd.format = wcf.text_uri_list; + break; + } + } + + if (list && formats) + XFree(formats); + } + else if (event->xclient.message_type == wcf.XdndDrop) + { + // The drag operation has finished by dropping on the window + Time time = CurrentTime; + + if (wcf.xdnd.version > WCF_XDND_VERSION) + return; + + if (wcf.xdnd.format) + { + if (wcf.xdnd.version >= 1) + time = event->xclient.data.l[2]; + + // Request the chosen format from the source window + XConvertSelection(wcf.display, + wcf.XdndSelection, + wcf.xdnd.format, + wcf.XdndSelection, + window->handle, + time); + } + else if (wcf.xdnd.version >= 2) + { + XEvent reply = { ClientMessage }; + reply.xclient.window = wcf.xdnd.source; + reply.xclient.message_type = wcf.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->handle; + reply.xclient.data.l[1] = 0; // The drag was rejected + reply.xclient.data.l[2] = None; + + XSendEvent(wcf.display, wcf.xdnd.source, + False, NoEventMask, &reply); + XFlush(wcf.display); + } + } + else if (event->xclient.message_type == wcf.XdndPosition) + { + // The drag operation has moved over the window + const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; + const int yabs = (event->xclient.data.l[2]) & 0xffff; + Window dummy; + int xpos, ypos; + + if (wcf.xdnd.version > WCF_XDND_VERSION) + return; + + XTranslateCoordinates(wcf.display, + wcf.root, + window->handle, + xabs, yabs, + &xpos, &ypos, + &dummy); + + ork_mouse(window, xpos, ypos); + + XEvent reply = { ClientMessage }; + reply.xclient.window = wcf.xdnd.source; + reply.xclient.message_type = wcf.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->handle; + reply.xclient.data.l[2] = 0; // Specify an empty rectangle + reply.xclient.data.l[3] = 0; + + if (wcf.xdnd.format) + { + // Reply that we are ready to copy the dragged data + reply.xclient.data.l[1] = 1; // Accept with no rectangle + if (wcf.xdnd.version >= 2) + reply.xclient.data.l[4] = wcf.XdndActionCopy; + } + + XSendEvent(wcf.display, wcf.xdnd.source, + False, NoEventMask, &reply); + XFlush(wcf.display); + } + + return; + } + + case SelectionNotify: + { + if (event->xselection.property == wcf.XdndSelection) + { + // The converted data from the drag operation has arrived + char* data; + const ulong result = + ork_getwinprop(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (byte**) &data); + + if (result) + { + int i, count; + char** paths = ork_parse_uri(data, &count); + + win_drop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + mem_free(paths[i]); + mem_free(paths); + } + + if (data) + XFree(data); + + if (wcf.xdnd.version >= 2) + { + XEvent reply = { ClientMessage }; + reply.xclient.window = wcf.xdnd.source; + reply.xclient.message_type = wcf.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->handle; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = wcf.XdndActionCopy; + + XSendEvent(wcf.display, wcf.xdnd.source, + False, NoEventMask, &reply); + XFlush(wcf.display); + } + } + + return; + } + + case FocusIn: + { + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->cursor_set ^ 1) + ork_disablecur(window); + + XSetICFocus(window->ic); + + // ork_resetkeys(window, 1); + wcf.active = window; + win_focus(window, 1); + return; + } + + case FocusOut: + { + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->cursor_set ^ 1) + ork_enablecur(window); + + XUnsetICFocus(window->ic); + + if (window->monitor) + wcf_minimize(window); + + if(wcf.active == window) + wcf.active = NULL; + win_focus(window, 0); + ork_resetkeys(window); + return; + } + + case Expose: + { + win_redraw(window); + return; + } + + case PropertyNotify: + { + if (event->xproperty.state != PropertyNewValue) + return; + + if (event->xproperty.atom == wcf.WM_STATE) + { + const int state = ork_getstate(window); + if (state != IconicState && state != NormalState) + return; + + const byte iconified = (state == IconicState); + if (window->iconified != iconified) + { + if (window->monitor) + { + if (iconified) + ork_unset_monitor(window); + else + ork_set_monitor(window); + } + + window->iconified = iconified; + } + } + + return; + } + + case DestroyNotify: + return; + } +} + +/*! + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require to register callbacks of its own + * can pass events in response to many window system function calls. + */ +void wcf_poll() +{ + XPending(wcf.display); + + while (XQLength(wcf.display)) + { + XEvent event; + XNextEvent(wcf.display, &event); + ork_event(&event); + } + + window_t* window = wcf.grab_cur_window; + if (window) + { + int width, height; + wcf_getsize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking event waiting with MotionNotify + if (window->last_cur_x != width / 2 || + window->last_cur_y != height / 2) + { + ork_setcur(window, width / 2, height / 2); + } + } + + XFlush(wcf.display); +} diff --git a/bank.h b/bank.h new file mode 100644 index 0000000..49feab1 --- /dev/null +++ b/bank.h @@ -0,0 +1,404 @@ + +void bank_reset(opl3_chip *chip, bank_handle *bank) { + ushort voices = chip->n_voices; + byte flags = bank->flags; + char velo_func = bank->velo_func; + bank_instr *instr = bank->bdata; + byte *bankdata = (byte*)bank; + bankdata += sizeof(bank_handle); + memset(bank, 0, sizeof(bank_handle)); + bank->voices = (bank_voice*)bankdata; + bank->v_avail = voices; + bank->flags = flags; + bank->velo_func = velo_func; + bank->bdata = instr; + for(int h = 0; h < 16; h++) { + bank_channel *channel = &bank->channel[h]; + channel->bank = bank; + memset(channel->notes, 0xff, 128); + channel->volume = 127; + channel->pbank = (h == 9) ? 128 : 0; + channel->instr = &bank->bdata[(h == 9) ? 128 : 0]; + channel->ch_num = h; + } + for(int h = 0; h < voices; h++) { + bank_voice *voice = &bank->voices[h]; + voice->channel = NULL; + voice->opl = &chip->channel[h]; + voice->note = 0xff; + voice->op = b_op2; + voice->detune = 0; + voice->pair = &bank->voices[h+(((h & 1) == 0) ? 1 : -1)]; + } +} + +bank_handle *bank_alloc(opl3_chip *chip, bank_instr *instr, byte keep, byte useunkn, char velofunc) { + byte *bankdata = malloc(sizeof(bank_handle) + (sizeof(bank_voice) * chip->n_voices)); + bank_handle *bank = (bank_handle*)bankdata; + bank->flags = (keep ? BANK_KEEP : 0) | (useunkn ? BANK_UNKN : 0); + bank->velo_func = velofunc; + bank->bdata = instr; + bank_reset(chip, bank); + return bank; +} + +byte bank_getnote(bank_channel *ch, byte key, byte id, byte drum, bank_instr *instr) { + short note = (short)key; + if(instr->fixed /* || drum */) { + note = ((short)instr->percnum) + instr->channels[id].offset; + } + else { + note += instr->channels[id].offset; + } + note = note < 0 ? 0 : note; + note = note > 127 ? 127 : note; + return (byte)note; +} + +void OPL3_ChannelFreqHz(opl3_channel *channel, uint freq) { + uint block = 0; + while(opl3_maxfreq[block] < freq) { + block++; + if(block == 8) { + break; + } + } + if(block == 8) { + OPL3_ChannelFreq(channel, 7, 1023); + return; + } + double f_num = ((double)freq) / 1000.0 * pow(2.0, (20.0-((double)block))) / 49716.0; + OPL3_ChannelFreq(channel, block, (uint)f_num); +} + +uint bank_getfreq(byte key, short pitch, short detune) { + double pfrq = pow(2.0, ((((double)pitch) / 8191.0 * BANK_PBRANGE) + (((double)detune) / 100.0)) / 12.0); + double freq = ((double)bank_notes[key]) * pfrq; + return (uint)freq; +} + +void OPL3_ChannelNote(opl3_channel *channel, byte key, short pitch, short detune) { + OPL3_ChannelFreqHz(channel, bank_getfreq(key, pitch, detune)); +} + +uint bank_getlevel(char function, byte velocity, byte volume, char pan) { + double lvl = ((function == -128) ? 1.0 : + (function ? BANK_VELOFUNC(((double)velocity)/127.0, function < 0 ? (0.1+(1.0-((((double)(-function))-1.0)/126.0))*9.9) : (1.0+((((double)function)-1.0)/126.0)*9.0)) : + (((double)velocity)/127.0))) * (((double)volume)/127.0) * (1.0-(0.9*((double)pan)/63.0)); + // fprintf(stderr, "%d===%d-->%.2f\n", function, velocity, lvl); + lvl *= 65536.0; + return (uint)lvl; +} + +void OPL3_ChannelLevelPan(opl3_channel *channel, char function, byte velocity, byte volume, char pan) { + OPL3_ChannelOutput(channel, 0, bank_getlevel(function, velocity, volume, pan)); + OPL3_ChannelOutput(channel, 1, bank_getlevel(function, velocity, volume, -pan)); +} + +void bank_release_voice(bank_handle *bank, bank_channel *ch, bank_voice *voice) { + if(voice->note == 0xff) { + return; + } + OPL3_ChannelKeyOff(voice->opl); + voice->note = 0xff; + if(voice->op == b_op22) { + voice->pair->note = 0xff; + OPL3_ChannelKeyOff(voice->pair->opl); + bank->v_used -= 2; + } + else if(voice->op == b_op4) { + voice->pair->note = 0xff; + bank->v_used -= 2; + } + else { + bank->v_used -= 1; + } +} + +bank_voice *bank_release_key(bank_channel *ch, byte note) { + if(ch->active == 0) { + return NULL; + } + if(ch->notes[note] == 0xff) { + return NULL; + } + bank_key *key = &ch->keys[ch->notes[note]]; + ch->notes[note] = 0xff; + ch->active -= 1; + if(key->voice) { + bank_release_voice(ch->bank, ch, key->voice); + } + key->velocity = 0; + return key->voice; +} + +void bank_init_voice(opl3_channel *channel, bank_instr *instr, byte id) { + OPL3_Channel4Op(channel, instr->op == b_op4); + for(int o = 0; o < ((instr->op == b_op4) ? 4 : 2); o++) { + bank_pair *pair = &instr->channels[(instr->op == b_op4) ? (o >> 1) : id]; + bank_operator *op = &pair->ops[o & 1]; + opl3_slot *slot = (o >= 2) ? (channel->pair->slots[o & 1]) : (channel->slots[o & 1]); + OPL3_SlotFlags(slot, op->tremolo, op->vibrato, op->sustaining, op->ksr); + OPL3_SlotMult(slot, op->mult); + OPL3_SlotKSL(slot, op->ksl); + OPL3_SlotLevel(slot, op->level); + OPL3_SlotADSR(slot, op->attack, op->decay, op->sustain, op->release); + OPL3_SlotWaveform(slot, op->waveform); + if((o & 1) == 1) { + opl3_channel *chn = (o >= 2) ? channel->pair : channel; + OPL3_ChannelFeedback(chn, pair->feedback); + OPL3_ChannelAM(chn, pair->am); + } + } +} + +bank_voice *bank_get_voice(bank_handle *bank, bank_channel *ch, byte note, byte velocity) { + bank_instr *instr = ch->instr; + if(ch->pbank == 128) { + instr += note; + } + else if((ch->pbank != 0) && (!(bank->flags & BANK_UNKN))) { + return NULL; + } + if(instr->op == b_op0) { + return NULL; + } + if(bank->v_used == bank->v_avail) { + if(bank->flags & BANK_KEEP) { + return NULL; + } + if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) { + bank->voiceindex += 1; + if(bank->voiceindex >= bank->v_avail) { + bank->voiceindex = 0; + } + } + bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note); + } + else if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) { + bank->voiceindex += 1; + if(bank->voiceindex >= bank->v_avail) { + bank->voiceindex = 0; + } + } + byte vi = bank->voiceindex; + while((bank->voices[bank->voiceindex].note != 0xff) || ((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff))) { + bank->voiceindex += (instr->op != b_op2) ? 2 : 1; + if(bank->voiceindex >= bank->v_avail) { + bank->voiceindex = 0; + } + if(vi == bank->voiceindex) { + if(bank->flags & BANK_KEEP) { + return NULL; + } + bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note); + if((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff)) { + bank_release_key(bank->voices[bank->voiceindex+1].channel, bank->voices[bank->voiceindex+1].note); + } + break; + } + } + bank_voice *voice = &bank->voices[bank->voiceindex]; + if((instr->op == b_op2) && (voice->op == b_op4)) { + short offset = ((short)bank->voiceindex) + (((bank->voiceindex & 1) == 0) ? 1 : -1); + bank_voice *voice2 = &bank->voices[offset]; + if(voice2->note == 0xff) { + OPL3_ChannelOutput(voice2->opl, 0, 0); + OPL3_ChannelOutput(voice2->opl, 1, 0); + } + } + bank->voiceindex += (instr->op != b_op2) ? 2 : 1; + if(bank->voiceindex >= bank->v_avail) { + bank->voiceindex = 0; + } + voice->channel = ch; + voice->note = note; + voice->op = instr->op; + voice->detune = instr->channels[0].detune; + bank_init_voice(voice->opl, instr, 0); + if(voice->op == b_op22) { + voice->pair->channel = ch; + voice->pair->note = note; + voice->pair->op = b_op22; + voice->pair->detune = instr->channels[1].detune; + bank_init_voice(voice->pair->opl, instr, 1); + OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune); + OPL3_ChannelNote(voice->pair->opl, bank_getnote(ch, note, 1, ch->pbank == 128, instr), ch->pitch, voice->pair->detune); + OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan); + OPL3_ChannelLevelPan(voice->pair->opl, bank->velo_func, velocity, ch->volume, ch->pan); + OPL3_ChannelKeyOn(voice->opl); + OPL3_ChannelKeyOn(voice->pair->opl); + bank->v_used += 2; + } + else if(voice->op == b_op4) { + voice->pair->channel = ch; + voice->pair->note = note; + voice->pair->op = b_op4; + OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune); + OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan); + OPL3_ChannelKeyOn(voice->opl); + bank->v_used += 2; + } + else { + OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune); + OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan); + OPL3_ChannelKeyOn(voice->opl); + bank->v_used += 1; + } + return voice; +} + +bank_voice *bank_press_key(bank_channel *ch, byte note, byte velocity) { + if(ch->notes[note] != 0xff) { + bank_release_key(ch, note); + } + if(ch->active == BANK_MAX) { + return NULL; + } + byte ki = ch->keyindex; + while(ch->keys[ch->keyindex].velocity != 0) { + ch->keyindex += 1; + if(ch->keyindex == BANK_MAX) { + ch->keyindex = 0; + } + if(ki == ch->keyindex) { + return NULL; + } + } + bank_voice *voice = bank_get_voice(ch->bank, ch, note, velocity); + ch->notes[note] = ch->keyindex; + ch->active += 1; + bank_key *key = &ch->keys[ch->keyindex]; + key->note = note; + key->velocity = velocity; + key->voice = voice; + return voice; +} + +void bank_notesoff(bank_channel *ch) { + for(int h = 0; (h < BANK_MAX) && (ch->active > 0); h++) { + bank_release_key(ch, h); + } +} + +void bank_progupdate(bank_channel *ch, opl3_chip *chip) { + bank_notesoff(ch); + ushort id = ch->program; + if(ch->pbank == 128) { + id = 128; + } + else if((ch->pbank != 0) && (!(ch->bank->flags & BANK_UNKN))) { + id = 0; + } + ch->instr = (ch->bank->bdata)+id; +} + +void bank_levelupdate(bank_channel *ch, opl3_chip *chip) { + byte done = 0; + for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) { + bank_key *key = (ch->keys)+h; + if((key->velocity == 0) || (key->voice == NULL)) { + continue; + } + OPL3_ChannelLevelPan(key->voice->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan); + if(key->voice->op == b_op22) { + OPL3_ChannelLevelPan(key->voice->pair->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan); + } + done++; + } +} + +void bank_frequpdate(bank_channel *ch, opl3_chip *chip) { + byte done = 0; + for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) { + bank_key *key = (ch->keys)+h; + if((key->velocity == 0) || (key->voice == NULL)) { + continue; + } + OPL3_ChannelNote(key->voice->opl, bank_getnote(ch, key->note, 0, ch->pbank == 128, ch->instr), ch->pitch, key->voice->detune); + if(key->voice->op == b_op22) { + OPL3_ChannelNote(key->voice->pair->opl, bank_getnote(ch, key->note, 1, ch->pbank == 128, ch->instr), ch->pitch, key->voice->pair->detune); + } + done++; + } +} + +void bank_noteon(bank_handle *bank, opl3_chip *chip, byte channel, byte key, byte velocity) { + bank_channel *ch = &bank->channel[channel]; + // if((ch->pbank == 128) && ((key < 35) || (key > 81))) { + // return; + // } + bank_press_key(ch, key, velocity); +} + +void bank_noteoff(bank_handle *bank, opl3_chip *chip, byte channel, byte key, byte velocity) { + bank_channel *ch = &bank->channel[channel]; + // if((ch->pbank == 128) && ((key < 35) || (key > 81))) { + // return; + // } + bank_release_key(ch, key); +} + +void bank_progchange(bank_handle *bank, opl3_chip *chip, byte channel, byte program) { + bank_channel *ch = &bank->channel[channel]; + ch->program = program; + bank_progupdate(ch, chip); +} + +void bank_pitchbend(bank_handle *bank, opl3_chip *chip, byte channel, short pitch) { + bank_channel *ch = &bank->channel[channel]; + ch->pitch = pitch; + bank_frequpdate(ch, chip); +} + +void bank_control(bank_handle *bank, opl3_chip *chip, byte channel, byte control, byte value) { + bank_channel *ch = &bank->channel[channel]; + switch(control) { + case 0x00: // bank MSB + // if((channel == 9) && (value == 0)) { + // ch->pbank = 128; + // } + // else { + if(channel != 9) { + ch->pbank &= 0x007f; + ch->pbank |= (((ushort)value) << 7) & 0x3f80; + } + bank_progupdate(ch, chip); + break; + case 0x20: // bank LSB + // if((channel == 9) && (value == 0)) { + // ch->pbank = 128; + // } + // else { + if(channel != 9) { + ch->pbank &= 0x3f80; + ch->pbank |= ((ushort)value) & 0x007f; + } + bank_progupdate(ch, chip); + break; + case 0x07: // volume MSB + ch->volume = value; + bank_levelupdate(ch, chip); + break; + case 0x0a: // pan MSB + ch->pan = ((char)value) - 64; + bank_levelupdate(ch, chip); + break; + case 0x79: // reset + ch->pbank = (channel == 9) ? 128 : 0; + ch->volume = 127; + ch->pan = 0; + bank_progupdate(ch, chip); + bank_levelupdate(ch, chip); + break; + case 0x7b: // all off + bank_notesoff(ch); + break; + } +} + +void bank_alloff(bank_handle *bank) { + for(int h = 0; h < 16; h++) { + bank_notesoff(&bank->channel[h]); + } +} \ No newline at end of file diff --git a/banks/bank49.opl b/banks/bank49.opl new file mode 100644 index 0000000..3016751 Binary files /dev/null and b/banks/bank49.opl differ diff --git a/banks/bank53.opl b/banks/bank53.opl new file mode 100644 index 0000000..f0c24fa Binary files /dev/null and b/banks/bank53.opl differ diff --git a/banks/cyberpuck.tmb b/banks/cyberpuck.tmb new file mode 100644 index 0000000..4ebadab Binary files /dev/null and b/banks/cyberpuck.tmb differ diff --git a/banks/d3dtimbr.tmb b/banks/d3dtimbr.tmb new file mode 100644 index 0000000..bc65895 Binary files /dev/null and b/banks/d3dtimbr.tmb differ diff --git a/banks/d3dtimbr_mod.tmb b/banks/d3dtimbr_mod.tmb new file mode 100644 index 0000000..fdd0fa3 Binary files /dev/null and b/banks/d3dtimbr_mod.tmb differ diff --git a/banks/dmx_dmx.op2 b/banks/dmx_dmx.op2 new file mode 100644 index 0000000..111091d Binary files /dev/null and b/banks/dmx_dmx.op2 differ diff --git a/banks/dmx_doom1.op2 b/banks/dmx_doom1.op2 new file mode 100644 index 0000000..f418296 Binary files /dev/null and b/banks/dmx_doom1.op2 differ diff --git a/banks/dmx_doom2.op2 b/banks/dmx_doom2.op2 new file mode 100644 index 0000000..f9a152f Binary files /dev/null and b/banks/dmx_doom2.op2 differ diff --git a/banks/dmx_raptor.op2 b/banks/dmx_raptor.op2 new file mode 100644 index 0000000..b553d62 Binary files /dev/null and b/banks/dmx_raptor.op2 differ diff --git a/banks/dmx_strife.op2 b/banks/dmx_strife.op2 new file mode 100644 index 0000000..4c13b7f Binary files /dev/null and b/banks/dmx_strife.op2 differ diff --git a/banks/drumopl.tmb b/banks/drumopl.tmb new file mode 100644 index 0000000..7339f99 Binary files /dev/null and b/banks/drumopl.tmb differ diff --git a/banks/earthsieg.ad b/banks/earthsieg.ad new file mode 100644 index 0000000..a918f4c Binary files /dev/null and b/banks/earthsieg.ad differ diff --git a/banks/fat2.op3 b/banks/fat2.op3 new file mode 100644 index 0000000..729f6fc Binary files /dev/null and b/banks/fat2.op3 differ diff --git a/banks/fat4.op3 b/banks/fat4.op3 new file mode 100644 index 0000000..daf307e Binary files /dev/null and b/banks/fat4.op3 differ diff --git a/banks/gmoconel.tmb b/banks/gmoconel.tmb new file mode 100644 index 0000000..5f58ba2 Binary files /dev/null and b/banks/gmoconel.tmb differ diff --git a/banks/gmopl.ibk b/banks/gmopl.ibk new file mode 100644 index 0000000..e177514 Binary files /dev/null and b/banks/gmopl.ibk differ diff --git a/banks/gmopl_mod.tmb b/banks/gmopl_mod.tmb new file mode 100644 index 0000000..d23ee4e Binary files /dev/null and b/banks/gmopl_mod.tmb differ diff --git a/banks/hmi_144.skb b/banks/hmi_144.skb new file mode 100644 index 0000000..d790343 Binary files /dev/null and b/banks/hmi_144.skb differ diff --git a/banks/insmaker_std.bnk b/banks/insmaker_std.bnk new file mode 100644 index 0000000..c0ec9d1 Binary files /dev/null and b/banks/insmaker_std.bnk differ diff --git a/banks/jv_2op.op3 b/banks/jv_2op.op3 new file mode 100644 index 0000000..b94b3a1 Binary files /dev/null and b/banks/jv_2op.op3 differ diff --git a/banks/mt32.ibk b/banks/mt32.ibk new file mode 100644 index 0000000..c5254b9 Binary files /dev/null and b/banks/mt32.ibk differ diff --git a/banks/nemesis.opl b/banks/nemesis.opl new file mode 100644 index 0000000..a34d8fe Binary files /dev/null and b/banks/nemesis.opl differ diff --git a/banks/op2x2.op3 b/banks/op2x2.op3 new file mode 100644 index 0000000..7d5c2e5 Binary files /dev/null and b/banks/op2x2.op3 differ diff --git a/banks/std_o3.skb b/banks/std_o3.skb new file mode 100644 index 0000000..1daaa2c Binary files /dev/null and b/banks/std_o3.skb differ diff --git a/banks/std_sb.skb b/banks/std_sb.skb new file mode 100644 index 0000000..e9bc5e0 Binary files /dev/null and b/banks/std_sb.skb differ diff --git a/banks/swtimbr.tmb b/banks/swtimbr.tmb new file mode 100644 index 0000000..6c3e72c Binary files /dev/null and b/banks/swtimbr.tmb differ diff --git a/banks/tmb_default.tmb b/banks/tmb_default.tmb new file mode 100644 index 0000000..748a3dd Binary files /dev/null and b/banks/tmb_default.tmb differ diff --git a/banks/wallace.op3 b/banks/wallace.op3 new file mode 100644 index 0000000..9642a8d Binary files /dev/null and b/banks/wallace.op3 differ diff --git a/banks/wallence.tmb b/banks/wallence.tmb new file mode 100644 index 0000000..9c7525d Binary files /dev/null and b/banks/wallence.tmb differ diff --git a/banks/warcraft.ad b/banks/warcraft.ad new file mode 100644 index 0000000..b373a06 Binary files /dev/null and b/banks/warcraft.ad differ diff --git a/banks/wolfinstein.op2 b/banks/wolfinstein.op2 new file mode 100644 index 0000000..ddf4cde Binary files /dev/null and b/banks/wolfinstein.op2 differ diff --git a/bbox.h b/bbox.h new file mode 100644 index 0000000..cf3050d --- /dev/null +++ b/bbox.h @@ -0,0 +1,192 @@ + +bbox_t *box_offset_to(bbox_t *aa, bbox_t *bb, double x, double y, double z) { + bb->x1 = aa->x1 + x; + bb->x2 = aa->x2 + x; + bb->y1 = aa->y1 + y; + bb->y2 = aa->y2 + y; + bb->z1 = aa->z1 + z; + bb->z2 = aa->z2 + z; + return bb; +} + +bbox_t *box_add_coord(bbox_t *aa, bbox_t *bb, double x, double y, double z) { + if(aa) + AABB_PCOPY(aa, bb); + if(x < 0.0D) + bb->x1 += x; + else if(x > 0.0D) + bb->x2 += x; + if(y < 0.0D) + bb->y1 += y; + else if(y > 0.0D) + bb->y2 += y; + if(z < 0.0D) + bb->z1 += z; + else if(z > 0.0D) + bb->z2 += z; + return bb; +} + +bbox_t *box_union(bbox_t *aa, bbox_t *bb, double x, double y, double z) { + if(aa) + AABB_PCOPY(aa, bb); + bb->x1 = MIN_VALUE(bb->x1, x); + bb->y1 = MIN_VALUE(bb->y1, y); + bb->z1 = MIN_VALUE(bb->z1, z); + bb->x2 = MAX_VALUE(bb->x2, x); + bb->y2 = MAX_VALUE(bb->y2, y); + bb->z2 = MAX_VALUE(bb->z2, z); + return bb; +} + +#define BOX_OFFSET_FUNC(cc, cd, ce) \ +double box_calc_ ## cc ## off(bbox_t *aa, bbox_t *bb, double offset) { \ + if(bb->cd ## 2 > aa->cd ## 1 && bb->cd ## 1 < aa->cd ## 2 && bb->ce ## 2 > aa->ce ## 1 && bb->ce ## 1 < aa->ce ## 2) { \ + if(offset > 0.0 && bb->cc ## 2 <= aa->cc ## 1) { \ + double pdelta = aa->cc ## 1 - bb->cc ## 2; \ + if(pdelta < offset) \ + offset = pdelta; \ + } \ + else if(offset < 0.0 && bb->cc ## 1 >= aa->cc ## 2) { \ + double ndelta = aa->cc ## 2 - bb->cc ## 1; \ + if(ndelta > offset) \ + offset = ndelta; \ + } \ + return offset; \ + } \ + else { \ + return offset; \ + } \ +} + +BOX_OFFSET_FUNC(x, y, z) +BOX_OFFSET_FUNC(y, x, z) +BOX_OFFSET_FUNC(z, x, y) + +byte box_intersects(bbox_t *aa, bbox_t *bb) { + return bb->x2 > aa->x1 && bb->x1 < aa->x2 && bb->y2 > aa->y1 && bb->y1 < aa->y2 && bb->z2 > aa->z1 && bb->z1 < aa->z2; +} + +chunk_t *chunk_get(world_t *world, int x, int y, int z, byte load); + +int box_get_colliding(world_t *world, bbox_t **listptr, bbox_t *box) { + bbox_t *list = sys.collision_list; + int x1 = ((int)(double)(box->x1 + (box->x2 - box->x1) / 2.0)) / CHUNK_SIZE; + int y1 = ((int)(double)(box->y1 + (box->y2 - box->y1) / 2.0)) / CHUNK_SIZE; + int z1 = ((int)(double)(box->z1 + (box->z2 - box->z1) / 2.0)) / CHUNK_SIZE; + int x2 = x1 + 2; + int y2 = y1 + 2; + int z2 = z1 + 2; + x1 -= 2; + y1 -= 2; + z1 -= 2; + // x1 = CHUNK_CLAMP(world, x1, x); + // y1 = CHUNK_CLAMP(world, y1, y); + // z1 = CHUNK_CLAMP(world, z1, z); + // x2 = CHUNK_CLAMP(world, x2, x); + // y2 = CHUNK_CLAMP(world, y2, y); + // z2 = CHUNK_CLAMP(world, z2, z); + int boxes = 0; + ulong pos = 0; + chunk_t *chunk; // = &world->global; + geometry_t **gptr; + geometry_t *geom; + if(listptr) + *listptr = list; + while(gptr = tbl_iter(&world->global_geom, &pos)) { + geom = *gptr; + if(box_intersects(&geom->collision, box)) { + list[boxes++] = geom->collision; + if(boxes == COLL_MAX) + return boxes; + } + } + for(int cx = x1; cx <= x2; cx++) { + for(int cz = z1; cz <= z2; cz++) { + for(int cy = y1; cy <= y2; cy++) { + if(!(chunk = chunk_get(world, cx, cy, cz, 0))) + continue; + pos = 0; + while(geom = tbl_iter(&chunk->obj_table, &pos)) { + if(!geom->global && box_intersects(&geom->collision, box)) { + list[boxes++] = geom->collision; + if(boxes == COLL_MAX) + return boxes; + } + } + } + } + } + return boxes; +} + +byte box_check_intersect(vec3 origin, vec3 dir, chunk_t *chunk, polygon_t *poly, float *dist) { + vec3 vects[3]; + for(int n = 0; n < 3; n++) { + VEC3_SET(vects[n], poly->vertices[n].pos X + (float)chunk->box.x1, poly->vertices[n].pos Y + (float)chunk->box.y1, + poly->vertices[n].pos Z + (float)chunk->box.z1); + } + return glm_ray_triangle(origin, dir, vects[0], vects[1], vects[2], dist); +} + +ulong box_get_pointed(world_t *world, camera_t *cam, vec3 pos) { + vec3 origin; + ulong iter = 0; + ulong id = 0; + float dist; + float mdist = 1000000.0f; + chunk_t *chunk; + geometry_t **gptr; + geometry_t *geom; + polygon_t *poly; + int x = (int)NEG_OFFSET(cam->pos_x, CHUNK_SIZE); + int y = (int)NEG_OFFSET(cam->pos_y, CHUNK_SIZE); + int z = (int)NEG_OFFSET(cam->pos_z, CHUNK_SIZE); + VEC3_SET(origin, (float)cam->pos_x, (float)cam->pos_y, (float)cam->pos_z); + + while(gptr = tbl_iter(&world->global_geom, &iter)) { + geom = *gptr; + if(!(chunk = chunk_get(world, geom->chunk_x, geom->chunk_y, geom->chunk_z, 0))) + continue; + poly = &chunk->poly_pool[geom->polys]; + while(poly) { + if(box_check_intersect(origin, cam->front, chunk, poly, &dist) && (dist < mdist)) { + mdist = dist; + id = geom->id; + if(pos) { + glm_vec3_scale(cam->front, dist, pos); + glm_vec3_add(origin, pos, pos); + } + } + poly = (poly->next == 0xffffffff) ? NULL : &chunk->poly_pool[poly->next]; + } + } + + for(int cx = -1; cx <= 1; cx++) { + for(int cz = -1; cz <= 1; cz++) { + for(int cy = -1; cy <= 1; cy++) { + if(!(chunk = chunk_get(world, x + cx, y + cy, z + cz, 0))) + continue; + iter = 0; + while(geom = tbl_iter(&chunk->obj_table, &iter)) { + if(!geom->global) { + poly = &chunk->poly_pool[geom->polys]; + while(poly) { + if(box_check_intersect(origin, cam->front, chunk, poly, &dist) && (dist < mdist)) { + mdist = dist; + id = geom->id; + if(pos) { + glm_vec3_scale(cam->front, dist, pos); + glm_vec3_add(origin, pos, pos); + } + } + poly = (poly->next == 0xffffffff) ? NULL : &chunk->poly_pool[poly->next]; + } + } + } + } + } + } + + return id; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..c04474e --- /dev/null +++ b/buffer.h @@ -0,0 +1,101 @@ + +int buf_init_int(byte dynamic, uint *vbo, uint *vao, const float *verts, uint size, int stride, va_list ap) { + int elems; + int pos; + int idx = 0; + glGenVertexArrays(1, vao); + glGenBuffers(1, vbo); + glBindBuffer(GL_ARRAY_BUFFER, *vbo); + glBufferData(GL_ARRAY_BUFFER, size, verts, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + gdr.buf_mem += size; + gdr.buf_peak = gdr.buf_mem > gdr.buf_peak ? gdr.buf_mem : gdr.buf_peak; + glBindVertexArray(*vao); + for(pos = 0; pos < stride; pos += elems) { + elems = va_arg(ap, int); + glVertexAttribPointer(idx, elems, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(pos * sizeof(float))); + glEnableVertexAttribArray(idx++); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return idx; +} + +void buf_init_sys(uint *vbo, uint *vao, const float *verts, uint size, int stride, ...) { + va_list ap; + va_start(ap, stride); + buf_init_int(0, vbo, vao, verts, size, stride, ap); + logd(LOG_GFX, STR_BUF_ILOADED, size, stride, *vbo, *vao); + va_end(ap); +} + +buf_t *buf_init(byte dynamic, uint *vbo, uint *vao, const float *verts, uint size, int stride, ...) { + va_list ap; + va_start(ap, stride); + int idx; + buf_t *bind; + sys_assert(bind = (buf_t*)tbl_push(&gdr.buffers)); + idx = buf_init_int(dynamic, vbo, vao, verts, size, stride, ap); + bind->vao = vao; + bind->vbo = vbo; + bind->size = size; + bind->stride = stride; + bind->groups = idx; + bind->dynamic = dynamic; + gdr.buf_loaded += 1; + logt(LOG_GFX, STR_BUF_LOADED, size, stride, *vbo, *vao); + va_end(ap); + return bind; +} + +void buf_data(buf_t *buf, const float *verts, uint size) { + size *= ((uint)buf->stride) * sizeof(float); + glBindBuffer(GL_ARRAY_BUFFER, *(buf->vbo)); + glBufferData(GL_ARRAY_BUFFER, size, verts, buf->dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + if(buf->size != size) { + logt(LOG_GFX, STR_BUF_RESIZED, *(buf->vbo), *(buf->vao), buf->size, size, buf->stride); + gdr.buf_mem -= buf->size; + gdr.buf_mem += buf->size = size; + gdr.buf_peak = gdr.buf_mem > gdr.buf_peak ? gdr.buf_mem : gdr.buf_peak; + } +} + +void buf_delete(uint *vbo, uint *vao) { + if(*vbo) { + glDeleteBuffers(1, vbo); + glDeleteVertexArrays(1, vao); + logd(LOG_GFX, STR_BUF_IUNLOADED, *vbo, *vao); + *vbo = 0; + *vao = 0; + } +} + +byte buf_unload(buf_t *buf) { + if(!(*(buf->vbo))) { + return 0; + } + glDeleteBuffers(1, buf->vbo); + glDeleteVertexArrays(1, buf->vao); + gdr.buf_mem -= buf->size; + logt(LOG_GFX, STR_BUF_UNLOADED, buf->size, buf->stride, *(buf->vbo), *(buf->vao)); + *(buf->vbo) = 0; + *(buf->vao) = 0; + return 1; +} + +void buf_remove(buf_t *buf) { + if(buf_unload(buf)) { + // tbl_pop(&gdr.buffers, buf); + gdr.buf_loaded -= 1; + } +} + +void buf_destroy(buf_t *buf) { + if(buf_unload(buf)) { + tbl_pop(&gdr.buffers, buf); + gdr.buf_loaded -= 1; + } +} + +int buf_clear() { + return tbl_clear_func(&gdr.buffers, (clear_func*)buf_remove); +} diff --git a/build.txt b/build.txt new file mode 100644 index 0000000..0e8a4ef --- /dev/null +++ b/build.txt @@ -0,0 +1 @@ +3295 \ No newline at end of file diff --git a/camera.h b/camera.h new file mode 100644 index 0000000..a7c77d6 --- /dev/null +++ b/camera.h @@ -0,0 +1,92 @@ + +void cam_calc_vec(camera_t *cam) { + VEC3_SET(cam->front, cos(glm_rad(cam->yaw)) * cos(glm_rad(cam->pitch)), sin(glm_rad(cam->pitch)), sin(glm_rad(cam->yaw)) * cos(glm_rad(cam->pitch))) + glm_vec3_normalize(cam->front); + glm_vec3_cross(cam->front, cam->world_up, cam->right); + glm_vec3_normalize(cam->right); + glm_vec3_cross(cam->right, cam->front, cam->up); + glm_vec3_normalize(cam->up); +} + +void cam_angle(camera_t *cam, float yaw, float pitch) { + cam->yaw = yaw; + cam->pitch = pitch; + cam_calc_vec(cam); +} + +void cam_angle_ent(camera_t *cam, entity_t *ent) { + cam->yaw = ent->yaw; + cam->pitch = ent->pitch; + cam_calc_vec(cam); +} + +void cam_pos(camera_t *cam, double x, double y, double z) { + cam->pos_x = x; + cam->pos_y = y; + cam->pos_z = z; +} + +void cam_pos_ent(camera_t *cam, entity_t *ent, double fraction) { + cam->pos_x = ent->last_x + (ent->pos_x - ent->last_x) * fraction; + cam->pos_y = (ent->last_y + (ent->pos_y - ent->last_y) * fraction) + ent->eye; + cam->pos_z = ent->last_z + (ent->pos_z - ent->last_z) * fraction; +} + +void cam_init(camera_t *cam, double x, double y, double z, float yaw, float pitch) { + VEC3_SET(cam->world_up, 0.0f, 1.0f, 0.0f) + cam_angle(cam, yaw, pitch); + cam_pos(cam, x, y, z); +} + +void cam_swing(camera_t *cam, float x, float y) { + // if(gdr.cam_fixed) + // return; + cam->yaw += x * sys.sensitivity * 0.1f; + cam->pitch += y * sys.sensitivity * 0.1f; + cam->yaw = fmodf(cam->yaw + 180.0f, 360.0f); + cam->yaw = ((cam->yaw < 0.0f) ? (cam->yaw + 360.0f) : cam->yaw) - 180.0f; + cam->pitch = CLAMP_VALUE(cam->pitch, -89.0f, 89.0f); + cam_calc_vec(cam); +} + +void cam_zoom(camera_t *cam, float zoom) { + // if(gdr.zoom_fixed) + // return; + cam->zoom += zoom; + cam->zoom = CLAMP_VALUE(cam->zoom, 0.0f, 1.0f); +} + +void cam_rzoom(camera_t *cam) { + cam->zoom = 0.0f; +} + +void cam_move_noclip(camera_t *cam, float delta, float speed, byte dir) { + // if(gdr.pos_fixed) + // return; + float velo = speed * delta; + vec3 pos; + VEC3_SET(pos, cam->pos_x, cam->pos_y, cam->pos_z) + switch(dir) { + case DIR_LEFT: + glm_vec3_muladds(cam->right, -velo, pos); + break; + case DIR_RIGHT: + glm_vec3_muladds(cam->right, velo, pos); + break; + case DIR_DOWN: + glm_vec3_muladds(cam->world_up, -velo, pos); + break; + case DIR_UP: + glm_vec3_muladds(cam->world_up, velo, pos); + break; + case DIR_BACKWARD: + glm_vec3_muladds(cam->front, -velo, pos); + break; + case DIR_FORWARD: + glm_vec3_muladds(cam->front, velo, pos); + break; + } + cam->pos_x = pos[0]; + cam->pos_y = pos[1]; + cam->pos_z = pos[2]; +} diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..c0c54e4 --- /dev/null +++ b/compile.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +BUILD_ID=1 +if [ -e build.txt ]; then + BUILD_ID=$(cat build.txt) + let BUILD_ID=BUILD_ID+1 +fi +MODIFIED=0 +if [ -e skcblitz ]; then + MODIFIED=$(date -r skcblitz +%s) +fi +for header in *.h *.c; do + if [ $(date -r "$header" +%s) -gt $MODIFIED ]; then + if gcc -rdynamic -g -DBUILD_DATE="\"$(date)\"" -DBUILD_SYS="\"$(uname -snrm)\"" -DBUILD_ID=$BUILD_ID -DBUILD_COMP="\"$(gcc --version | head -n 1)\"" -o skcblitz program.c -lm -lX11 -ldl -lasound -pthread; then + echo -n $BUILD_ID > build.txt + echo "Build $BUILD_ID" + break; + else + exit 1; + fi + fi +done +set -x +$@ diff --git a/console.h b/console.h new file mode 100644 index 0000000..8027908 --- /dev/null +++ b/console.h @@ -0,0 +1,453 @@ + +#define logcu(fmt, args ...) logu(LOG_CON, fmt, args) +#define logci(fmt, args ...) logi(LOG_CON, fmt, args) +#define logcw(fmt, args ...) logw(LOG_CON, fmt, args) +#define logce(fmt, args ...) loge(LOG_CON, fmt, args) + +int str_tokenize(char *arg, char **args, const char *str) { +// if(str[0] == 0) { +// return 0; +// } + int pos = 0; + int apos = 0; + int argc = 0; + int last = 0; + byte space = 1; + byte quote = 0; + byte escape = 0; + char c; + while(c = str[pos++]) { + if(escape) { + escape = 0; + switch(c) { + case '\\': + case ' ': + case '"': + arg[apos++] = c; + break; + default: + logce(STR_PARSER_ESC_UNKNOWN, pos, c); + return 0; + } + } + else if(c == '\\') { + space = 0; + escape = 1; + } + else if(c == '"') { + space = 0; + quote = !quote; + last = pos; + } + else if(c == ' ' && !quote) { + if(!space) { + args[argc++] = arg; + arg[apos++] = 0; + arg += apos; + apos = 0; + } + space = 1; + } + else { + space = 0; + arg[apos++] = c; + } + } + if(escape) { + logce(STR_PARSER_ESC_SHORT, pos); + return 0; + } + if(quote) { + logce(STR_PARSER_NO_QUOTE, last); + return 0; + } + if(!space) { + args[argc++] = arg; + arg[apos] = 0; + } + return argc; +} + +ccmd_t *con_get(const char *key) { + return (ccmd_t*)smap_get(&sys.ccmd_map, key); +} + +void con_exec(const char *line) { + char str[64]; + int argc; + char **argv; + cvar_t *cv; + ccmd_t *cmd; + if(!(argc = str_tokenize(sys.con_tok, sys.con_arg, line))) + return; + argv = sys.con_arg; + cv = cvar_get(argv[0]); + if(cv && argc == 1) { + cvar_format(cv, str); + logcu("%s = %s", cv->name, str); + return; + } + else if(cv && argc == 2) { + if(cv->readonly) { + logce(STR_CVAR_READONLY, cv->name); + } + else if(cvar_set(cv, argv[1], 0)) { + cvar_format(cv, str); + logcu("%s = %s", cv->name, str); + } + else { + logce(STR_CVAR_INVALID, cv->name, argv[1]); + } + return; + } + else if(cv) { + logce(STR_CVAR_MOREARGS, argc); + return; + } + cmd = con_get(argv[0]); + if(!cmd) { + logce(STR_CVAR_CCMD_UNKNOWN, argv[0]); + return; + } + if(--argc < cmd->min) { + logce(STR_CCMD_LESSARGS, argv[0], cmd->min, argc); + return; + } + else if(argc > cmd->max) { + logce(STR_CCMD_MOREARGS, argv[0], cmd->max, argc); + return; + } + logcu("# %s", line); + cmd->func(cmd, (const char **)&argv[1], argc); +} + +void con_reset() { + sys.log_len = sprintf(sys.con_buf, COL_NEON "*** " SYS_PROGRAM_FULL " ***"); + // for(int z = 0; z < sys.con_size; z++) { + // sys.con_msgs[z].message = NULL; + // } + // sys.con_pos = 0; +} + +ccmd_t *con_add(const char *name, const char *args, ccmd_func *func, ushort min, ushort max) { + ccmd_t *cmd = &sys.ccmds[sys.n_ccmds]; + cmd->func = func; + cmd->args = args; + cmd->min = min; + cmd->max = max; + cmd->id = sys.n_ccmds; + cmd->name = name; + sys.n_ccmds += 1; + return cmd; +} + +void con_fmt_cvar(cvar_t *cv, char *str) { + int pos; + if(cv->type == CVAR_ENUM) { + pos = sprintf(str, "%s "COL_DGRAY"["COL_GRAY"D "COL_CRIMSON"%s"COL_DGRAY"] ["COL_LGRAY, ((const char**)cv->aux_data)[cv->value], ((const char**)cv->aux_data)[cv->def]); + for(int z = 0; z < cv->max; z++) { + pos += sprintf(str+pos, z ? ", %s" : "%s", ((const char**)cv->aux_data)[z]); + } + pos += sprintf(str+pos, COL_DGRAY"]"); + } + else { + cvar_fmt[cv->type](str, cv->value); + pos = strlen(str); + pos += sprintf(str+pos, " "COL_DGRAY"["COL_GRAY"D "COL_CRIMSON); + cvar_fmt[cv->type](str+pos, cv->def); + pos += strlen(str+pos); + pos += sprintf(str+pos, COL_DGRAY"]"); + if(cv->type == CVAR_INT) + pos += sprintf(str+pos, " ["COL_LGRAY"%d .. %d"COL_DGRAY"]", cv->min, cv->max); + else if(cv->type == CVAR_FLOAT) + pos += sprintf(str+pos, " ["COL_LGRAY"%.3f .. %.3f"COL_DGRAY"]", cvar_itf(cv->min), cvar_itf(cv->max)); + } + logcu("%s "COL_NEON"%s "COL_DGRAY"[%s"COL_DGRAY"]"COL_GRAY" = "COL_WHITE"%s%s", cvar_types[cv->type], cv->name, cvar_categories[cv->category], str, cv->readonly ? " "COL_DGRAY"["COL_RED"RO"COL_DGRAY"]" : ""); +} + +void con_cmd_cvar(ccmd_t *cmd, const char **argv, int argc) { + cvar_t *cv; + char fmt[512]; + if(argc) { + for(int z = 0; z < argc; z++) { + cv = cvar_get(argv[z]); + if(!cv) { + logce(STR_CVAR_UNKNOWN, argv[z]); + continue; + } + con_fmt_cvar(cv, fmt); + } + } + else { + int ro = 0; + for(int z = 0; z < sys.n_cvars; z++) { + cv = &sys.cvars[z]; + con_fmt_cvar(cv, fmt); + ro += cv->readonly ? 1 : 0; + } + logcu(STR_CMD_CVARLIST, sys.n_cvars, ro); + } +} + +void con_fmt_ccmd(ccmd_t *cmd, char *str) { + logcu("%s "COL_NEON"%s "COL_DGRAY"["COL_LGRAY"%d .. %d"COL_DGRAY"]", cmd->name, cmd->args, cmd->min, cmd->max); +} + +void con_cmd_ccmd(ccmd_t *cmd, const char **argv, int argc) { + ccmd_t *ccmd; + char fmt[128]; + if(argc) { + for(int z = 0; z < argc; z++) { + ccmd = con_get(argv[z]); + if(!ccmd) { + logce(STR_CCMD_UNKNOWN, argv[z]); + continue; + } + con_fmt_ccmd(ccmd, fmt); + } + } + else { + for(int z = 0; z < sys.n_ccmds; z++) { + ccmd = &sys.ccmds[z]; + con_fmt_ccmd(ccmd, fmt); + } + logcu(STR_CMD_CCMDLIST, sys.n_ccmds); + } +} + +void con_cmd_memdump(ccmd_t *cmd, const char **argv, int argc) { + logcu("base : %12.3f MB (%5.2f %%), 1 block (%5.2f %%)", ((float)SYS_BASE) / 1024.0f / 1024.0f, ((float)SYS_BASE) / ((float)sys.mem_alloc) * 100.0f, 100.0f / ((float)sys.mem_blocks)); + for(int z = 0; z < MEM_CATS; z++) { + logcu("%-10s: %12.3f MB (%5.2f %%), %6lld block%c (%5.2f %%)", mem_types[z], ((float)sys.mem_pool_sizes[z]) / 1024.0f / 1024.0f, ((float)sys.mem_pool_sizes[z]) / ((float)sys.mem_alloc) * 100.0f, + sys.mem_pool_blocks[z], sys.mem_pool_blocks[z] == 1 ? ' ' : 's', ((float)sys.mem_pool_blocks[z]) / ((float)sys.mem_blocks) * 100.0f); + } + logcu("total : %12.3f MB (peak: %.3f MB), %lld blocks", ((float)sys.mem_alloc) / 1024.0f / 1024.0f, ((float)sys.mem_peak) / 1024.0f / 1024.0f, sys.mem_blocks); +} + +byte plr_queue(const char *dmxname, const char *drumname, const char **files, int count); +void plr_end(); + +void con_cmd_play(ccmd_t *cmd, const char **argv, int argc) { + const char *bank = NULL; + const char *drum = NULL; + int id = snd.mid_bank; + if(argc) { + if(!strcmp(argv[0], "-b")) { + if(argc < 2) { + logce(STR_CMD_ARGREQ, "-b"); + for(int z = 0; z < 32; z++) { + logce("#%02d -- %s", z, z < 31 ? snd_banknames[z] : "[custom]"); + } + return; + } + if(!str_parse_enum(argv[1], snd_banknames, 31, &id)) { + bank = argv[1]; + } + argv += 2; + argc -= 2; + } + else if(!strcmp(argv[0], "-B")) { + if(argc < 3) { + logce(STR_CMD_ARGREQS, "-B", 2); + return; + } + bank = argv[1]; + drum = argv[2]; + argv += 3; + argc -= 3; + } + if(!argc) { + logcu(STR_CMD_PLAY_BANKID, bank ? 31 : id); + return; + } + if(plr_queue(bank ? bank : snd_bankfiles[id], drum, argv, argc)) + logcu(STR_CMD_PLAY_QUEUED, argc); + } + else { + if(snd.mid_queued) { + logcu(STR_CMD_PLAY_PLAYING, snd.mid_queued); + for(int z = 0; z < snd.mid_queued; z++) { + if(z != (snd.mid_queued - 1)) + snd.mid_queue[z+1][-1] = 0; + logcu("%d. -- %s", z + 1, &(snd.mid_queue[z])[6]); + if(z != (snd.mid_queued - 1)) + snd.mid_queue[z+1][-1] = '\n'; + } + } + else { + logcu(STR_CMD_PLAY_STOPPED, NULL); + } + } +} + +void con_cmd_stop(ccmd_t *cmd, const char **argv, int argc) { + if(snd.mid_queued) + logcu(STR_CMD_PLAY_ENDED, NULL); + else + logcu(STR_CMD_PLAY_STOPPED, NULL); + plr_end(); +} + +#define theme_add(str) \ + style = (style_t*)tbl_push(&sys.themes);\ + style->name = str; + +#define theme_window(bdrtop, bdrbtm, top, btm, text) \ + style->wbrdr_top = bdrtop;\ + style->wbrdr_btm = bdrbtm;\ + style->win_top = top;\ + style->win_btm = btm;\ + style->text_win = text; + +#define theme_border(top, btm, size) \ + style->brdr_top = top;\ + style->brdr_btm = btm;\ + style->border = size; + +#define theme_background(top, btm) \ + style->bg_top = top;\ + style->bg_btm = btm; + +#define theme_base(top, btm, text, label) \ + style->fill_top = top;\ + style->fill_btm = btm;\ + style->text_base = text;\ + style->text_label = label; + +#define theme_field(top, btm, text) \ + style->field_top = top;\ + style->field_btm = btm;\ + style->text_field = text; + +#define theme_slider(handle) \ + style->slider_width = handle; + +#define theme_select(presstop, pressbtm, hovertop, hoverbtm, sel, cur) \ + style->press_top = presstop;\ + style->press_btm = pressbtm;\ + style->hover_top = hovertop;\ + style->hover_btm = hoverbtm;\ + style->select = sel;\ + style->cursor = cur; + +void con_cmd_themedump(ccmd_t *cmd, const char **argv, int argc) { + logcu("theme_add(STR_THEME_%s);", argc ? argv[0] : ""); + logcu("theme_window(0x%06x, 0x%06x, 0x%06x, 0x%06x, 0x%06x);", sys.style.wbrdr_top & 0xffffff, sys.style.wbrdr_btm & 0xffffff, sys.style.win_top & 0xffffff, sys.style.win_btm & 0xffffff, sys.style.text_win & 0xffffff); + logcu("theme_border(0x%06x, 0x%06x, %d);", sys.style.brdr_top & 0xffffff, sys.style.brdr_btm & 0xffffff, sys.style.border); + logcu("theme_background(0x%06x, 0x%06x);", sys.style.bg_top & 0xffffff, sys.style.bg_btm & 0xffffff); + logcu("theme_base(0x%06x, 0x%06x, 0x%06x, 0x%06x);", sys.style.fill_top & 0xffffff, sys.style.fill_btm & 0xffffff, sys.style.text_base & 0xffffff, sys.style.text_label & 0xffffff); + logcu("theme_field(0x%06x, 0x%06x, 0x%06x);", sys.style.field_top & 0xffffff, sys.style.field_btm & 0xffffff, sys.style.text_field & 0xffffff); + logcu("theme_slider(%d);", sys.style.slider_width); + logcu("theme_select(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x);", sys.style.press_top, sys.style.press_btm, sys.style.hover_top, sys.style.hover_btm, sys.style.select, sys.style.cursor); +} + +void con_reg() { + con_add("cvar", "[cvar ...]", con_cmd_cvar, 0, CCMD_UNLIMITED); + con_add("ccmd", "[ccmd ...]", con_cmd_ccmd, 0, CCMD_UNLIMITED); + con_add("memdump", "", con_cmd_memdump, 0, 0); + con_add("play", "[-b | -B ] [midi ...]", con_cmd_play, 0, CCMD_UNLIMITED); + con_add("stop", "", con_cmd_stop, 0, 0); + con_add("themedump", "[id]", con_cmd_themedump, 0, 1); +} + +void con_init() { + ccmd_t *cmd; + con_reg(); + smap_init(&sys.ccmd_map, 1, MEM_SYSTEM); + for(int z = 0; z < sys.n_ccmds; z++) { + cmd = &sys.ccmds[z]; + smap_put(&sys.ccmd_map, cmd->name, cmd); + } +} + +void con_end() { + smap_clear(&sys.ccmd_map); +} + +void con_resize(int size) { + for(int z = sys.hud_size; z < size; z++) { + sys.hud_msgs[z].message = NULL; + } + sys.hud_size = size; + if(sys.hud_pos >= sys.hud_size) + sys.hud_pos = 0; +} + +gui_t *gui_get(window_t *win, int id); +void gui_update_text(gui_t *elem); +void gui_text_update_cur(gui_t *elem, int offset, byte shift); + +void log_sys(const char *msg, int length, int offset) { + char c; + if(sys.con_timestamps ^ 1) { + length -= offset; + msg += offset; + offset = 0; + } + if((sys.log_len + length + 2) > CON_BUF) { + offset = (length + 2) > 1024 ? (length + 2) : 1024; + while(c = sys.con_buf[offset++]) { + if(c == '\n') + break; + } + offset -= c ? 0 : 1; + sys.log_len -= offset; + memmove(sys.con_buf, &sys.con_buf[offset], sys.log_len); + } + sys.con_buf[sys.log_len] = '\n'; + sys.log_len += 1; + memcpy(&sys.con_buf[sys.log_len], msg, length); + sys.log_len += length; + sys.con_buf[sys.log_len] = 0; + if(sys.console && sys.console->open) { + gui_t *elem = gui_get(sys.console, 5); + if(sys.con_autoscroll) + elem->sel_start = elem->sel_end = elem->sel_drag = sys.log_len; + gui_update_text(elem); + if(elem->sel_start >= 0) + gui_text_update_cur(elem, elem->sel_start, 1); + } +} + +void log_hud(window_t *win, const char *fmt, ...) { + conmsg_t *con; + va_list ap; + va_start(ap, fmt); + int length = vsnprintf(sys.log_buf, LOG_BUF, fmt, ap); + if((sys.hud_len + length + 2) > HUD_BUF) { + char c; + int offset = (length + 2) > 1024 ? (length + 2) : 1024; + while(c = sys.hud_buf[offset++]) { + if(c == '\n') + break; + } + offset -= c ? 0 : 1; + sys.hud_len -= offset; + memmove(sys.hud_buf, &sys.hud_buf[offset], sys.hud_len); + for(int z = 0; z < sys.hud_size; z++) { + con = &sys.hud_msgs[z]; + if(con->message) { + if((con->offset -= offset) < 0) + con->message = NULL; + else + con->message -= offset; + } + } + } + sys.hud_buf[sys.hud_len] = '\n'; + sys.hud_len += 1; + memcpy(&sys.hud_buf[sys.hud_len], sys.log_buf, length); + if(sys.hud_size) { + con = &sys.hud_msgs[sys.hud_pos]; + con->message = &sys.hud_buf[sys.hud_len]; + con->offset = sys.hud_len; + con->length = length; + con->time = sys.tmr_current; + con->window = win; + if(++(sys.hud_pos) >= sys.hud_size) + sys.hud_pos = 0; + } + sys.hud_len += length; + sys.hud_buf[sys.hud_len] = 0; + va_end(ap); +} diff --git a/cvar.h b/cvar.h new file mode 100644 index 0000000..7aad1d4 --- /dev/null +++ b/cvar.h @@ -0,0 +1,681 @@ + +int cvar_fti(float f) { + return *((int*)&f); +} + +float cvar_itf(int i) { + return *((float*)&i); +} + +void cvar_fnc_void(cvar_t *cv, int value) { + ; +} + +void cvar_fnc_bool(cvar_t *cv, int value) { + *((byte*)cv->data) = (byte)value; +} + +void cvar_fnc_int(cvar_t *cv, int value) { + *((int*)cv->data) = value; +} + +void cvar_fnc_float(cvar_t *cv, int value) { + *((float*)cv->data) = cvar_itf(value); +} + +void cvar_fnc_double(cvar_t *cv, int value) { + *((double*)cv->data) = (double)cvar_itf(value); +} + +void cvar_fnc_color(cvar_t *cv, int value) { + *((uint*)cv->data) = (uint)value; +} + +void cvar_fnc_fcolor(cvar_t *cv, int value) { + ((float*)cv->data)[0] = ((float)((uint)((value >> 16) & 0xff))) * 255.0f; + ((float*)cv->data)[1] = ((float)((uint)((value >> 8) & 0xff))) * 255.0f; + ((float*)cv->data)[2] = ((float)((uint)(value & 0xff))) * 255.0f; +} + +void cvar_fnc_scolor(cvar_t *cv, int value) { + *((uint*)cv->data) = ((uint)value) | 0xff000000; +} + +void cvar_fnc_ccolor(cvar_t *cv, int value) { + *((uint*)cv->data) = ((uint)value) | ((*((uint*)cv->data)) & 0xff000000); +} + +void cvar_fnc_acolor(cvar_t *cv, int value) { + *((uint*)cv->data) = ((uint)(float)(cvar_itf(value) * 255.0f)) | ((*((uint*)cv->data)) & 0x00ffffff); +} + +void cvar_fnc_bind(cvar_t *cv, int value) { + ((bind_t*)cv->data)->key = value; +} + +cvar_t *cvar_add(const char *name, cvar_func *func, void *data, void *aux_data, int def, int min, int max, byte type, byte category, byte readonly, byte startup) { + cvar_t *cv = &sys.cvars[sys.n_cvars]; + cv->func = func; + cv->data = data; + cv->aux_data = aux_data; + cv->value = cv->def = def; + cv->min = min; + cv->max = max; + cv->id = sys.n_cvars; + cv->readonly = readonly; + cv->startup = startup; + cv->type = type; + cv->category = category; + cv->name = name; + sys.n_cvars += 1; + return cv; +} + +cvar_t *cvar_add_bool(const char *name, byte category, byte *data, byte def) { + cvar_t *cv = cvar_add(name, cvar_fnc_bool, data, NULL, (int)def, 0, 0, CVAR_BOOL, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_sbool(const char *name, byte category, cvar_func *func, byte *data, byte def, byte startup) { + cvar_t *cv = cvar_add(name, func, data, NULL, (int)def, 0, 0, CVAR_BOOL, category, 0, startup); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_wbool(const char *name, byte category, cvar_func *func, byte def) { + return cvar_add(name, func, NULL, NULL, (int)def, 0, 0, CVAR_BOOL, category, 0, 0); +} + +cvar_t *cvar_add_int(const char *name, byte category, int *data, int def, int min, int max) { + cvar_t *cv = cvar_add(name, cvar_fnc_int, data, NULL, def, min, max, CVAR_INT, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_sint(const char *name, byte category, cvar_func *func, int *data, int def, int min, int max, byte startup) { + cvar_t *cv = cvar_add(name, func, data, NULL, def, min, max, CVAR_INT, category, 0, startup); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_byte(const char *name, byte category, byte *data, byte def, byte min, byte max) { + cvar_t *cv = cvar_add(name, cvar_fnc_bool, data, NULL, (int)def, (int)min, (int)max, CVAR_INT, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_rint(const char *name, byte category, int def, int min, int max) { + return cvar_add(name, cvar_fnc_void, NULL, NULL, def, min, max, CVAR_INT, category, 1, 0); +} + +cvar_t *cvar_add_wint(const char *name, byte category, cvar_func *func, int def, int min, int max) { + return cvar_add(name, func, NULL, NULL, def, min, max, CVAR_INT, category, 0, 0); +} + +cvar_t *cvar_add_float(const char *name, byte category, float *data, float def, float min, float max) { + cvar_t *cv = cvar_add(name, cvar_fnc_float, data, NULL, cvar_fti(def), cvar_fti(min), cvar_fti(max), CVAR_FLOAT, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_sfloat(const char *name, byte category, cvar_func *func, float *data, float def, float min, float max, byte startup) { + cvar_t *cv = cvar_add(name, func, data, NULL, cvar_fti(def), cvar_fti(min), cvar_fti(max), CVAR_FLOAT, category, 0, startup); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_double(const char *name, byte category, double *data, float def, float min, float max) { + cvar_t *cv = cvar_add(name, cvar_fnc_double, data, NULL, cvar_fti(def), cvar_fti(min), cvar_fti(max), CVAR_FLOAT, category, 0, 1); + if(data) + *data = (double)def; + return cv; +} + +cvar_t *cvar_add_benum(const char *name, byte category, byte *data, const char **names, byte def, int states) { + cvar_t *cv = cvar_add(name, cvar_fnc_bool, data, names, (int)def, 0, states, CVAR_ENUM, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_sbenum(const char *name, byte category, cvar_func *func, byte *data, const char **names, byte def, int states, byte startup) { + cvar_t *cv = cvar_add(name, func, data, names, (int)def, 0, states, CVAR_ENUM, category, 0, startup); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_ienum(const char *name, byte category, int *data, const char **names, int def, int states) { + cvar_t *cv = cvar_add(name, cvar_fnc_int, data, names, def, 0, states, CVAR_ENUM, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_color(const char *name, byte category, uint *data, uint def) { + cvar_t *cv = cvar_add(name, cvar_fnc_scolor, data, NULL, (int)def, 0, 0, CVAR_COLOR, category, 0, 1); + if(data) + *data = def | 0xff000000; + return cv; +} + +cvar_t *cvar_add_scolor(const char *name, byte category, cvar_func *func, uint *data, uint def, byte startup) { + cvar_t *cv = cvar_add(name, func, data, NULL, (int)def, 0, 0, CVAR_COLOR, category, 0, startup); + if(data) + *data = def | 0xff000000; + return cv; +} + +cvar_t *cvar_add_acolor(const char *name, byte category, uint *data, uint def) { + cvar_t *cv = cvar_add(name, cvar_fnc_color, data, NULL, (int)def, 0, 0, CVAR_COLOR_ALPHA, category, 0, 1); + if(data) + *data = def; + return cv; +} + +cvar_t *cvar_add_key(const char *name, byte bind, int def) { + cvar_t *cv = cvar_add(name, cvar_fnc_bind, &sys.binds[bind], NULL, def, 0, 0, CVAR_KEY, CVAR_BIND, 0, 1); + sys.binds[bind].key = def; + return cv; +} + +void cvar_fmt_bool(char *str, int value) { + sprintf(str, value ? "true" : "false"); +} + +void cvar_fmt_int(char *str, int value) { + sprintf(str, "%d", value); +} + +void cvar_fmt_float(char *str, int value) { + sprintf(str, "%.3f", cvar_itf(value)); +} + +void cvar_fmt_color(char *str, int value) { + sprintf(str, "%06x", (uint)value); +} + +void cvar_fmt_acolor(char *str, int value) { + sprintf(str, "%08x", (uint)value); +} + +void cvar_fmt_key(char *str, int value) { + if(value == 0) { + sprintf(str, "none"); + } + else if(value >= -SCROLL_RIGHT && value <= -MOUSE_LEFT) { + sprintf(str, key_names[KEY_MOUSE_OFFSET - (value + MOUSE_LEFT)]); + } + else if(value >= KEYSYM_0 && value <= KEYSYM_9) { + sprintf(str, "%d", value - KEYSYM_0); + } + else if(value >= KEYSYM_A && value <= KEYSYM_Z) { + sprintf(str, "%c", 'a' + (char)(value - KEYSYM_A)); + } + else if(value >= KEYSYM_F1 && value <= KEYSYM_F12) { + sprintf(str, "f%d", 1 + value - KEYSYM_F1); + } + else if(value >= KEYSYM_KP_0 && value <= KEYSYM_KP_9) { + sprintf(str, "kp%d", value - KEYSYM_KP_0); + } + else if(value >= KEYSYM_FIRST && value <= KEYSYM_LAST) { + sprintf(str, key_names[value - KEYSYM_FIRST]); + } + else { + sprintf(str, "#%d", value); + } +} + +static const cvar_fmt_func cvar_fmt[] = {cvar_fmt_bool, cvar_fmt_int, cvar_fmt_float, NULL, cvar_fmt_color, cvar_fmt_acolor, cvar_fmt_key}; + +byte cvar_parse_bool(const char *str, int *value) { + if(str_eq_lower("1", str) || str_eq_lower("true", str) || str_eq_lower("on", str) || str_eq_lower("yes", str) || str_eq_lower("y", str)) { + *value = 1; + return 1; + } + else if(str_eq_lower("0", str) || str_eq_lower("false", str) || str_eq_lower("off", str) || str_eq_lower("no", str) || str_eq_lower("n", str)) { + *value = 0; + return 1; + } + return 0; +} + +byte cvar_parse_int(const char *str, int *value) { + return str_parse_int(str, value, 0); +} + +byte cvar_parse_float(const char *str, int *value) { + errno = 0; + float f = strtof(str, NULL); + if(errno) + return 0; + *value = cvar_fti(f); + return 1; +} + +byte cvar_parse_color(const char *str, int *value) { + int c; + if((!str_parse_int(str, &c, -16)) || (c & 0xff000000)) + return 0; + *value = c; + return 1; +} + +byte cvar_parse_acolor(const char *str, int *value) { + return str_parse_int(str, value, -16); +} + +byte cvar_parse_key(const char *str, int *value) { + int comp; + int max = 0; + int best = 0; + if(str[0] >= '0' && str[0] <= '9' && !(str[1])) { + *value = (str[0] - '0') + KEYSYM_0; + return 1; + } + else if(str[0] >= 'a' && str[0] <= 'z' && !(str[1])) { + *value = (str[0] - 'a') + KEYSYM_A; + return 1; + } + else if(str[0] >= 'A' && str[0] <= 'Z' && !(str[1])) { + *value = (str[0] - 'A') + KEYSYM_A; + return 1; + } + else if((str[0] == '#') && str[1] && str_parse_int(str+1, value, 10)) { + return 1; + } + else if((str_lower(str[0]) == 'f') && str[1] && str_parse_int(str+1, &comp, 10) && (comp >= 1) && (comp <= 25)) { + *value = (comp - 1) + KEYSYM_F1; + return 1; + } + else if((str_lower(str[0]) == 'k') && (str_lower(str[1]) == 'p') && (str[2] >= '0') && (str[2] <= '9') && !(str[3])) { + *value = (str[2] - '0') + KEYSYM_KP_0; + return 1; + } + else if(str_eq_lower("none", str)) { + *value = 0; + return 1; + } + for(int z = 0; z < KEY_MOUSE_OFFSET + SCROLL_RIGHT; z++) { + if((comp = str_cmp_lower(key_names[z], str)) > best) { + max = comp; + best = z; + } + } + if(max) { + *value = (best >= KEY_MOUSE_OFFSET) ? -(MOUSE_LEFT + best - KEY_MOUSE_OFFSET) : (KEYSYM_FIRST + best); + return 1; + } + return 0; +} + +static const cvar_parse_func cvar_parse[] = {cvar_parse_bool, cvar_parse_int, cvar_parse_float, NULL, cvar_parse_color, cvar_parse_acolor, cvar_parse_key}; + +cvar_t *cvar_get(const char *key) { + return (cvar_t*)smap_get(&sys.cvar_map, key); +} + +byte cvar_set(cvar_t *cv, const char *str, byte startup) { + if((cv->type == CVAR_ENUM) ? str_parse_enum(str, (const char**)cv->aux_data, cv->max, &cv->value) : cvar_parse[cv->type](str, &cv->value)) { + if(cv->type == CVAR_INT) + cv->value = CLAMP_VALUE(cv->value, cv->min, cv->max); + else if(cv->type == CVAR_FLOAT) + cv->value = cvar_fti(CLAMP_VALUE(cvar_itf(cv->value), cvar_itf(cv->min), cvar_itf(cv->max))); + if((!startup) || cv->startup) + cv->func(cv, cv->value); + sys.cfg_dirty |= startup ^ 1; + return 1; + } + else { + sys.cfg_dirty |= startup; + return 0; + } +} + +void cvar_format(cvar_t *cv, char *str) { + if(cv->type == CVAR_ENUM) { + sprintf(str, "%s", ((const char**)cv->aux_data)[cv->value]); + } + else { + cvar_fmt[cv->type](str, cv->value); + } +} + +byte cvar_byte(const char *name) { + return (byte)cvar_get(name)->value; +} + +int cvar_int(const char *name) { + return cvar_get(name)->value; +} + +float cvar_float(const char *name) { + return cvar_itf(cvar_get(name)->value); +} + +uint cvar_color(const char *name) { + return (uint)cvar_get(name)->value; +} + +int cvar_key(byte bind) { + return sys.cvars[bind].value; +} + +int cvar_defkey(byte bind) { + return sys.cvars[bind].def; +} + +void cvar_sdef(cvar_t *cv) { + cv->value = cv->def; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_setdef(const char *name) { + cvar_sdef(cvar_get(name)); +} + +void cvar_str(const char *name, char *str) { + cvar_format(cvar_get(name), str); +} + +void cvar_sbool(cvar_t *cv, byte value) { + cv->value = (int)value; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_setbool(const char *name, byte value) { + cvar_sbool(cvar_get(name), value); +} + +void cvar_toggle(const char *name) { + cvar_t *cv = cvar_get(name); + cv->value ^= 1; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_sint(cvar_t *cv, int value) { + cv->value = value; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_setint(const char *name, int value) { + cvar_sint(cvar_get(name), value); +} + +void cvar_sfloat(cvar_t *cv, float value) { + cv->value = cvar_fti(value); + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_setfloat(const char *name, float value) { + cvar_sfloat(cvar_get(name), value); +} + +void cvar_scolor(cvar_t *cv, uint value) { + cv->value = (int)value; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +void cvar_skey(byte bind, int value) { + cvar_t *cv = &sys.cvars[bind]; + cv->value = (int)value; + cv->func(cv, cv->value); + sys.cfg_dirty = 1; +} + +byte cvar_sstr(cvar_t *cv, const char *value) { + return cvar_set(cv, value, 0); +} + +// void win_full(byte full); + +// void cvar_fnc_full(cvar_t *cv, int value) { + // win_full(value); +// } + +void win_sync(int sync); + +void cvar_fnc_sync(cvar_t *cv, int value) { + win_sync(value); +} + +void cvar_fnc_dlight(cvar_t *cv, int value) { + light_setup(value); +} + +void tick_target(float tps); + +void cvar_fnc_ticktarget(cvar_t *cv, int value) { + tick_target(cvar_itf(value)); +} + +void cvar_fnc_tex(cvar_t *cv, int value) { + cvar_fnc_bool(cv, value); + tex_config(); +} + +void cvar_fnc_ftex(cvar_t *cv, int value) { + cvar_fnc_float(cv, value); + tex_config(); +} + +void con_resize(int size); + +void cvar_fnc_consize(cvar_t *cv, int value) { + con_resize(value); +} + +void plr_debug(byte debug); +void plr_volume(byte channel, ushort volume); + +void cvar_fnc_middebug(cvar_t *cv, int value) { + plr_debug((byte)value); +} + +void cvar_fnc_volume(cvar_t *cv, int value) { + plr_volume((byte)(int)(cv->id - cvar_get(snd_cvar_volume[0])->id), (ushort)(cvar_itf(value) * 32767.0f)); +} + +void cvar_fnc_sound(cvar_t *cv, int value) { +} + +void gfx_back_color(uint color); + +void cvar_fnc_clight(cvar_t *cv, int value) { + gfx_back_color(0xff000000 | (uint)value); +} + +void cvar_reg() { +#include "vars.h" +} + +void theme_reg() { + style_t *style; + +#define theme_add(str) \ + style = (style_t*)tbl_push(&sys.themes);\ + style->name = str; + +#define theme_window(bdrtop, bdrbtm, top, btm, text) \ + style->wbrdr_top = bdrtop;\ + style->wbrdr_btm = bdrbtm;\ + style->win_top = top;\ + style->win_btm = btm;\ + style->text_win = text; + +#define theme_border(top, btm, size) \ + style->brdr_top = top;\ + style->brdr_btm = btm;\ + style->border = size; + +#define theme_background(top, btm) \ + style->bg_top = top;\ + style->bg_btm = btm; + +#define theme_base(top, btm, text, label) \ + style->fill_top = top;\ + style->fill_btm = btm;\ + style->text_base = text;\ + style->text_label = label; + +#define theme_field(top, btm, text) \ + style->field_top = top;\ + style->field_btm = btm;\ + style->text_field = text; + +#define theme_slider(handle) \ + style->slider_width = handle; + +#define theme_select(presstop, pressbtm, hovertop, hoverbtm, sel, cur) \ + style->press_top = presstop;\ + style->press_btm = pressbtm;\ + style->hover_top = hovertop;\ + style->hover_btm = hoverbtm;\ + style->select = sel;\ + style->cursor = cur; + +#include "themes.h" + +#undef theme_add +#undef theme_window +#undef theme_border +#undef theme_background +#undef theme_base +#undef theme_field +#undef theme_slider +#undef theme_select + + sys.style_def = style; +} + +byte cvar_load() { + char str[64]; + char *lines; + char *line = NULL; + cvar_t *cv; + int pos; + if(!file_sread(&lines, sys.cfg_file)) { + return 0; + } + while(line = strtok(line ? NULL : lines, "\n")) { + for(pos = 0; line[pos]; pos++) { + if(line[pos] == '=') + break; + } + if(line[pos]) { + line[pos++] = 0; + if(!(cv = cvar_get(line))) { + loge(LOG_SYS, STR_CVAR_UNKNOWN, line); + continue; + } + if(sys.log_set != 0xff && cv->data == &sys.log_level) { + if(str_parse_enum(line+pos, (const char**)cv->aux_data, cv->max, &cv->value)) + sys.log_set = (byte)cv->value; + else + loge(LOG_SYS, STR_CVAR_INVALID, cv->name, line+pos); + cv->value = (int)sys.log_level; + continue; + } + if(cvar_set(cv, line+pos, 1)) { + cvar_format(cv, str); + logt(LOG_SYS, "%s = %s", cv->name, str); + } + else { + loge(LOG_SYS, STR_CVAR_INVALID, cv->name, line+pos); + } + } + } + mem_free(lines); + return 1; +} + +byte cvar_save() { + char str[64]; + cvar_t *cv; + FILE *fd; + int err; + if(!(fd = fopen(sys.cfg_file, "wb"))) { + err = errno; + loge(LOG_IO, STR_CVAR_EWOPEN, sys.cfg_file, strerror(err), err); + return 0; + } + for(int z = 0; z < sys.n_cvars; z++) { + cv = &sys.cvars[z]; + if(sys.log_set != 0xff && cv->data == &sys.log_level) + sprintf(str, "%s", ((const char**)cv->aux_data)[sys.log_set]); + else + cvar_format(cv, str); + fprintf(fd, "%s=%s\n", cv->name, str); + } + fclose(fd); + return 1; +} + +void cvar_init() { + cvar_t *cv; + tbl_init(&sys.themes, 32, 1, sizeof(style_t), MEM_SYSTEM); + theme_reg(); + cvar_reg(); + smap_init(&sys.cvar_map, 1, MEM_SYSTEM); + for(int z = 0; z < sys.n_cvars; z++) { + cv = &sys.cvars[z]; + smap_put(&sys.cvar_map, cv->name, cv); + } + cvar_load(); +} + +void cvar_end() { + cvar_save(); + smap_clear(&sys.cvar_map); + tbl_clear(&sys.themes); +} + +const char *cvar_getbind(byte bind) { + return STR_BIND_QUIT + (LOCALE_ELEM * bind); +} + +const char *cvar_getkey(int key, char *buf) { + if(key == 0) { + return "---"; + } + else if(key >= -SCROLL_RIGHT && key <= -MOUSE_LEFT) { + return STR_KEY_MOUSE_LEFT + (LOCALE_ELEM * -(key + MOUSE_LEFT)); + } + else if(key >= KEYSYM_0 && key <= KEYSYM_9) { + sprintf(buf, "<%d>", key - KEYSYM_0); + return buf; + } + else if(key >= KEYSYM_A && key <= KEYSYM_Z) { + sprintf(buf, "<%c>", 'A' + (char)(key - KEYSYM_A)); + return buf; + } + else if(key >= KEYSYM_F1 && key <= KEYSYM_F12) { + sprintf(buf, "F%d", 1 + key - KEYSYM_F1); + return buf; + } + else if(key >= KEYSYM_KP_0 && key <= KEYSYM_KP_9) { + sprintf(buf, "#%d", key - KEYSYM_KP_0); + return buf; + } + else if(key >= KEYSYM_FIRST && key <= KEYSYM_LAST) { + return STR_KEY_SPACE + (LOCALE_ELEM * (key - KEYSYM_FIRST)); + } + else { + sprintf(buf, "[%d]", key); + return buf; + } +} diff --git a/dmx.h b/dmx.h new file mode 100644 index 0000000..3d21275 --- /dev/null +++ b/dmx.h @@ -0,0 +1,900 @@ + +// #ifndef DMX_STDMEM +#define DMX_MALLOC(s) mem_alloc(s, MEM_FILE) +#define DMX_FREE(p) mem_free(p) +// #else +// #define DMX_MALLOC(s) malloc(s) +// #define DMX_FREE(p) free(p) +// #endif +// #ifndef DMX_LOG +#define DMX_LOG(s, a...) loge(LOG_IO, s, a) +// #endif + +uint bnk_read_mhbank(byte **data, bank_instr **instr, const char *filename, uint minsize, uint maxsize, const char *hdr, uint hdrsize) { + int err; + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) { + err = errno; + DMX_LOG(STR_BNK_OPNERR, filename, strerror(err), err); + return 0; + } + if(fseek(fd, 0L, SEEK_END)) { + err = errno; + DMX_LOG(STR_BNK_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + long size = ftell(fd); + if(size < 0L) { + err = errno; + DMX_LOG(STR_BNK_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + if(fseek(fd, 0L, SEEK_SET)) { + err = errno; + DMX_LOG(STR_BNK_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + if(size < (hdrsize+minsize)) { + DMX_LOG(STR_BNK_ESHORT, filename); + fclose(fd); + return 0; + } + if(hdrsize > 0) { + byte header[hdrsize]; + if(fread(header, 1, hdrsize, fd) != hdrsize) { + err = feof(fd); + DMX_LOG(err == 0 ? STR_BNK_EIO : STR_BNK_EEOF, filename); + fclose(fd); + return 0; + } + if(memcmp(header, hdr, hdrsize) != 0) { + DMX_LOG(STR_BNK_EMFHDR, filename); + fclose(fd); + return 0; + } + size -= hdrsize; + } + size = ((maxsize > 0) && (size > maxsize)) ? maxsize : size; + *data = DMX_MALLOC(size); + if(fread(*data, 1, size, fd) != size) { + err = feof(fd); + DMX_LOG(err == 0 ? STR_BNK_EIO : STR_BNK_EEOF, filename); + fclose(fd); + DMX_FREE(*data); + return 0; + } + fclose(fd); + *instr = DMX_MALLOC(sizeof(bank_instr)*256); + return size; +} + +uint bnk_read_mbank(byte **data, bank_instr **instr, const char *filename, uint minsize, uint maxsize) { + return bnk_read_mhbank(data, instr, filename, minsize, maxsize, NULL, 0); +} + +uint bnk_read_hbank(byte **data, bank_instr **instr, const char *filename, uint size, const char *hdr, uint hdrsize) { + return bnk_read_mhbank(data, instr, filename, size, size, hdr, hdrsize); +} + +uint bnk_read_sbank(byte **data, bank_instr **instr, const char *filename, uint size) { + return bnk_read_mhbank(data, instr, filename, size, size, NULL, 0); +} + +ushort dmx_read_uint16(byte *data) { + ushort value = 0; + for(int h = 0; h < 2; h++) { + value |= ((ushort)data[1-h]); + value <<= h < 1 ? 8 : 0; + } + return value; +} + +ushort dmx_read_uint16be(byte *data) { + ushort value = 0; + for(int h = 0; h < 2; h++) { + value |= ((ushort)data[h]); + value <<= h < 1 ? 8 : 0; + } + return value; +} + +void dmx_read_instr(bank_instr *instr, byte *data, byte drum) { + instr->fixed = ((data[0] & 0x01) > 0) | drum; + instr->op = ((data[0] & 0x04) > 0) ? b_op22 : b_op2; + instr->channels[0].detune = 0; + instr->channels[1].detune = ((short)data[2]) - 128; + instr->percnum = data[3] & 0x7f; + data += 4; + for(int ch = 0; ch < 2; ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op*7+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op*7+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op*7+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op*7+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op*7+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op*7+1] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op*7+1] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op*7+2] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op*7+2] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op*7+3] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op*7+4] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op*7+5] & 0x3f; + } + instr->channels[ch].feedback = (data[6] >> 1) & 0x07; + instr->channels[ch].am = data[6] & 0x01; + instr->channels[ch].offset = ((short)dmx_read_uint16(data+14)) + 12; + data += 16; + } +} + +bank_instr *dmx_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_hbank(&data, &instr, filename, 175*68, "#OPL_II#", 8); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + for(int i = 0; i < 175; i++) { + dmx_read_instr(&instr[(i < 128) ? i : (i + 35)], data, i >= 128); + data += 36; + } + for(int i = 0; i < 175; i++) { + bank_instr *ins = &instr[(i < 128) ? i : (i + 35)]; + memcpy(ins->name, data, 32); + ins->name[31] = 0; + data += 32; + } + DMX_FREE(odata); + return instr; +} + +void tmb_read_instr(bank_instr *instr, byte *data, byte drum) { + instr->name[0] = 0; + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->percnum = drum ? (data[11] & 0x7f) : 0; + instr->channels[0].offset = drum ? 0 : ((char)data[11]); + instr->channels[0].detune = 0; +} + +bank_instr *tmb_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_sbank(&data, &instr, filename, 256*13); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 256; i++) { + tmb_read_instr(&instr[i], data, i >= 128); + data += 13; + } + DMX_FREE(odata); + return instr; +} + +void ibk_read_instr(bank_instr *instr, byte *data, byte drum) { + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->percnum = drum ? (data[13] & 0x7f) : 0; + instr->channels[0].offset = drum ? 0 : ((char)data[13]); + instr->channels[0].detune = 0; +} + +bank_instr *ibk_read_bank(const char *filename, byte drum) { + byte *data; + bank_instr *instr; + uint size = bnk_read_hbank(&data, &instr, filename, 128*25, "IBK\x1a", 4); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + ibk_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 16; + } + for(int i = 0; i < 128; i++) { + bank_instr *ins = &instr[i + (drum ? 128 : 0)]; + memcpy(ins->name, data, 8); + ins->name[8] = 0; + data += 9; + } + DMX_FREE(odata); + return instr; +} + +void sb_read_instr(bank_instr *instr, byte *data, byte drum) { + for(int h = 0; h < 28; h++) { + instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h]; + } + instr->name[28] = 0; + instr->percnum = data[35] & 0x7f; + instr->fixed = drum; + instr->op = b_op2; + data += 36; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[0].ops[op].mult = data[op+0] & 0x0f; + instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[0].ops[op].decay = data[op+4] & 0x0f; + instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[0].ops[op].release = data[op+6] & 0x0f; + instr->channels[0].ops[op].waveform = data[op+8] & 0x07; + instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[0].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[0].feedback = (data[10] >> 1) & 0x07; + instr->channels[0].am = data[10] & 0x01; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; +} + +bank_instr *sb_read_bank(const char *filename, byte drum) { + byte *data; + bank_instr *instr; + uint size = bnk_read_sbank(&data, &instr, filename, 128*52); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + sb_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 52; + } + DMX_FREE(odata); + return instr; +} + +void sb3_read_instr(bank_instr *instr, byte *data, byte drum) { + for(int h = 0; h < 28; h++) { + instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h]; + } + instr->name[28] = 0; + instr->percnum = data[35] & 0x7f; + instr->fixed = drum; + instr->op = (data[0] == '4') ? b_op4 : b_op2; + data += 36; + for(int ch = 0; ch < 2; ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op+4] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op+4] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op+6] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op+6] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op+8] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op+2] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op+2] & 0x3f; + } + instr->channels[ch].feedback = (data[10] >> 1) & 0x07; + instr->channels[ch].am = data[10] & 0x01; + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } +} + +bank_instr *sb3_read_bank(const char *filename, byte drum) { + byte *data; + bank_instr *instr; + uint size = bnk_read_sbank(&data, &instr, filename, 128*60); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 128; i++) { + instr[i + (drum ? 0 : 128)].op = b_op0; + instr[i + (drum ? 0 : 128)].name[0] = 0; + } + for(int i = 0; i < 128; i++) { + sb3_read_instr(&instr[i + (drum ? 128 : 0)], data, drum); + data += 60; + } + DMX_FREE(odata); + return instr; +} + +void op3_read_instr(bank_instr *instr, byte *data, byte drum) { + instr->percnum = data[1] & 0x7f; + instr->fixed = drum; + instr->op = ((data[0] & 0x01) > 0) ? b_op4 : b_op2; + data += 2; + for(int ch = 0; ch < 2; ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f; + } + instr->channels[ch].feedback = (data[5] >> 1) & 0x07; + instr->channels[ch].am = data[5] & 0x01; + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } +} + +bank_instr *op3_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mhbank(&data, &instr, filename, 16, 256*24+16, "Junglevision Patch File\x1a", 24); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + data += 8; + ushort nmelo = dmx_read_uint16(&data[0]); + ushort ndrum = dmx_read_uint16(&data[2]); + ushort omelo = dmx_read_uint16(&data[4]); + ushort odrum = dmx_read_uint16(&data[6]); + data += 8; + if((((nmelo+ndrum)*24+16) > size) || ((omelo+nmelo) > 128) || ((odrum+ndrum) > 128)) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + for(int i = 0; i < nmelo; i++) { + op3_read_instr(&instr[i + omelo], data, 0); + data += 24; + } + for(int i = 0; i < ndrum; i++) { + op3_read_instr(&instr[i + odrum + 128], data, 1); + data += 24; + } + DMX_FREE(odata); + return instr; +} + +void ad_read_instr(bank_instr *instr, byte *data, byte drum, ushort isize) { + if(isize < 12) { + return; + } + instr->percnum = data[0] & 0x7f; + instr->fixed = drum; + instr->op = (isize >= 23) ? b_op4 : b_op2; + data += 1; + instr->channels[0].feedback = instr->channels[1].feedback = (data[5] >> 1) & 0x07; + instr->channels[0].am = data[5] & 0x01; + instr->channels[1].am = (data[5] & 0x80) > 0; + for(int ch = 0; ch < ((isize >= 23) ? 2 : 1); ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f; + instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f; + instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07; + instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f; + } + instr->channels[ch].offset = 0; + instr->channels[ch].detune = 0; + data += 11; + } +} + +bank_instr *ad_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mbank(&data, &instr, filename, 2, 256*20+2); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + for(int i = 0;; i++, data += 6) { + if((i*6) >= size) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + byte prog = data[0]; + if(prog == 0xff) { + break; + } + if(prog >= 128) { + continue; + } + byte bank = data[1]; + if((bank != 0) && (bank != 127)) { + continue; + } + ushort offs = dmx_read_uint16(data+2); + ushort isize; + if(((offs+2) > size) || ((offs+(isize = dmx_read_uint16(odata+offs))) > size) || (isize < 2)) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + ad_read_instr(&instr[prog + ((bank == 127) ? 128 : 0)], odata+(offs+2), bank == 127, isize-2); + } + DMX_FREE(odata); + return instr; +} + +void bk_read_instr(bank_instr *instr, byte *data, byte drum) { + instr->fixed = drum; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = data[op*13+9] & 0x01; + instr->channels[0].ops[op].vibrato = data[op*13+10] & 0x01; + instr->channels[0].ops[op].sustaining = data[op*13+5] & 0x01; + instr->channels[0].ops[op].ksr = data[op*13+11] & 0x01; + instr->channels[0].ops[op].mult = data[op*13+1] & 0x0f; + instr->channels[0].ops[op].attack = data[op*13+3] & 0x0f; + instr->channels[0].ops[op].decay = data[op*13+6] & 0x0f; + instr->channels[0].ops[op].sustain = data[op*13+4] & 0x0f; + instr->channels[0].ops[op].release = data[op*13+7] & 0x0f; + instr->channels[0].ops[op].waveform = data[26+op] & 0x07; + instr->channels[0].ops[op].ksl = data[op*13+0] & 0x03; + instr->channels[0].ops[op].level = data[op*13+8] & 0x3f; + } + instr->channels[0].feedback = data[2] & 0x07; + instr->channels[0].am = (data[25] & 0x01) ^ 1; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; +} + +bank_instr *bk_read_bank(const char *filename, byte drum) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mbank(&data, &instr, filename, 28, 256*42+28); + if(size == 0) { + return NULL; + } + byte *odata = data; + if(memcmp(data+2, "ADLIB-", 6) != 0) { + DMX_LOG(STR_BNK_EMFHDR, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + data += 8; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + // ushort nnorm = data[0]; + uint ninst = dmx_read_uint16(&data[2]); + data += 20; + if((ninst*42+28) > size) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + data += ninst*12; + int pos = 0; + for(int i = 0; i < ninst; i++, data += 30) { + if(data[0] != 0) { + continue; + } + bk_read_instr(&instr[pos + (drum ? 128 : 0)], data+2, drum); + byte *ndata = odata+(i*12+28); + instr[pos + (drum ? 128 : 0)].percnum = ndata[2] & 0x7f; + memcpy(instr[pos + (drum ? 128 : 0)].name, ndata+3, 8); + instr[pos + (drum ? 128 : 0)].name[8] = 0; + if(++pos == 128) { + break; + } + } + DMX_FREE(odata); + return instr; +} + +void tim_read_instr(bank_instr *instr, byte *data) { + instr->percnum = 0; + instr->fixed = 0; + instr->op = b_op2; + for(int op = 0; op < 2; op++) { + instr->channels[0].ops[op].tremolo = dmx_read_uint16(&data[op*26+18]) & 0x01; + instr->channels[0].ops[op].vibrato = dmx_read_uint16(&data[op*26+20]) & 0x01; + instr->channels[0].ops[op].sustaining = dmx_read_uint16(&data[op*26+10]) & 0x01; + instr->channels[0].ops[op].ksr = dmx_read_uint16(&data[op*26+22]) & 0x01; + instr->channels[0].ops[op].mult = dmx_read_uint16(&data[op*26+2]) & 0x0f; + instr->channels[0].ops[op].attack = dmx_read_uint16(&data[op*26+6]) & 0x0f; + instr->channels[0].ops[op].decay = dmx_read_uint16(&data[op*26+12]) & 0x0f; + instr->channels[0].ops[op].sustain = dmx_read_uint16(&data[op*26+8]) & 0x0f; + instr->channels[0].ops[op].release = dmx_read_uint16(&data[op*26+14]) & 0x0f; + instr->channels[0].ops[op].waveform = dmx_read_uint16(&data[52+op*2]) & 0x07; + instr->channels[0].ops[op].ksl = dmx_read_uint16(&data[op*26+0]) & 0x03; + instr->channels[0].ops[op].level = dmx_read_uint16(&data[op*26+16]) & 0x3f; + } + instr->channels[0].feedback = dmx_read_uint16(&data[4]) & 0x07; + instr->channels[0].am = (dmx_read_uint16(&data[50]) & 0x01) ^ 1; + instr->channels[0].offset = 0; + instr->channels[0].detune = 0; +} + +bank_instr *tim_read_bank(const char *filename, byte drum) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mbank(&data, &instr, filename, 6, 256*65+6); + if(size == 0) { + return NULL; + } + byte *odata = data; + for(int i = 0; i < 256; i++) { + instr[i].op = b_op0; + instr[i].name[0] = 0; + } + uint ninst = dmx_read_uint16(&data[2]); + data += 6; + if((ninst*65+6) > size) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + data += ninst*9; + for(int i = 0; (i < ninst) && (i < 128); i++, data += 56) { + tim_read_instr(&instr[i + (drum ? 128 : 0)], data); + byte *ndata = odata+(i*9+6); + memcpy(instr[i + (drum ? 128 : 0)].name, ndata, 8); + instr[i + (drum ? 128 : 0)].name[8] = 0; + } + DMX_FREE(odata); + return instr; +} + +/* +void wopl_read_instr(bank_instr *instr, byte *data, byte drum) { + memcpy(instr->name, data, 32); + instr->name[31] = 0; + data += 32; + instr->percnum = data[6] & 0x7f; + instr->fixed = drum; + instr->op = (data[7] & 0x01) ? (((data[7] & 0x02) > 0) ? b_op22 : b_op4) : b_op2; + instr->channels[0].offset = (short)dmx_read_uint16be(&data[0]); + instr->channels[0].detune = 0; + if(instr->op != b_op2) { + instr->channels[1].offset = (short)dmx_read_uint16be(&data[2]); + instr->channels[0].detune = (char)data[5]; + } + instr->channels[0].am = data[8] & 0x01; + instr->channels[0].feedback = (data[8] >> 1) & 0x07; + if(instr->op != b_op2) { + instr->channels[1].am = data[9] & 0x01; + instr->channels[1].feedback = (data[9] >> 1) & 0x07; + } + data += 10; + for(int ch = 0; ch < ((instr->op != b_op2) ? 2 : 1); ch++) { + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[1-op].tremolo = (data[op*5+0] & 0x80) > 0; + instr->channels[ch].ops[1-op].vibrato = (data[op*5+0] & 0x40) > 0; + instr->channels[ch].ops[1-op].sustaining = (data[op*5+0] & 0x20) > 0; + instr->channels[ch].ops[1-op].ksr = (data[op*5+0] & 0x10) > 0; + instr->channels[ch].ops[1-op].mult = data[op*5+0] & 0x0f; + instr->channels[ch].ops[1-op].attack = (data[op*5+2] >> 4) & 0x0f; + instr->channels[ch].ops[1-op].decay = data[op*5+2] & 0x0f; + instr->channels[ch].ops[1-op].sustain = (data[op*5+3] >> 4) & 0x0f; + instr->channels[ch].ops[1-op].release = data[op*5+3] & 0x0f; + instr->channels[ch].ops[1-op].waveform = data[op*5+4] & 0x07; + instr->channels[ch].ops[1-op].ksl = (data[op*5+1] >> 6) & 0x03; + instr->channels[ch].ops[1-op].level = data[op*5+1] & 0x3f; + } + data += 10; + } +} + +bank_instr *wopl_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mhbank(&data, &instr, filename, 256*62+9, 512*66+77, "WOPL3-BANK", 10); + if(size == 0) { + return NULL; + } + byte *odata = data; + int stride = (data[7] != 0) ? 66 : 62; + int off = (data[7] != 0) ? 77 : 9 + ((data[4] == 2) ? 102 : 0); + int off2 = (data[4] == 2) ? (128*stride) : 0; + if((256*stride+off+off2) > size) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(odata); + DMX_FREE(instr); + return NULL; + } + data += off; + for(int i = 0; i < 128; i++) { + wopl_read_instr(&instr[i], data, 0); + data += stride; + } + data += off2; + for(int i = 0; i < 128; i++) { + wopl_read_instr(&instr[i + 128], data, 1); + data += stride; + } + DMX_FREE(odata); + return instr; +} +*/ + +// SKB! +// OO-NNNNN NAME[N] FPPPPPPP AFFFAFFF +// DDDDDDDD OOOOOOOO TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR -WWW-WWW + +uint skb_read_instr(bank_instr *instr, byte *data, uint pos, uint size) { + if((pos + 1) > size) { + return 0; + } + data += pos; + instr->op = (data[0] >> 6) & 0x03; + byte nlen = data[0] & 0x1f; + if((pos + 1 + nlen) > size) { + return 0; + } + memcpy(instr->name, data+1, nlen); + instr->name[nlen] = 0; + pos += 1 + nlen; + if(instr->op == b_op0) { + return pos; + } + int numch = (instr->op == b_op2) ? 1 : 2; + if((pos + 1 + (12 * numch)) > size) { + return 0; + } + data += 1 + nlen; + instr->fixed = (data[0] & 0x80) > 0; + instr->percnum = data[0] & 0x7f; + instr->channels[0].am = (data[1] & 0x08) > 0; + instr->channels[0].feedback = data[1] & 0x07; + if(numch == 2) { + instr->channels[1].am = (data[1] & 0x80) > 0; + instr->channels[1].feedback = (data[1] >> 4) & 0x07; + } + data += 2; + for(int ch = 0; ch < numch; ch++) { + instr->channels[ch].detune = ((short)data[0]) - 128; + instr->channels[ch].offset = ((short)data[1]) - 128; + data += 2; + for(int op = 0; op < 2; op++) { + instr->channels[ch].ops[op].tremolo = (data[0] & 0x80) > 0; + instr->channels[ch].ops[op].vibrato = (data[0] & 0x40) > 0; + instr->channels[ch].ops[op].sustaining = (data[0] & 0x20) > 0; + instr->channels[ch].ops[op].ksr = (data[0] & 0x10) > 0; + instr->channels[ch].ops[op].mult = data[0] & 0x0f; + instr->channels[ch].ops[op].ksl = (data[1] >> 6) & 0x03; + instr->channels[ch].ops[op].level = data[1] & 0x3f; + instr->channels[ch].ops[op].attack = (data[2] >> 4) & 0x0f; + instr->channels[ch].ops[op].decay = data[2] & 0x0f; + instr->channels[ch].ops[op].sustain = (data[3] >> 4) & 0x0f; + instr->channels[ch].ops[op].release = data[3] & 0x0f; + data += 4; + } + instr->channels[ch].ops[0].waveform = (data[0] >> 4) & 0x07; + instr->channels[ch].ops[1].waveform = data[0] & 0x07; + data += 1; + } + return pos + 2 + (11 * numch); +} + +bank_instr *skb_read_bank(const char *filename) { + byte *data; + bank_instr *instr; + uint size = bnk_read_mhbank(&data, &instr, filename, 256, 256*56, "SKB!", 4); + if(size == 0) { + return NULL; + } + uint pos = 0; + for(int i = 0; i < 256; i++) { + pos = skb_read_instr(&instr[i], data, pos, size); + if(pos == 0) { + DMX_LOG(STR_BNK_EEOD, filename); + DMX_FREE(data); + DMX_FREE(instr); + return NULL; + } + } + DMX_FREE(data); + return instr; +} + +bank_instr *bnk_read_bank(const char *filename, byte drum) { + int len = 0; + int dp = 0; + char ch; + while(ch = filename[len++]) { + if(ch == '.') { + dp = len; + } + } + if(dp == 0) { + DMX_LOG(STR_BNK_UNKN, filename); + return NULL; + } + char ext[len -= dp]; + for(int c = 0; c < len; c++) { + ext[c] = filename[dp+c]; + if((ext[c] >= 'A') && (ext[c] <= 'Z')) { + ext[c] = ext[c] - 'A' + 'a'; + } + } + if(strcmp(ext, "skb") == 0) { + return skb_read_bank(filename); + } + else if((strcmp(ext, "dmx") == 0) || (strcmp(ext, "op2") == 0) || (strcmp(ext, "lmp") == 0)) { + return dmx_read_bank(filename); + } + else if(strcmp(ext, "tmb") == 0) { + return tmb_read_bank(filename); + } + else if(strcmp(ext, "ibk") == 0) { + return ibk_read_bank(filename, drum); + } + else if(strcmp(ext, "sb") == 0) { + return sb_read_bank(filename, drum); + } + else if(strcmp(ext, "o3") == 0) { + return sb3_read_bank(filename, drum); + } + else if(strcmp(ext, "op3") == 0) { + return op3_read_bank(filename); + } + else if((strcmp(ext, "ad") == 0) || (strcmp(ext, "opl") == 0)) { + return ad_read_bank(filename); + } + else if(strcmp(ext, "bnk") == 0) { + return bk_read_bank(filename, drum); + } + else if((strcmp(ext, "tim") == 0) || (strcmp(ext, "snd") == 0)) { + return tim_read_bank(filename, drum); + } + // else if(strcmp(ext, "wopl") == 0) { + // return wopl_read_bank(filename); + // } + DMX_LOG(STR_BNK_UNKN, filename); + return NULL; +} + +bank_instr *bnk_read_banks(const char *filename, const char *drumname, byte usedrum) { + bank_instr *ins1 = bnk_read_bank(filename, usedrum); + if((ins1 != NULL) && (drumname != NULL)) { + bank_instr *ins2 = bnk_read_bank(drumname, usedrum ^ 1); + if(ins2 != NULL) { + memcpy(ins1+128, ins2+128, sizeof(bank_instr)*128); + DMX_FREE(ins2); + } + else { + DMX_FREE(ins1); + ins1 = NULL; + } + } + return ins1; +} + +uint skb_write_instr(bank_instr *instr, byte *data, uint pos) { + data += pos; + byte nlen = strlen(instr->name); + data[0] = (instr->op << 6) | nlen; + memcpy(data+1, instr->name, nlen); + pos += 1 + nlen; + if(instr->op == b_op0) { + return pos; + } + int numch = (instr->op == b_op2) ? 1 : 2; + data += 1 + nlen; + data[0] = (instr->fixed << 7) | instr->percnum; + data[1] = ((numch == 2) ? ((instr->channels[1].am << 7) | (instr->channels[1].feedback << 4)) : 0) | (instr->channels[0].am << 3) | instr->channels[0].feedback; + data += 2; + for(int ch = 0; ch < numch; ch++) { + data[0] = (byte)((short)(instr->channels[ch].detune + 128)); + data[1] = (byte)((short)(instr->channels[ch].offset + 128)); + data += 2; + for(int op = 0; op < 2; op++) { + data[0] = (instr->channels[ch].ops[op].tremolo << 7) | (instr->channels[ch].ops[op].vibrato << 6) | (instr->channels[ch].ops[op].sustaining << 5) | + (instr->channels[ch].ops[op].ksr << 4) | instr->channels[ch].ops[op].mult; + data[1] = (instr->channels[ch].ops[op].ksl << 6) | instr->channels[ch].ops[op].level; + data[2] = (instr->channels[ch].ops[op].attack << 4) | instr->channels[ch].ops[op].decay; + data[3] = (instr->channels[ch].ops[op].sustain << 4) | instr->channels[ch].ops[op].release; + data += 4; + } + data[0] = (instr->channels[ch].ops[0].waveform << 4) | instr->channels[ch].ops[1].waveform; + data += 1; + } + return pos + 2 + (11 * numch); +} + +byte skb_write_bank(bank_instr *instr, const char *filename) { + int err; + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) { + err = errno; + DMX_LOG(STR_BNK_OPNERR, filename, strerror(err), err); + return 0; + } + if(fwrite("SKB!", 1, 4, fd) != 4) { + DMX_LOG(STR_BNK_EWIO, filename); + fclose(fd); + return 0; + } + byte *data = DMX_MALLOC(256*56); + uint size = 0; + for(int i = 0; i < 256; i++) { + size = skb_write_instr(&instr[i], data, size); + } + if(fwrite(data, 1, size, fd) != size) { + DMX_LOG(STR_BNK_EWIO, filename); + fclose(fd); + DMX_FREE(data); + return 0; + } + fclose(fd); + DMX_FREE(data); + return 1; +} diff --git a/drawlist.h b/drawlist.h new file mode 100644 index 0000000..63bdaed --- /dev/null +++ b/drawlist.h @@ -0,0 +1,142 @@ + +void draw_set_material(world_t *world, material_t *mat) { + shd_sel(mat->shader ? mat->shader : gdr.shd_world, world); + shd_scolor("specular", mat->spec_color); + shd_float("shine", mat->shine); + shd_float("density", mat->density); + if(mat->texture) + glBindTexture(GL_TEXTURE_2D, mat->texture); +} + +int draw_dispatch(world_t *world, drawlist_t *list) { + int drawn = 0; + draw_t *draw; + mat4 mat; + glm_mat4_identity(mat); + glm_translate(mat, list->position); + glBindVertexArray(list->vao); + for(int z = 0; z < list->size; z++) { + draw = &list->draws[z]; + draw_set_material(world, draw->material); + shd_mat4("model", mat); + glDrawArrays(GL_TRIANGLES, draw->offset, draw->size); + drawn += draw->size; + } + return drawn; +} + +void draw_world(world_t *world) { + int x1 = ((int)world->camera.pos_x) / CHUNK_SIZE; + int y1 = ((int)world->camera.pos_y) / CHUNK_SIZE; + int z1 = ((int)world->camera.pos_z) / CHUNK_SIZE; + int x2 = x1 + gdr.ldist_chunk_xz; + int y2 = y1 + gdr.ldist_chunk_y; + int z2 = z1 + gdr.ldist_chunk_xz; + int lists = 0; + int drawn = 0; + chunk_t *chunk; // = &world->global; + x1 -= gdr.ldist_chunk_xz; + y1 -= gdr.ldist_chunk_y; + z1 -= gdr.ldist_chunk_xz; + // x1 = CHUNK_CLAMP(world, x1, x); + // y1 = CHUNK_CLAMP(world, y1, y); + // z1 = CHUNK_CLAMP(world, z1, z); + // x2 = CHUNK_CLAMP(world, x2, x); + // y2 = CHUNK_CLAMP(world, y2, y); + // z2 = CHUNK_CLAMP(world, z2, z); + // if(chunk->drawlist) { + // drawn += draw_dispatch(chunk->drawlist); + // lists++; + // } + for(int cx = x1; cx <= x2; cx++) { + for(int cz = z1; cz <= z2; cz++) { + for(int cy = y1; cy <= y2; cy++) { + if(!(chunk = chunk_get(world, cx, cy, cz, 0))) + continue; + if(chunk->drawlist) { + drawn += draw_dispatch(world, chunk->drawlist); + lists++; + } + } + } + } + gdr.lists_drawn += lists; + gdr.tris_drawn += drawn / 3; +} + +/* +void draw_floor(world_t *world) { + if(!(world->flat_tex)) + return; + mat4 mat; + glm_mat4_identity(mat); + glm_scale(mat, world->flat_scale); + glm_translate(mat, world->flat_offset); + shd_mat4("model", mat); + glBindVertexArray(gdr.vao_box); + glBindTexture(GL_TEXTURE_2D, world->flat_tex); + draw_set_material(world->flat_mat); + glDrawArrays(GL_TRIANGLES, 30, 6); +} +*/ + +void draw_destroy(drawlist_t *list) { + if(list->bind) + buf_destroy(list->bind); + if(list->draws) + mem_free(list->draws); + list->size = 0; + list->draws = NULL; + list->bind = NULL; +} + +void draw_build(chunk_t *chunk, drawlist_t *list) { + int pos; + int done; + int verts; + uint id; + material_t *material; + polygon_t *poly; + float *vert; + draw_t *draw; + float *vertices = mem_alloc(chunk->poly_list.stored * 3 * sizeof(vertex_t), MEM_POLY); + list->size = 0; + list->draws = NULL; + for(done = 0; done < chunk->poly_list.stored; list->size += 1) { + list->draws = mem_realloc(list->draws, sizeof(draw_t) * (list->size + 1), MEM_POLY); + draw = &list->draws[list->size]; + verts = 0; + material = NULL; + pos = 0; + draw->offset = done * 3; + while((id = lst_iter(&chunk->poly_list, &pos)) != 0xffffffff) { + poly = &chunk->poly_pool[id]; + if(!material) { + material = poly->material; + for(int z = 0; z < list->size; z++) { + if(material == list->draws[z].material) { + material = NULL; + break; + } + } + if(!material) + continue; + } + else if(material != poly->material) { + // logd("nnt", "C %d %d %d %d %.3f %.3f", texture, poly->texture, material, poly->material, density, poly->density); + continue; + } + sys_assert(done < chunk->poly_list.stored) + memcpy(&vertices[24 * done], poly->vertices, 3 * sizeof(vertex_t)); + done++; + verts++; + } + draw->size = verts * 3; + draw->material = material; + // logd("nnt", "L %d %d %d %.3f", draw->size, draw->texture, draw->material, draw->density); + } + list->bind = buf_init(0, &list->vbo, &list->vao, vertices, done * 3 * sizeof(vertex_t), 8, 3, 3, 2); + mem_free(vertices); + VEC3_SET(list->position, chunk->box.x1, chunk->box.y1, chunk->box.z1); + // logd("nnt", "= %d @ %.3f %.3f %.3f", list->size, list->position[0], list->position[1], list->position[2]); +} diff --git a/entity.h b/entity.h new file mode 100644 index 0000000..8dc3486 --- /dev/null +++ b/entity.h @@ -0,0 +1,333 @@ + +void ent_position(entity_t *ent, double x, double y, double z) { + ent->last_x = ent->pos_x = x; + ent->last_y = ent->pos_y = y; + ent->last_z = ent->pos_z = z; + AABB_SET(ent->box, x - (ent->width / 2.0), y, z - (ent->width / 2.0), x + (ent->width / 2.0), y + ent->height, z + (ent->width / 2.0)); +} + +void ent_angle(entity_t *ent, float yaw, float pitch) { + ent->yaw = yaw; + ent->pitch = pitch; + ent->rot_yaw = ((double)yaw) - 90.0; +} + +void ent_pos(entity_t *ent, double x, double y, double z, float yaw, float pitch) { + ent_position(ent, x, y, z); + ent_angle(ent, yaw, pitch); +} + +void ent_init(entity_t *ent, world_t *world, double x, double y, double z, float yaw, float pitch) { + memset(ent, 0, sizeof(entity_t)); + ent->world = world; + ent->speed = 1.0; + ent->eye = 1.8; + ent->width = 0.6; + ent->height = 2.0; + ent->step_height = 0.6; + ent->jump_motion = 0.42; + ent->fly_base_speed = 0.05; + ent->walk_base_speed = 0.1; + ent->air_base_speed = 0.02; + ent->first_tick = 1; + ent_pos(ent, x, y, z, yaw, pitch); +} + +void ent_position_vec(entity_t *ent, vec3 pos) { + ent_position(ent, pos[0], pos[1], pos[2]); +} + +void ent_position_eye(entity_t *ent, vec3 pos) { + ent_position(ent, pos[0], pos[1] - ent->eye, pos[2]); +} + +void ent_getpos(entity_t *ent, vec3 pos) { + VEC3_SET(pos, ent->pos_x, ent->pos_y, ent->pos_z); +} + +void ent_geteye(entity_t *ent, vec3 pos) { + VEC3_SET(pos, ent->pos_x, ent->pos_y + ent->eye, ent->pos_z); +} + +void ent_angle_cam(entity_t *ent, camera_t *cam) { + ent_angle(ent, cam->yaw, cam->pitch); +} + +void ent_tick(entity_t *ent) { + float strafe, forward; + byte jumping, noclip; + double last_mot_y, velo, friction, mot; + double x, y, z; + // double cx, cy, cz; + noclip = (ent == ent->world->entity) && ent->world->noclip && !(ent->world->camonly); + if(noclip) + ent->ground = 0; + ent->last_x = ent->pos_x; + ent->last_y = ent->pos_y; + ent->last_z = ent->pos_z; + ent->first_tick = 0; + if(!(ent->sprinting) && !(ent->sneak) && (ent->forward > 0.0) && /* wld.can_sprint && */ !(ent->use) && ent->sprint) + ent->sprinting = 1; + if(ent->sprinting && (ent->sneak || (ent->forward <= 0.0) || ent->use || ent->collided_h /* || !(wld.can_sprint) */)) + ent->sprinting = 0; + // if(wld.can_fly) { + if(noclip) { + if(!(ent->flying)) + ent->flying = 1; + } + else if(!(ent->last_jump) && ent->jump) { + if(ent->fly_timer == 0) { + ent->fly_timer = 7; + } + else { + ent->flying ^= 1; + ent->fly_timer = 0; + } + } + // } + ent->last_jump = ent->jump; + if(ent->flying && (ent == ent->world->entity)) { + if(ent->sneak) + ent->motion_y -= ent->fly_speed * 3.0 * (ent->sprint ? 2.0 : 1.0); + if(ent->jump) + ent->motion_y += ent->fly_speed * 3.0 * (ent->sprint ? 2.0 : 1.0); + } + if (ent->fly_timer > 0) + ent->fly_timer -= 1; + if (ent->jump_ticks > 0) + ent->jump_ticks -= 1; + if(fabs(ent->motion_x) < 0.005) + ent->motion_x = 0.0; + if(fabs(ent->motion_y) < 0.005) + ent->motion_y = 0.0; + if(fabs(ent->motion_z) < 0.005) + ent->motion_z = 0.0; + if(ent->blocked) { + jumping = 0; + strafe = 0.0; + forward = 0.0; + } + else if(ent == ent->world->entity) { + strafe = ent->strafe * (ent->sneak ? 0.3 : 1.0) * (ent->use ? 0.2 : 1.0); + forward = ent->forward * (ent->sneak ? 0.3 : 1.0) * (ent->use ? 0.2 : 1.0); + jumping = ent->jump; + } + if(jumping) { + if(ent->ground && ent->jump_ticks == 0) { + ent->motion_y = ent->jump_motion; + if(ent->sprinting) { + double bmot = ent->rot_yaw * M_PI / 180.0; + ent->motion_x -= sin(bmot) * 0.2; + ent->motion_z += cos(bmot) * 0.2; + } + ent->jump_ticks = 10; + } + } + else { + ent->jump_ticks = 0; + } + strafe *= 0.98; + forward *= 0.98; + if(ent->flying) { + last_mot_y = ent->motion_y; + ent->air_speed = ent->fly_speed * (ent->sprinting ? 2.0 : 1.0); + } + velo = 0.91; + if(ent->ground) { + velo *= (1.0 - ent->world->friction) * 0.5 + 0.5; + friction = ent->walk_speed * 0.16277136 / (velo * velo * velo); + } + else { + friction = ent->air_speed; + } + mot = strafe * strafe + forward * forward; + if(mot >= 1.0E-4) { + mot = sqrt(mot); + if(mot < 1.0) + mot = 1.0; + mot = friction / mot; + strafe = strafe * mot; + forward = forward * mot; + double smot = sin(ent->rot_yaw * M_PI / 180.0); + double cmot = cos(ent->rot_yaw * M_PI / 180.0); + ent->motion_x += strafe * cmot - forward * smot; + ent->motion_z += forward * cmot + strafe * smot; + } + velo = 0.91; + if(ent->ground) + velo *= (1.0 - ent->world->friction) * 0.5 + 0.5; + x = ent->motion_x; + y = ent->motion_y; + z = ent->motion_z; + if(noclip) { + AABB_OFFSET(ent->box, x, y, z) + ent->pos_x = (ent->box.x1 + ent->box.x2) / 2.0; + ent->pos_y = ent->box.y1; + ent->pos_z = (ent->box.z1 + ent->box.z2) / 2.0; + } + else { + double last_x, last_y, last_z; + bbox_t box; + bbox_t *list; + int len; + byte step; + if(ent->braked) { + ent->braked = 0; + x *= 0.25; + y *= 0.05000000074505806; + z *= 0.25; + ent->motion_x = ent->motion_y = ent->motion_z = 0.0; + } + last_x = x; + last_y = y; + last_z = z; + if(ent->ground && ent->sneak) { + double shift; + for(shift = 0.05; x != 0.0 && box_get_colliding(ent->world, NULL, box_offset_to(&ent->box, &box, x, -1.0, 0.0)) == 0; last_x = x) { + if(x < shift && x >= -shift) + x = 0.0; + else if(x > 0.0) + x -= shift; + else + x += shift; + } + for(; z != 0.0 && box_get_colliding(ent->world, NULL, box_offset_to(&ent->box, &box, 0.0, -1.0, z)) == 0; last_z = z) { + if(z < shift && z >= -shift) + z = 0.0; + else if(z > 0.0) + z -= shift; + else + z += shift; + } + for(; x != 0.0 && z != 0.0 && box_get_colliding(ent->world, NULL, box_offset_to(&ent->box, &box, x, -1.0, z)) == 0; last_z = z) { + if(x < shift && x >= -shift) + x = 0.0; + else if(x > 0.0) + x -= shift; + else + x += shift; + last_x = x; + if(z < shift && z >= -shift) + z = 0.0; + else if(z > 0.0) + z -= shift; + else + z += shift; + } + } + len = box_get_colliding(ent->world, &list, box_add_coord(&ent->box, &box, x, y, z)); + AABB_COPY(ent->box, box); + for(int n = 0; n < len; n++) + y = box_calc_yoff(&list[n], &ent->box, y); + AABB_OFFSET(ent->box, 0.0, y, 0.0); + step = ent->ground || last_y != y && last_y < 0.0; + for(int n = 0; n < len; n++) + x = box_calc_xoff(&list[n], &ent->box, x); + AABB_OFFSET(ent->box, x, 0.0, 0.0); + for(int n = 0; n < len; n++) + z = box_calc_zoff(&list[n], &ent->box, z); + AABB_OFFSET(ent->box, 0.0, 0.0, z); + if(ent->step_height > 0.0 && step && (last_x != x || last_z != z)) { + double step_x = x; + double step_y = y; + double step_z = z; + bbox_t box_prev, box_shift, box_sshift; + double shift_x, shift_y, shift_z; + double shift_sx, shift_sy, shift_sz; + AABB_COPY(ent->box, box_prev); + AABB_COPY(box, ent->box); + y = ent->step_height; + len = box_get_colliding(ent->world, &list, box_add_coord(&ent->box, &box, last_x, y, last_z)); + AABB_COPY(ent->box, box_shift); + box_add_coord(&box_shift, &box, last_x, 0.0, last_z); + shift_y = y; + for(int n = 0; n < len; n++) + shift_y = box_calc_yoff(&list[n], &box, shift_y); + AABB_OFFSET(box_shift, 0.0, shift_y, 0.0); + shift_x = last_x; + for(int n = 0; n < len; n++) + shift_x = box_calc_xoff(&list[n], &box_shift, shift_x); + AABB_OFFSET(box_shift, shift_x, 0.0, 0.0); + shift_z = last_z; + for(int n = 0; n < len; n++) + shift_z = box_calc_zoff(&list[n], &box_shift, shift_z); + AABB_OFFSET(box_shift, 0.0, 0.0, shift_z); + AABB_COPY(ent->box, box_sshift); + shift_sy = y; + for(int n = 0; n < len; n++) + shift_sy = box_calc_yoff(&list[n], &box_sshift, shift_sy); + AABB_OFFSET(box_sshift, 0.0, shift_sy, 0.0); + shift_sx = last_x; + for(int n = 0; n < len; n++) + shift_sx = box_calc_xoff(&list[n], &box_sshift, shift_sx); + AABB_OFFSET(box_sshift, shift_sx, 0.0, 0.0); + shift_sz = last_z; + for(int n = 0; n < len; n++) + shift_sz = box_calc_zoff(&list[n], &box_sshift, shift_sz); + AABB_OFFSET(box_sshift, 0.0, 0.0, shift_sz); + if((shift_x * shift_x + shift_z * shift_z) > (shift_sx * shift_sx + shift_sz * shift_sz)) { + x = shift_x; + z = shift_z; + y = -shift_y; + AABB_COPY(box_shift, ent->box); + } + else { + x = shift_sx; + z = shift_sz; + y = -shift_sy; + AABB_COPY(box_sshift, ent->box); + } + for(int n = 0; n < len; n++) + y = box_calc_yoff(&list[n], &ent->box, y); + AABB_OFFSET(ent->box, 0.0, y, 0.0); + if(step_x * step_x + step_z * step_z >= x * x + z * z) { + x = step_x; + y = step_y; + z = step_z; + AABB_COPY(box_prev, ent->box); + } + } + ent->pos_x = (ent->box.x1 + ent->box.x2) / 2.0; + ent->pos_y = ent->box.y1; + ent->pos_z = (ent->box.z1 + ent->box.z2) / 2.0; + ent->collided_h = last_x != x || last_z != z; + ent->collided_v = last_y != y; + ent->ground = ent->collided_v && last_y < 0.0; + ent->collided = ent->collided_h || ent->collided_v; + if(ent->ground) { + if(ent->fall_distance > 0.0) { + // fall_distance ... + ent->fall_distance = 0.0; + } + } + else if(y < 0.0) { + ent->fall_distance = ent->fall_distance - y; + } + if(last_x != x) + ent->motion_x = 0.0; + if(last_z != z) + ent->motion_z = 0.0; + if(last_y != y) + ent->motion_y = 0.0; + } + ent->motion_y -= ent->world->gravity * 0.1; + ent->motion_y *= 0.9800000190734863; + ent->motion_x *= velo; + ent->motion_z *= velo; + if(ent->flying) + ent->motion_y = last_mot_y * 0.6; + ent->walk_speed = ent->walk_base_speed * ent->speed; + ent->air_speed = ent->air_base_speed * ent->speed; + ent->fly_speed = ent->fly_base_speed * ent->speed; + if(ent->sprinting) { + ent->air_speed *= 1.3; + ent->walk_speed *= 1.3; + } + if(ent->ground && ent->flying && !noclip) + ent->flying = 0; + // cx = ent->world->clamp_xz ? CLAMP_VALUE(ent->pos_x, ent->world->global.box.x1, ent->world->global.box.x2) : ent->pos_x; + // cy = ent->world->clamp_y ? CLAMP_VALUE(ent->pos_y, ent->world->global.box.y1, ent->world->global.box.y2) : ent->pos_y; + // cz = ent->world->clamp_xz ? CLAMP_VALUE(ent->pos_z, ent->world->global.box.z1, ent->world->global.box.z2) : ent->pos_z; + // if(cx != ent->pos_x || cy != ent->pos_y || cz != ent->pos_z) + // ent_position(ent, cx, cy, cz); +} diff --git a/files.h b/files.h new file mode 100644 index 0000000..8a9733b --- /dev/null +++ b/files.h @@ -0,0 +1,162 @@ + +uint file_read_hdr(byte **data, const char *filename, const char *hdr, uint hdrsize, byte flags) { + int err; + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) { + err = errno; + if(err != ENOENT || !(flags & FILE_FLAG_IGNMISS)) + loge(LOG_IO, STR_FILE_OPENERR, filename, strerror(err), err); + return 0; + } + if(fseek(fd, 0L, SEEK_END)) { + err = errno; + loge(LOG_IO, STR_FILE_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + long size = ftell(fd); + if(size < 0L) { + err = errno; + loge(LOG_IO, STR_FILE_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + if(fseek(fd, 0L, SEEK_SET)) { + err = errno; + loge(LOG_IO, STR_FILE_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + if(!size) { + if(!(flags & FILE_FLAG_IGNEMPTY)) + loge(LOG_IO, STR_FILE_EEMPTY, filename); + fclose(fd); + if((flags & FILE_FLAG_IGNEMPTY) && (flags & FILE_FLAG_STRING)) { + *data = mem_alloc(1, MEM_FILE); + (*data)[0] = 0; + return 1; + } + return 0; + } + if(size < hdrsize) { + loge(LOG_IO, STR_FILE_ESHORT, filename); + fclose(fd); + return 0; + } + *data = mem_alloc(size + ((flags & FILE_FLAG_STRING) ? 1 : 0), MEM_FILE); + if(hdrsize > 0) { + if(fread(*data, 1, hdrsize, fd) != hdrsize) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_FILE_EIO : STR_FILE_EEOF, filename); + fclose(fd); + mem_free(*data); + return 0; + } + if(memcmp(*data, hdr, hdrsize) != 0) { + loge(LOG_IO, STR_FILE_EMFHDR, filename); + fclose(fd); + mem_free(*data); + return 0; + } + } + if(fread(&((*data)[hdrsize]), 1, size-hdrsize, fd) != size) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_FILE_EIO : STR_FILE_EEOF, filename); + fclose(fd); + mem_free(*data); + return 0; + } + fclose(fd); + if(flags & FILE_FLAG_STRING) + (*data)[size] = 0; + return size; +} + +byte file_has_ext(const char *file, const char **exts, int n_exts) { + int len = strlen(file); + int extlen; + for(int extidx = 0; extidx < n_exts; extidx++) { + extlen = strlen(exts[extidx]); + if(len < extlen + 2) + continue; + if((file[len - (extlen + 1)] == '.') && str_eq_lower(exts[extidx], &file[len - extlen])) + return 1; + } + return 0; +} + +const char **file_list_dir(int *count, const char *path, const char **exts, int n_exts) { + DIR *dir; + struct dirent *ent; + int found = 0; + // int extlen; + // int extidx; + int pos = 0; + int plen = strlen(path); + char *buf = NULL; + uint *idx = NULL; + const char *sep = "/"; + if(path[plen - 1] == '/') + sep = ""; + else + plen += 1; + dir = opendir(path); + if(dir) { + while(ent = readdir(dir)) { + if(ent->d_type == DT_REG) { + if(exts && !file_has_ext(ent->d_name, exts, n_exts)) { + /* + len = strlen(ent->d_name); + for(extidx = 0; extidx < n_exts; extidx++) { + extlen = strlen(exts[extidx]); + if(len < extlen + 2) + continue; + if((ent->d_name[len - (extlen + 1)] == '.') && str_eq_lower(exts[extidx], &ent->d_name[len - extlen])) + break; + } + if(extidx == n_exts) + */ + continue; + } + idx = mem_realloc(idx, (sizeof(uint) * (found + 1)), MEM_FILE); + buf = mem_realloc(buf, pos + plen + strlen(ent->d_name) + 1, MEM_FILE); + idx[found] = pos; + pos += sprintf(&buf[pos], "%s%s%s", path, sep, ent->d_name) + 1; + found += 1; + } + } + closedir(dir); + } + *count = found; + if(buf) { + char **list = mem_alloc((sizeof(char *) * found) + pos, MEM_FILE); + memcpy(&list[found], buf, pos); + for(int z = 0; z < found; z++) { + list[z] = &((char *)&list[found])[idx[z]]; + } + mem_free(buf); + mem_free(idx); + // logd("TMP", "%d ...", found); + // for(int z = 0; z < found; z++) { + // logd("TMP", "%d -> %s", z, list[z]); + // } + return (const char **)list; + } + return NULL; +} + +const char **file_list(int *count, const char *path, const char **exts, int n_exts) { + struct stat node; + *count = 0; + if(stat(path, &node)) + return NULL; + if(((node.st_mode & S_IFMT) == S_IFREG) && file_has_ext(path, exts, n_exts)) { + int len = strlen(path) + 1; + char **file = mem_alloc(sizeof(char *) + len, MEM_FILE); + file[0] = (char *)&file[1]; + memcpy(&file[1], path, len); + *count = 1; + return (const char**)file; + } + return ((node.st_mode & S_IFMT) == S_IFDIR) ? file_list_dir(count, path, exts, n_exts) : NULL; +} diff --git a/fonts/font.xcf b/fonts/font.xcf new file mode 100644 index 0000000..2317e04 Binary files /dev/null and b/fonts/font.xcf differ diff --git a/fonts/font_0000.png b/fonts/font_0000.png new file mode 100644 index 0000000..824bc5a Binary files /dev/null and b/fonts/font_0000.png differ diff --git a/fonts/font_0020.png b/fonts/font_0020.png new file mode 100644 index 0000000..0adc508 Binary files /dev/null and b/fonts/font_0020.png differ diff --git a/fonts/font_0030.png b/fonts/font_0030.png new file mode 100644 index 0000000..779bbd8 Binary files /dev/null and b/fonts/font_0030.png differ diff --git a/fonts/font_01f0.png b/fonts/font_01f0.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f0.png differ diff --git a/fonts/font_01f1.png b/fonts/font_01f1.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f1.png differ diff --git a/fonts/font_01f2.png b/fonts/font_01f2.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f2.png differ diff --git a/fonts/font_01f3.png b/fonts/font_01f3.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f3.png differ diff --git a/fonts/font_01f4.png b/fonts/font_01f4.png new file mode 100644 index 0000000..f34d47c Binary files /dev/null and b/fonts/font_01f4.png differ diff --git a/fonts/font_01f5.png b/fonts/font_01f5.png new file mode 100644 index 0000000..b02a838 Binary files /dev/null and b/fonts/font_01f5.png differ diff --git a/fonts/font_01f6.png b/fonts/font_01f6.png new file mode 100644 index 0000000..2768ba2 Binary files /dev/null and b/fonts/font_01f6.png differ diff --git a/fonts/font_01f7.png b/fonts/font_01f7.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f7.png differ diff --git a/fonts/font_01f8.png b/fonts/font_01f8.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01f8.png differ diff --git a/fonts/font_01f9.png b/fonts/font_01f9.png new file mode 100644 index 0000000..5984f5d Binary files /dev/null and b/fonts/font_01f9.png differ diff --git a/fonts/font_01fa.png b/fonts/font_01fa.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01fa.png differ diff --git a/fonts/font_01fb.png b/fonts/font_01fb.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01fb.png differ diff --git a/fonts/font_01fc.png b/fonts/font_01fc.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01fc.png differ diff --git a/fonts/font_01fd.png b/fonts/font_01fd.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01fd.png differ diff --git a/fonts/font_01fe.png b/fonts/font_01fe.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01fe.png differ diff --git a/fonts/font_01ff.png b/fonts/font_01ff.png new file mode 100644 index 0000000..2e21bfa Binary files /dev/null and b/fonts/font_01ff.png differ diff --git a/fonts/font_1000.png b/fonts/font_1000.png new file mode 100644 index 0000000..5f0a1a6 Binary files /dev/null and b/fonts/font_1000.png differ diff --git a/framebuf.h b/framebuf.h new file mode 100644 index 0000000..acc877f --- /dev/null +++ b/framebuf.h @@ -0,0 +1,154 @@ + +byte fb_init_int(uint *fbo, uint *fbtex, uint *rbo, byte linear) { + uint status; + glGenFramebuffers(1, fbo); + glBindFramebuffer(GL_FRAMEBUFFER, *fbo); + glGenTextures(1, fbtex); + glBindTexture(GL_TEXTURE_2D, *fbtex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *fbtex, 0); + if(rbo) { + glGenRenderbuffers(1, rbo); + glBindRenderbuffer(GL_RENDERBUFFER, *rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 16, 16); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *rbo); + } + if((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { + loge(LOG_GFX, STR_FBO_INCOMPLETE, *fbo, status); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, fbtex); + glDeleteFramebuffers(1, fbo); + if(rbo) { + glDeleteRenderbuffers(1, rbo); + } + return 0; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + gdr.fb_mem += 16 * 16 * 4; + gdr.fb_peak = gdr.fb_mem > gdr.fb_peak ? gdr.fb_mem : gdr.fb_peak; + return 1; +} + +byte fb_init_sys(uint *fbo, uint *fbtex) { + if(fb_init_int(fbo, fbtex, NULL, 0)) { + logd(LOG_GFX, STR_FBO_ADDED, *fbo, *fbtex); + gdr.fb_loaded += 1; + return 1; + } + return 0; +} + +fbo_t *fb_init(uint *fbo, uint *fbtex, uint *rbo, window_t *resize, byte linear) { + fbo_t *bind; + if(!fb_init_int(fbo, fbtex, rbo, linear)) + return NULL; + sys_assert(bind = (fbo_t*)tbl_push(&gdr.framebufs)); + bind->fbo = fbo; + bind->tex = fbtex; + bind->rbo = rbo; + bind->resize = resize; + bind->linear = linear; + bind->xsize = 0; + bind->ysize = 0; + gdr.fb_loaded += 1; + if(rbo) + logd(LOG_GFX, STR_FBO_ADDED_RBO, *fbo, *fbtex, *rbo); + else + logd(LOG_GFX, STR_FBO_ADDED, *fbo, *fbtex); + return bind; +} + +void fb_resize_int(uint fbtex, int px, int py, int x, int y) { + gdr.fb_mem -= px * py * 4; + glBindTexture(GL_TEXTURE_2D, fbtex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gdr.fb_mem += x * y * 4; + gdr.fb_peak = gdr.fb_mem > gdr.fb_peak ? gdr.fb_mem : gdr.fb_peak; +} + +byte fb_resize(fbo_t *buf, window_t *resize) { + if(!(buf->resize) || (buf->resize != resize)) + return 0; + glBindTexture(GL_TEXTURE_2D, *(buf->tex)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, resize->fb_x, resize->fb_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if(buf->rbo) { + glBindRenderbuffer(GL_RENDERBUFFER, *(buf->rbo)); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, resize->fb_x, resize->fb_y); + } + gdr.fb_mem -= buf->xsize * buf->ysize * 4; + gdr.fb_mem += (buf->xsize = resize->fb_x) * (buf->ysize = resize->fb_y) * 4; + gdr.fb_peak = gdr.fb_mem > gdr.fb_peak ? gdr.fb_mem : gdr.fb_peak; + return 1; +} + +int fb_reconfig(window_t *resize) { + int done = 0; + ulong iter = 0; + fbo_t *buf; + while(buf = tbl_iter(&gdr.framebufs, &iter)) { + done += (int)fb_resize(buf, resize); + } + // logd(LOG_GFX, STR_FBO_RESIZE, done); + return done; +} + +void fb_size_int(uint tex, uint rbo, int x, int y) { + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if(rbo) { + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, x, y); + } +} + +byte fb_size(fbo_t *buf, int x, int y) { + fb_size_int(*(buf->tex), buf->rbo ? *(buf->rbo) : 0, x, y); + gdr.fb_mem -= buf->xsize * buf->ysize * 4; + gdr.fb_mem += (buf->xsize = x) * (buf->ysize = y) * 4; + gdr.fb_peak = gdr.fb_mem > gdr.fb_peak ? gdr.fb_mem : gdr.fb_peak; +} + +void fb_delete(uint *fbo, uint *fbtex) { + if(*fbo) { + glDeleteTextures(1, fbtex); + glDeleteFramebuffers(1, fbo); + logd(LOG_GFX, STR_FBO_DELETED, *fbo, *fbtex); + *fbo = 0; + *fbtex = 0; + gdr.fb_loaded -= 1; + } +} + +byte fb_unload(fbo_t *buf) { + if(!(*(buf->fbo))) { + return 0; + } + glDeleteTextures(1, buf->tex); + glDeleteFramebuffers(1, buf->fbo); + if(buf->rbo) { + glDeleteRenderbuffers(1, buf->rbo); + logd(LOG_GFX, STR_FBO_DELETED_RBO, *(buf->fbo), *(buf->tex), *(buf->rbo)); + *(buf->rbo) = 0; + } + else { + logd(LOG_GFX, STR_FBO_DELETED, *(buf->fbo), *(buf->tex)); + } + *(buf->fbo) = 0; + *(buf->tex) = 0; + gdr.fb_loaded -= 1; + return 1; +} + +void fb_remove(fbo_t *buf) { + if(fb_unload(buf)) { + tbl_pop(&gdr.framebufs, buf); + } +} + +int fb_clear() { + return tbl_clear_func(&gdr.framebufs, (clear_func*)fb_unload); +} diff --git a/gl_constants.h b/gl_constants.h new file mode 100644 index 0000000..a26eeb5 --- /dev/null +++ b/gl_constants.h @@ -0,0 +1,1381 @@ + +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DOUBLE 0x140A +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFF +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_QUADS 0x0007 +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_DISPLAY_LIST 0x82E7 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_MINMAX 0x802E +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED diff --git a/gl_functions.h b/gl_functions.h new file mode 100644 index 0000000..98372c7 --- /dev/null +++ b/gl_functions.h @@ -0,0 +1,718 @@ +// GL_API_1_0 +OGL_DEF1(glCullFace, uint) +OGL_DEF1(glFrontFace, uint) +OGL_DEF2(glHint, uint, uint) +OGL_DEF1(glLineWidth, float) +OGL_DEF1(glPointSize, float) +OGL_DEF2(glPolygonMode, uint, uint) +OGL_DEF4(glScissor, int, int, int, int) +OGL_DEF3(glTexParameterf, uint, uint, float) +OGL_DEF3(glTexParameterfv, uint, uint, const float *) +OGL_DEF3(glTexParameteri, uint, uint, int) +OGL_DEF3(glTexParameteriv, uint, uint, const int *) +OGL_DEF8(glTexImage1D, uint, int, int, int, int, uint, uint, const void *) +OGL_DEF9(glTexImage2D, uint, int, int, int, int, int, uint, uint, const void *) +OGL_DEF1(glDrawBuffer, uint) +OGL_DEF1(glClear, uint) +OGL_DEF4(glClearColor, float, float, float, float) +OGL_DEF1(glClearStencil, int) +OGL_DEF1(glClearDepth, double) +OGL_DEF1(glStencilMask, uint) +OGL_DEF4(glColorMask, byte, byte, byte, byte) +OGL_DEF1(glDepthMask, byte) +OGL_DEF1(glDisable, uint) +OGL_DEF1(glEnable, uint) +OGL_DEF(glFinish) +OGL_DEF(glFlush) +OGL_DEF2(glBlendFunc, uint, uint) +OGL_DEF1(glLogicOp, uint) +OGL_DEF3(glStencilFunc, uint, int, uint) +OGL_DEF3(glStencilOp, uint, uint, uint) +OGL_DEF1(glDepthFunc, uint) +OGL_DEF2(glPixelStoref, uint, float) +OGL_DEF2(glPixelStorei, uint, int) +OGL_DEF1(glReadBuffer, uint) +OGL_DEF7(glReadPixels, int, int, int, int, uint, uint, void *) +OGL_DEF2(glGetBooleanv, uint, byte *) +OGL_DEF2(glGetDoublev, uint, double *) +OGL_DEFR(uint, glGetError) +OGL_DEF2(glGetFloatv, uint, float *) +OGL_DEF2(glGetIntegerv, uint, int *) +OGL_DEFR1(const byte *, glGetString, uint) +OGL_DEF5(glGetTexImage, uint, int, uint, uint, void *) +OGL_DEF3(glGetTexParameterfv, uint, uint, float *) +OGL_DEF3(glGetTexParameteriv, uint, uint, int *) +OGL_DEF4(glGetTexLevelParameterfv, uint, int, uint, float *) +OGL_DEF4(glGetTexLevelParameteriv, uint, int, uint, int *) +OGL_DEFR1(byte, glIsEnabled, uint) +OGL_DEF2(glDepthRange, double, double) +OGL_DEF4(glViewport, int, int, int, int) +// GL_API_1_1 +OGL_DEF3(glDrawArrays, uint, int, int) +OGL_DEF4(glDrawElements, uint, int, uint, const void *) +OGL_DEF2(glPolygonOffset, float, float) +OGL_DEF7(glCopyTexImage1D, uint, int, uint, int, int, int, int) +OGL_DEF8(glCopyTexImage2D, uint, int, uint, int, int, int, int, int) +OGL_DEF6(glCopyTexSubImage1D, uint, int, int, int, int, int) +OGL_DEF8(glCopyTexSubImage2D, uint, int, int, int, int, int, int, int) +OGL_DEF7(glTexSubImage1D, uint, int, int, int, uint, uint, const void *) +OGL_DEF9(glTexSubImage2D, uint, int, int, int, int, int, uint, uint, const void *) +OGL_DEF2(glBindTexture, uint, uint) +OGL_DEF2(glDeleteTextures, int, const uint *) +OGL_DEF2(glGenTextures, int, uint *) +OGL_DEFR1(byte, glIsTexture, uint) +// GL_API_1_2 +OGL_DEF6(glDrawRangeElements, uint, uint, uint, int, uint, const void *) +OGL_DEF10(glTexImage3D, uint, int, int, int, int, int, int, uint, uint, const void *) +OGL_DEF11(glTexSubImage3D, uint, int, int, int, int, int, int, int, uint, uint, const void *) +OGL_DEF9(glCopyTexSubImage3D, uint, int, int, int, int, int, int, int, int) +// GL_API_1_3 +OGL_DEF1(glActiveTexture, uint) +OGL_DEF2(glSampleCoverage, float, byte) +OGL_DEF9(glCompressedTexImage3D, uint, int, uint, int, int, int, int, int, const void *) +OGL_DEF8(glCompressedTexImage2D, uint, int, uint, int, int, int, int, const void *) +OGL_DEF7(glCompressedTexImage1D, uint, int, uint, int, int, int, const void *) +OGL_DEF11(glCompressedTexSubImage3D, uint, int, int, int, int, int, int, int, uint, int, const void *) +OGL_DEF9(glCompressedTexSubImage2D, uint, int, int, int, int, int, uint, int, const void *) +OGL_DEF7(glCompressedTexSubImage1D, uint, int, int, int, uint, int, const void *) +OGL_DEF3(glGetCompressedTexImage, uint, int, void *) +// GL_API_1_4 +OGL_DEF4(glBlendFuncSeparate, uint, uint, uint, uint) +OGL_DEF4(glMultiDrawArrays, uint, const int *, const int *, int) +OGL_DEF5(glMultiDrawElements, uint, const int *, uint, const void *const*, int) +OGL_DEF2(glPointParameterf, uint, float) +OGL_DEF2(glPointParameterfv, uint, const float *) +OGL_DEF2(glPointParameteri, uint, int) +OGL_DEF2(glPointParameteriv, uint, const int *) +OGL_DEF4(glBlendColor, float, float, float, float) +OGL_DEF1(glBlendEquation, uint) +// GL_API_1_5 +OGL_DEF2(glGenQueries, int, uint *) +OGL_DEF2(glDeleteQueries, int, const uint *) +OGL_DEFR1(byte, glIsQuery, uint) +OGL_DEF2(glBeginQuery, uint, uint) +OGL_DEF1(glEndQuery, uint) +OGL_DEF3(glGetQueryiv, uint, uint, int *) +OGL_DEF3(glGetQueryObjectiv, uint, uint, int *) +OGL_DEF3(glGetQueryObjectuiv, uint, uint, uint *) +OGL_DEF2(glBindBuffer, uint, uint) +OGL_DEF2(glDeleteBuffers, int, const uint *) +OGL_DEF2(glGenBuffers, int, uint *) +OGL_DEFR1(byte, glIsBuffer, uint) +OGL_DEF4(glBufferData, uint, ssize, const void *, uint) +OGL_DEF4(glBufferSubData, uint, iptr, ssize, const void *) +OGL_DEF4(glGetBufferSubData, uint, iptr, ssize, void *) +OGL_DEFR2(void *, glMapBuffer, uint, uint) +OGL_DEFR1(byte, glUnmapBuffer, uint) +OGL_DEF3(glGetBufferParameteriv, uint, uint, int *) +OGL_DEF3(glGetBufferPointerv, uint, uint, void **) +// GL_API_2_0 +OGL_DEF2(glBlendEquationSeparate, uint, uint) +OGL_DEF2(glDrawBuffers, int, const uint *) +OGL_DEF4(glStencilOpSeparate, uint, uint, uint, uint) +OGL_DEF4(glStencilFuncSeparate, uint, uint, int, uint) +OGL_DEF2(glStencilMaskSeparate, uint, uint) +OGL_DEF2(glAttachShader, uint, uint) +OGL_DEF3(glBindAttribLocation, uint, uint, const char *) +OGL_DEF1(glCompileShader, uint) +OGL_DEFR(uint, glCreateProgram) +OGL_DEFR1(uint, glCreateShader, uint) +OGL_DEF1(glDeleteProgram, uint) +OGL_DEF1(glDeleteShader, uint) +OGL_DEF2(glDetachShader, uint, uint) +OGL_DEF1(glDisableVertexAttribArray, uint) +OGL_DEF1(glEnableVertexAttribArray, uint) +OGL_DEF7(glGetActiveAttrib, uint, uint, int, int *, int *, uint *, char *) +OGL_DEF7(glGetActiveUniform, uint, uint, int, int *, int *, uint *, char *) +OGL_DEF4(glGetAttachedShaders, uint, int, int *, uint *) +OGL_DEFR2(int, glGetAttribLocation, uint, const char *) +OGL_DEF3(glGetProgramiv, uint, uint, int *) +OGL_DEF4(glGetProgramInfoLog, uint, int, int *, char *) +OGL_DEF3(glGetShaderiv, uint, uint, int *) +OGL_DEF4(glGetShaderInfoLog, uint, int, int *, char *) +OGL_DEF4(glGetShaderSource, uint, int, int *, char *) +OGL_DEFR2(int, glGetUniformLocation, uint, const char *) +OGL_DEF3(glGetUniformfv, uint, int, float *) +OGL_DEF3(glGetUniformiv, uint, int, int *) +OGL_DEF3(glGetVertexAttribdv, uint, uint, double *) +OGL_DEF3(glGetVertexAttribfv, uint, uint, float *) +OGL_DEF3(glGetVertexAttribiv, uint, uint, int *) +OGL_DEF3(glGetVertexAttribPointerv, uint, uint, void **) +OGL_DEFR1(byte, glIsProgram, uint) +OGL_DEFR1(byte, glIsShader, uint) +OGL_DEF1(glLinkProgram, uint) +OGL_DEF4(glShaderSource, uint, int, const char *const*, const int *) +OGL_DEF1(glUseProgram, uint) +OGL_DEF2(glUniform1f, int, float) +OGL_DEF3(glUniform2f, int, float, float) +OGL_DEF4(glUniform3f, int, float, float, float) +OGL_DEF5(glUniform4f, int, float, float, float, float) +OGL_DEF2(glUniform1i, int, int) +OGL_DEF3(glUniform2i, int, int, int) +OGL_DEF4(glUniform3i, int, int, int, int) +OGL_DEF5(glUniform4i, int, int, int, int, int) +OGL_DEF3(glUniform1fv, int, int, const float *) +OGL_DEF3(glUniform2fv, int, int, const float *) +OGL_DEF3(glUniform3fv, int, int, const float *) +OGL_DEF3(glUniform4fv, int, int, const float *) +OGL_DEF3(glUniform1iv, int, int, const int *) +OGL_DEF3(glUniform2iv, int, int, const int *) +OGL_DEF3(glUniform3iv, int, int, const int *) +OGL_DEF3(glUniform4iv, int, int, const int *) +OGL_DEF4(glUniformMatrix2fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix3fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix4fv, int, int, byte, const float *) +OGL_DEF1(glValidateProgram, uint) +OGL_DEF2(glVertexAttrib1d, uint, double) +OGL_DEF2(glVertexAttrib1dv, uint, const double *) +OGL_DEF2(glVertexAttrib1f, uint, float) +OGL_DEF2(glVertexAttrib1fv, uint, const float *) +OGL_DEF2(glVertexAttrib1s, uint, short) +OGL_DEF2(glVertexAttrib1sv, uint, const short *) +OGL_DEF3(glVertexAttrib2d, uint, double, double) +OGL_DEF2(glVertexAttrib2dv, uint, const double *) +OGL_DEF3(glVertexAttrib2f, uint, float, float) +OGL_DEF2(glVertexAttrib2fv, uint, const float *) +OGL_DEF3(glVertexAttrib2s, uint, short, short) +OGL_DEF2(glVertexAttrib2sv, uint, const short *) +OGL_DEF4(glVertexAttrib3d, uint, double, double, double) +OGL_DEF2(glVertexAttrib3dv, uint, const double *) +OGL_DEF4(glVertexAttrib3f, uint, float, float, float) +OGL_DEF2(glVertexAttrib3fv, uint, const float *) +OGL_DEF4(glVertexAttrib3s, uint, short, short, short) +OGL_DEF2(glVertexAttrib3sv, uint, const short *) +OGL_DEF2(glVertexAttrib4Nbv, uint, const char *) +OGL_DEF2(glVertexAttrib4Niv, uint, const int *) +OGL_DEF2(glVertexAttrib4Nsv, uint, const short *) +OGL_DEF5(glVertexAttrib4Nub, uint, byte, byte, byte, byte) +OGL_DEF2(glVertexAttrib4Nubv, uint, const byte *) +OGL_DEF2(glVertexAttrib4Nuiv, uint, const uint *) +OGL_DEF2(glVertexAttrib4Nusv, uint, const ushort *) +OGL_DEF2(glVertexAttrib4bv, uint, const char *) +OGL_DEF5(glVertexAttrib4d, uint, double, double, double, double) +OGL_DEF2(glVertexAttrib4dv, uint, const double *) +OGL_DEF5(glVertexAttrib4f, uint, float, float, float, float) +OGL_DEF2(glVertexAttrib4fv, uint, const float *) +OGL_DEF2(glVertexAttrib4iv, uint, const int *) +OGL_DEF5(glVertexAttrib4s, uint, short, short, short, short) +OGL_DEF2(glVertexAttrib4sv, uint, const short *) +OGL_DEF2(glVertexAttrib4ubv, uint, const byte *) +OGL_DEF2(glVertexAttrib4uiv, uint, const uint *) +OGL_DEF2(glVertexAttrib4usv, uint, const ushort *) +OGL_DEF6(glVertexAttribPointer, uint, int, uint, byte, int, const void *) +// GL_API_2_1 +OGL_DEF4(glUniformMatrix2x3fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix3x2fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix2x4fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix4x2fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix3x4fv, int, int, byte, const float *) +OGL_DEF4(glUniformMatrix4x3fv, int, int, byte, const float *) +// GL_API_3_0 +OGL_DEF5(glColorMaski, uint, byte, byte, byte, byte) +OGL_DEF3(glGetBooleani_v, uint, uint, byte *) +OGL_DEF3(glGetIntegeri_v, uint, uint, int *) +OGL_DEF2(glEnablei, uint, uint) +OGL_DEF2(glDisablei, uint, uint) +OGL_DEFR2(byte, glIsEnabledi, uint, uint) +OGL_DEF1(glBeginTransformFeedback, uint) +OGL_DEF(glEndTransformFeedback) +OGL_DEF5(glBindBufferRange, uint, uint, uint, iptr, ssize) +OGL_DEF3(glBindBufferBase, uint, uint, uint) +OGL_DEF4(glTransformFeedbackVaryings, uint, int, const char *const*, uint) +OGL_DEF7(glGetTransformFeedbackVarying, uint, uint, int, int *, int *, uint *, char *) +OGL_DEF2(glClampColor, uint, uint) +OGL_DEF2(glBeginConditionalRender, uint, uint) +OGL_DEF(glEndConditionalRender) +OGL_DEF5(glVertexAttribIPointer, uint, int, uint, int, const void *) +OGL_DEF3(glGetVertexAttribIiv, uint, uint, int *) +OGL_DEF3(glGetVertexAttribIuiv, uint, uint, uint *) +OGL_DEF2(glVertexAttribI1i, uint, int) +OGL_DEF3(glVertexAttribI2i, uint, int, int) +OGL_DEF4(glVertexAttribI3i, uint, int, int, int) +OGL_DEF5(glVertexAttribI4i, uint, int, int, int, int) +OGL_DEF2(glVertexAttribI1ui, uint, uint) +OGL_DEF3(glVertexAttribI2ui, uint, uint, uint) +OGL_DEF4(glVertexAttribI3ui, uint, uint, uint, uint) +OGL_DEF5(glVertexAttribI4ui, uint, uint, uint, uint, uint) +OGL_DEF2(glVertexAttribI1iv, uint, const int *) +OGL_DEF2(glVertexAttribI2iv, uint, const int *) +OGL_DEF2(glVertexAttribI3iv, uint, const int *) +OGL_DEF2(glVertexAttribI4iv, uint, const int *) +OGL_DEF2(glVertexAttribI1uiv, uint, const uint *) +OGL_DEF2(glVertexAttribI2uiv, uint, const uint *) +OGL_DEF2(glVertexAttribI3uiv, uint, const uint *) +OGL_DEF2(glVertexAttribI4uiv, uint, const uint *) +OGL_DEF2(glVertexAttribI4bv, uint, const char *) +OGL_DEF2(glVertexAttribI4sv, uint, const short *) +OGL_DEF2(glVertexAttribI4ubv, uint, const byte *) +OGL_DEF2(glVertexAttribI4usv, uint, const ushort *) +OGL_DEF3(glGetUniformuiv, uint, int, uint *) +OGL_DEF3(glBindFragDataLocation, uint, uint, const char *) +OGL_DEFR2(int, glGetFragDataLocation, uint, const char *) +OGL_DEF2(glUniform1ui, int, uint) +OGL_DEF3(glUniform2ui, int, uint, uint) +OGL_DEF4(glUniform3ui, int, uint, uint, uint) +OGL_DEF5(glUniform4ui, int, uint, uint, uint, uint) +OGL_DEF3(glUniform1uiv, int, int, const uint *) +OGL_DEF3(glUniform2uiv, int, int, const uint *) +OGL_DEF3(glUniform3uiv, int, int, const uint *) +OGL_DEF3(glUniform4uiv, int, int, const uint *) +OGL_DEF3(glTexParameterIiv, uint, uint, const int *) +OGL_DEF3(glTexParameterIuiv, uint, uint, const uint *) +OGL_DEF3(glGetTexParameterIiv, uint, uint, int *) +OGL_DEF3(glGetTexParameterIuiv, uint, uint, uint *) +OGL_DEF3(glClearBufferiv, uint, int, const int *) +OGL_DEF3(glClearBufferuiv, uint, int, const uint *) +OGL_DEF3(glClearBufferfv, uint, int, const float *) +OGL_DEF4(glClearBufferfi, uint, int, float, int) +OGL_DEFR2(const byte *, glGetStringi, uint, uint) +OGL_DEFR1(byte, glIsRenderbuffer, uint) +OGL_DEF2(glBindRenderbuffer, uint, uint) +OGL_DEF2(glDeleteRenderbuffers, int, const uint *) +OGL_DEF2(glGenRenderbuffers, int, uint *) +OGL_DEF4(glRenderbufferStorage, uint, uint, int, int) +OGL_DEF3(glGetRenderbufferParameteriv, uint, uint, int *) +OGL_DEFR1(byte, glIsFramebuffer, uint) +OGL_DEF2(glBindFramebuffer, uint, uint) +OGL_DEF2(glDeleteFramebuffers, int, const uint *) +OGL_DEF2(glGenFramebuffers, int, uint *) +OGL_DEFR1(uint, glCheckFramebufferStatus, uint) +OGL_DEF5(glFramebufferTexture1D, uint, uint, uint, uint, int) +OGL_DEF5(glFramebufferTexture2D, uint, uint, uint, uint, int) +OGL_DEF6(glFramebufferTexture3D, uint, uint, uint, uint, int, int) +OGL_DEF4(glFramebufferRenderbuffer, uint, uint, uint, uint) +OGL_DEF4(glGetFramebufferAttachmentParameteriv, uint, uint, uint, int *) +OGL_DEF1(glGenerateMipmap, uint) +OGL_DEF10(glBlitFramebuffer, int, int, int, int, int, int, int, int, uint, uint) +OGL_DEF5(glRenderbufferStorageMultisample, uint, int, uint, int, int) +OGL_DEF5(glFramebufferTextureLayer, uint, uint, uint, int, int) +OGL_DEFR4(void *, glMapBufferRange, uint, iptr, ssize, uint) +OGL_DEF3(glFlushMappedBufferRange, uint, iptr, ssize) +OGL_DEF1(glBindVertexArray, uint) +OGL_DEF2(glDeleteVertexArrays, int, const uint *) +OGL_DEF2(glGenVertexArrays, int, uint *) +OGL_DEFR1(byte, glIsVertexArray, uint) +// GL_API_3_1 +OGL_DEF4(glDrawArraysInstanced, uint, int, int, int) +OGL_DEF5(glDrawElementsInstanced, uint, int, uint, const void *, int) +OGL_DEF3(glTexBuffer, uint, uint, uint) +OGL_DEF1(glPrimitiveRestartIndex, uint) +OGL_DEF5(glCopyBufferSubData, uint, uint, iptr, iptr, ssize) +OGL_DEF4(glGetUniformIndices, uint, int, const char *const*, uint *) +OGL_DEF5(glGetActiveUniformsiv, uint, int, const uint *, uint, int *) +OGL_DEF5(glGetActiveUniformName, uint, uint, int, int *, char *) +OGL_DEFR2(uint, glGetUniformBlockIndex, uint, const char *) +OGL_DEF4(glGetActiveUniformBlockiv, uint, uint, uint, int *) +OGL_DEF5(glGetActiveUniformBlockName, uint, uint, int, int *, char *) +OGL_DEF3(glUniformBlockBinding, uint, uint, uint) +// GL_API_3_2 +OGL_DEF5(glDrawElementsBaseVertex, uint, int, uint, const void *, int) +OGL_DEF7(glDrawRangeElementsBaseVertex, uint, uint, uint, int, uint, const void *, int) +OGL_DEF6(glDrawElementsInstancedBaseVertex, uint, int, uint, const void *, int, int) +OGL_DEF6(glMultiDrawElementsBaseVertex, uint, const int *, uint, const void *const*, int, const int *) +OGL_DEF1(glProvokingVertex, uint) +OGL_DEFR2(sync_t, glFenceSync, uint, uint) +OGL_DEFR1(byte, glIsSync, sync_t) +OGL_DEF1(glDeleteSync, sync_t) +OGL_DEFR3(uint, glClientWaitSync, sync_t, uint, ulong) +OGL_DEF3(glWaitSync, sync_t, uint, ulong) +OGL_DEF2(glGetInteger64v, uint, long *) +OGL_DEF5(glGetSynciv, sync_t, uint, int, int *, int *) +OGL_DEF3(glGetInteger64i_v, uint, uint, long *) +OGL_DEF3(glGetBufferParameteri64v, uint, uint, long *) +OGL_DEF4(glFramebufferTexture, uint, uint, uint, int) +OGL_DEF6(glTexImage2DMultisample, uint, int, uint, int, int, byte) +OGL_DEF7(glTexImage3DMultisample, uint, int, uint, int, int, int, byte) +OGL_DEF3(glGetMultisamplefv, uint, uint, float *) +OGL_DEF2(glSampleMaski, uint, uint) +// GL_API_3_3 +OGL_DEF4(glBindFragDataLocationIndexed, uint, uint, uint, const char *) +OGL_DEFR2(int, glGetFragDataIndex, uint, const char *) +OGL_DEF2(glGenSamplers, int, uint *) +OGL_DEF2(glDeleteSamplers, int, const uint *) +OGL_DEFR1(byte, glIsSampler, uint) +OGL_DEF2(glBindSampler, uint, uint) +OGL_DEF3(glSamplerParameteri, uint, uint, int) +OGL_DEF3(glSamplerParameteriv, uint, uint, const int *) +OGL_DEF3(glSamplerParameterf, uint, uint, float) +OGL_DEF3(glSamplerParameterfv, uint, uint, const float *) +OGL_DEF3(glSamplerParameterIiv, uint, uint, const int *) +OGL_DEF3(glSamplerParameterIuiv, uint, uint, const uint *) +OGL_DEF3(glGetSamplerParameteriv, uint, uint, int *) +OGL_DEF3(glGetSamplerParameterIiv, uint, uint, int *) +OGL_DEF3(glGetSamplerParameterfv, uint, uint, float *) +OGL_DEF3(glGetSamplerParameterIuiv, uint, uint, uint *) +OGL_DEF2(glQueryCounter, uint, uint) +OGL_DEF3(glGetQueryObjecti64v, uint, uint, long *) +OGL_DEF3(glGetQueryObjectui64v, uint, uint, ulong *) +OGL_DEF2(glVertexAttribDivisor, uint, uint) +OGL_DEF4(glVertexAttribP1ui, uint, uint, byte, uint) +OGL_DEF4(glVertexAttribP1uiv, uint, uint, byte, const uint *) +OGL_DEF4(glVertexAttribP2ui, uint, uint, byte, uint) +OGL_DEF4(glVertexAttribP2uiv, uint, uint, byte, const uint *) +OGL_DEF4(glVertexAttribP3ui, uint, uint, byte, uint) +OGL_DEF4(glVertexAttribP3uiv, uint, uint, byte, const uint *) +OGL_DEF4(glVertexAttribP4ui, uint, uint, byte, uint) +OGL_DEF4(glVertexAttribP4uiv, uint, uint, byte, const uint *) +OGL_DEF2(glVertexP2ui, uint, uint) +OGL_DEF2(glVertexP2uiv, uint, const uint *) +OGL_DEF2(glVertexP3ui, uint, uint) +OGL_DEF2(glVertexP3uiv, uint, const uint *) +OGL_DEF2(glVertexP4ui, uint, uint) +OGL_DEF2(glVertexP4uiv, uint, const uint *) +OGL_DEF2(glTexCoordP1ui, uint, uint) +OGL_DEF2(glTexCoordP1uiv, uint, const uint *) +OGL_DEF2(glTexCoordP2ui, uint, uint) +OGL_DEF2(glTexCoordP2uiv, uint, const uint *) +OGL_DEF2(glTexCoordP3ui, uint, uint) +OGL_DEF2(glTexCoordP3uiv, uint, const uint *) +OGL_DEF2(glTexCoordP4ui, uint, uint) +OGL_DEF2(glTexCoordP4uiv, uint, const uint *) +OGL_DEF3(glMultiTexCoordP1ui, uint, uint, uint) +OGL_DEF3(glMultiTexCoordP1uiv, uint, uint, const uint *) +OGL_DEF3(glMultiTexCoordP2ui, uint, uint, uint) +OGL_DEF3(glMultiTexCoordP2uiv, uint, uint, const uint *) +OGL_DEF3(glMultiTexCoordP3ui, uint, uint, uint) +OGL_DEF3(glMultiTexCoordP3uiv, uint, uint, const uint *) +OGL_DEF3(glMultiTexCoordP4ui, uint, uint, uint) +OGL_DEF3(glMultiTexCoordP4uiv, uint, uint, const uint *) +OGL_DEF2(glNormalP3ui, uint, uint) +OGL_DEF2(glNormalP3uiv, uint, const uint *) +OGL_DEF2(glColorP3ui, uint, uint) +OGL_DEF2(glColorP3uiv, uint, const uint *) +OGL_DEF2(glColorP4ui, uint, uint) +OGL_DEF2(glColorP4uiv, uint, const uint *) +OGL_DEF2(glSecondaryColorP3ui, uint, uint) +OGL_DEF2(glSecondaryColorP3uiv, uint, const uint *) +// GL_API_4_0 +OGL_DEF1(glMinSampleShading, float) +OGL_DEF2(glBlendEquationi, uint, uint) +OGL_DEF3(glBlendEquationSeparatei, uint, uint, uint) +OGL_DEF3(glBlendFunci, uint, uint, uint) +OGL_DEF5(glBlendFuncSeparatei, uint, uint, uint, uint, uint) +OGL_DEF2(glDrawArraysIndirect, uint, const void *) +OGL_DEF3(glDrawElementsIndirect, uint, uint, const void *) +OGL_DEF2(glUniform1d, int, double) +OGL_DEF3(glUniform2d, int, double, double) +OGL_DEF4(glUniform3d, int, double, double, double) +OGL_DEF5(glUniform4d, int, double, double, double, double) +OGL_DEF3(glUniform1dv, int, int, const double *) +OGL_DEF3(glUniform2dv, int, int, const double *) +OGL_DEF3(glUniform3dv, int, int, const double *) +OGL_DEF3(glUniform4dv, int, int, const double *) +OGL_DEF4(glUniformMatrix2dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix3dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix4dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix2x3dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix2x4dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix3x2dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix3x4dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix4x2dv, int, int, byte, const double *) +OGL_DEF4(glUniformMatrix4x3dv, int, int, byte, const double *) +OGL_DEF3(glGetUniformdv, uint, int, double *) +OGL_DEFR3(int, glGetSubroutineUniformLocation, uint, uint, const char *) +OGL_DEFR3(uint, glGetSubroutineIndex, uint, uint, const char *) +OGL_DEF5(glGetActiveSubroutineUniformiv, uint, uint, uint, uint, int *) +OGL_DEF6(glGetActiveSubroutineUniformName, uint, uint, uint, int, int *, char *) +OGL_DEF6(glGetActiveSubroutineName, uint, uint, uint, int, int *, char *) +OGL_DEF3(glUniformSubroutinesuiv, uint, int, const uint *) +OGL_DEF3(glGetUniformSubroutineuiv, uint, int, uint *) +OGL_DEF4(glGetProgramStageiv, uint, uint, uint, int *) +OGL_DEF2(glPatchParameteri, uint, int) +OGL_DEF2(glPatchParameterfv, uint, const float *) +OGL_DEF2(glBindTransformFeedback, uint, uint) +OGL_DEF2(glDeleteTransformFeedbacks, int, const uint *) +OGL_DEF2(glGenTransformFeedbacks, int, uint *) +OGL_DEFR1(byte, glIsTransformFeedback, uint) +OGL_DEF(glPauseTransformFeedback) +OGL_DEF(glResumeTransformFeedback) +OGL_DEF2(glDrawTransformFeedback, uint, uint) +OGL_DEF3(glDrawTransformFeedbackStream, uint, uint, uint) +OGL_DEF3(glBeginQueryIndexed, uint, uint, uint) +OGL_DEF2(glEndQueryIndexed, uint, uint) +OGL_DEF4(glGetQueryIndexediv, uint, uint, uint, int *) +// GL_API_4_1 +OGL_DEF(glReleaseShaderCompiler) +OGL_DEF5(glShaderBinary, int, const uint *, uint, const void *, int) +OGL_DEF4(glGetShaderPrecisionFormat, uint, uint, int *, int *) +OGL_DEF2(glDepthRangef, float, float) +OGL_DEF1(glClearDepthf, float) +OGL_DEF5(glGetProgramBinary, uint, int, int *, uint *, void *) +OGL_DEF4(glProgramBinary, uint, uint, const void *, int) +OGL_DEF3(glProgramParameteri, uint, uint, int) +OGL_DEF3(glUseProgramStages, uint, uint, uint) +OGL_DEF2(glActiveShaderProgram, uint, uint) +OGL_DEFR3(uint, glCreateShaderProgramv, uint, int, const char *const*) +OGL_DEF1(glBindProgramPipeline, uint) +OGL_DEF2(glDeleteProgramPipelines, int, const uint *) +OGL_DEF2(glGenProgramPipelines, int, uint *) +OGL_DEFR1(byte, glIsProgramPipeline, uint) +OGL_DEF3(glGetProgramPipelineiv, uint, uint, int *) +OGL_DEF3(glProgramUniform1i, uint, int, int) +OGL_DEF4(glProgramUniform1iv, uint, int, int, const int *) +OGL_DEF3(glProgramUniform1f, uint, int, float) +OGL_DEF4(glProgramUniform1fv, uint, int, int, const float *) +OGL_DEF3(glProgramUniform1d, uint, int, double) +OGL_DEF4(glProgramUniform1dv, uint, int, int, const double *) +OGL_DEF3(glProgramUniform1ui, uint, int, uint) +OGL_DEF4(glProgramUniform1uiv, uint, int, int, const uint *) +OGL_DEF4(glProgramUniform2i, uint, int, int, int) +OGL_DEF4(glProgramUniform2iv, uint, int, int, const int *) +OGL_DEF4(glProgramUniform2f, uint, int, float, float) +OGL_DEF4(glProgramUniform2fv, uint, int, int, const float *) +OGL_DEF4(glProgramUniform2d, uint, int, double, double) +OGL_DEF4(glProgramUniform2dv, uint, int, int, const double *) +OGL_DEF4(glProgramUniform2ui, uint, int, uint, uint) +OGL_DEF4(glProgramUniform2uiv, uint, int, int, const uint *) +OGL_DEF5(glProgramUniform3i, uint, int, int, int, int) +OGL_DEF4(glProgramUniform3iv, uint, int, int, const int *) +OGL_DEF5(glProgramUniform3f, uint, int, float, float, float) +OGL_DEF4(glProgramUniform3fv, uint, int, int, const float *) +OGL_DEF5(glProgramUniform3d, uint, int, double, double, double) +OGL_DEF4(glProgramUniform3dv, uint, int, int, const double *) +OGL_DEF5(glProgramUniform3ui, uint, int, uint, uint, uint) +OGL_DEF4(glProgramUniform3uiv, uint, int, int, const uint *) +OGL_DEF6(glProgramUniform4i, uint, int, int, int, int, int) +OGL_DEF4(glProgramUniform4iv, uint, int, int, const int *) +OGL_DEF6(glProgramUniform4f, uint, int, float, float, float, float) +OGL_DEF4(glProgramUniform4fv, uint, int, int, const float *) +OGL_DEF6(glProgramUniform4d, uint, int, double, double, double, double) +OGL_DEF4(glProgramUniform4dv, uint, int, int, const double *) +OGL_DEF6(glProgramUniform4ui, uint, int, uint, uint, uint, uint) +OGL_DEF4(glProgramUniform4uiv, uint, int, int, const uint *) +OGL_DEF5(glProgramUniformMatrix2fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix3fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix4fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix2dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix3dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix4dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix2x3fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix3x2fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix2x4fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix4x2fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix3x4fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix4x3fv, uint, int, int, byte, const float *) +OGL_DEF5(glProgramUniformMatrix2x3dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix3x2dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix2x4dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix4x2dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix3x4dv, uint, int, int, byte, const double *) +OGL_DEF5(glProgramUniformMatrix4x3dv, uint, int, int, byte, const double *) +OGL_DEF1(glValidateProgramPipeline, uint) +OGL_DEF4(glGetProgramPipelineInfoLog, uint, int, int *, char *) +OGL_DEF2(glVertexAttribL1d, uint, double) +OGL_DEF3(glVertexAttribL2d, uint, double, double) +OGL_DEF4(glVertexAttribL3d, uint, double, double, double) +OGL_DEF5(glVertexAttribL4d, uint, double, double, double, double) +OGL_DEF2(glVertexAttribL1dv, uint, const double *) +OGL_DEF2(glVertexAttribL2dv, uint, const double *) +OGL_DEF2(glVertexAttribL3dv, uint, const double *) +OGL_DEF2(glVertexAttribL4dv, uint, const double *) +OGL_DEF5(glVertexAttribLPointer, uint, int, uint, int, const void *) +OGL_DEF3(glGetVertexAttribLdv, uint, uint, double *) +OGL_DEF3(glViewportArrayv, uint, int, const float *) +OGL_DEF5(glViewportIndexedf, uint, float, float, float, float) +OGL_DEF2(glViewportIndexedfv, uint, const float *) +OGL_DEF3(glScissorArrayv, uint, int, const int *) +OGL_DEF5(glScissorIndexed, uint, int, int, int, int) +OGL_DEF2(glScissorIndexedv, uint, const int *) +OGL_DEF3(glDepthRangeArrayv, uint, int, const double *) +OGL_DEF3(glDepthRangeIndexed, uint, double, double) +OGL_DEF3(glGetFloati_v, uint, uint, float *) +OGL_DEF3(glGetDoublei_v, uint, uint, double *) +// GL_API_4_2 +OGL_DEF5(glDrawArraysInstancedBaseInstance, uint, int, int, int, uint) +OGL_DEF6(glDrawElementsInstancedBaseInstance, uint, int, uint, const void *, int, uint) +OGL_DEF7(glDrawElementsInstancedBaseVertexBaseInstance, uint, int, uint, const void *, int, int, uint) +OGL_DEF5(glGetInternalformativ, uint, uint, uint, int, int *) +OGL_DEF4(glGetActiveAtomicCounterBufferiv, uint, uint, uint, int *) +OGL_DEF7(glBindImageTexture, uint, uint, int, byte, int, uint, uint) +OGL_DEF1(glMemoryBarrier, uint) +OGL_DEF4(glTexStorage1D, uint, int, uint, int) +OGL_DEF5(glTexStorage2D, uint, int, uint, int, int) +OGL_DEF6(glTexStorage3D, uint, int, uint, int, int, int) +OGL_DEF3(glDrawTransformFeedbackInstanced, uint, uint, int) +OGL_DEF4(glDrawTransformFeedbackStreamInstanced, uint, uint, uint, int) +// GL_API_4_3 +OGL_DEF5(glClearBufferData, uint, uint, uint, uint, const void *) +OGL_DEF7(glClearBufferSubData, uint, uint, iptr, ssize, uint, uint, const void *) +OGL_DEF3(glDispatchCompute, uint, uint, uint) +OGL_DEF1(glDispatchComputeIndirect, iptr) +OGL_DEF15(glCopyImageSubData, uint, uint, int, int, int, int, uint, uint, int, int, int, int, int, int, int) +OGL_DEF3(glFramebufferParameteri, uint, uint, int) +OGL_DEF3(glGetFramebufferParameteriv, uint, uint, int *) +OGL_DEF5(glGetInternalformati64v, uint, uint, uint, int, long *) +OGL_DEF8(glInvalidateTexSubImage, uint, int, int, int, int, int, int, int) +OGL_DEF2(glInvalidateTexImage, uint, int) +OGL_DEF3(glInvalidateBufferSubData, uint, iptr, ssize) +OGL_DEF1(glInvalidateBufferData, uint) +OGL_DEF3(glInvalidateFramebuffer, uint, int, const uint *) +OGL_DEF7(glInvalidateSubFramebuffer, uint, int, const uint *, int, int, int, int) +OGL_DEF4(glMultiDrawArraysIndirect, uint, const void *, int, int) +OGL_DEF5(glMultiDrawElementsIndirect, uint, uint, const void *, int, int) +OGL_DEF4(glGetProgramInterfaceiv, uint, uint, uint, int *) +OGL_DEFR3(uint, glGetProgramResourceIndex, uint, uint, const char *) +OGL_DEF6(glGetProgramResourceName, uint, uint, uint, int, int *, char *) +OGL_DEF8(glGetProgramResourceiv, uint, uint, uint, int, const uint *, int, int *, int *) +OGL_DEFR3(int, glGetProgramResourceLocation, uint, uint, const char *) +OGL_DEFR3(int, glGetProgramResourceLocationIndex, uint, uint, const char *) +OGL_DEF3(glShaderStorageBlockBinding, uint, uint, uint) +OGL_DEF5(glTexBufferRange, uint, uint, uint, iptr, ssize) +OGL_DEF6(glTexStorage2DMultisample, uint, int, uint, int, int, byte) +OGL_DEF7(glTexStorage3DMultisample, uint, int, uint, int, int, int, byte) +OGL_DEF8(glTextureView, uint, uint, uint, uint, uint, uint, uint, uint) +OGL_DEF4(glBindVertexBuffer, uint, uint, iptr, int) +OGL_DEF5(glVertexAttribFormat, uint, int, uint, byte, uint) +OGL_DEF4(glVertexAttribIFormat, uint, int, uint, uint) +OGL_DEF4(glVertexAttribLFormat, uint, int, uint, uint) +OGL_DEF2(glVertexAttribBinding, uint, uint) +OGL_DEF2(glVertexBindingDivisor, uint, uint) +OGL_DEF6(glDebugMessageControl, uint, uint, uint, int, const uint *, byte) +OGL_DEF6(glDebugMessageInsert, uint, uint, uint, uint, int, const char *) +OGL_DEF2(glDebugMessageCallback, gldebug_t, const void *) +OGL_DEFR8(uint, glGetDebugMessageLog, uint, int, uint *, uint *, uint *, uint *, int *, char *) +OGL_DEF4(glPushDebugGroup, uint, uint, int, const char *) +OGL_DEF(glPopDebugGroup) +OGL_DEF4(glObjectLabel, uint, uint, int, const char *) +OGL_DEF5(glGetObjectLabel, uint, uint, int, int *, char *) +OGL_DEF3(glObjectPtrLabel, const void *, int, const char *) +OGL_DEF4(glGetObjectPtrLabel, const void *, int, int *, char *) +OGL_DEF2(glGetPointerv, uint, void **) +// GL_API_4_4 +OGL_DEF4(glBufferStorage, uint, ssize, const void *, uint) +OGL_DEF5(glClearTexImage, uint, int, uint, uint, const void *) +OGL_DEF11(glClearTexSubImage, uint, int, int, int, int, int, int, int, uint, uint, const void *) +OGL_DEF4(glBindBuffersBase, uint, uint, int, const uint *) +OGL_DEF6(glBindBuffersRange, uint, uint, int, const uint *, const iptr *, const ssize *) +OGL_DEF3(glBindTextures, uint, int, const uint *) +OGL_DEF3(glBindSamplers, uint, int, const uint *) +OGL_DEF3(glBindImageTextures, uint, int, const uint *) +OGL_DEF5(glBindVertexBuffers, uint, int, const uint *, const iptr *, const int *) +// GL_API_4_5 +OGL_DEF2(glClipControl, uint, uint) +OGL_DEF2(glCreateTransformFeedbacks, int, uint *) +OGL_DEF3(glTransformFeedbackBufferBase, uint, uint, uint) +OGL_DEF5(glTransformFeedbackBufferRange, uint, uint, uint, iptr, ssize) +OGL_DEF3(glGetTransformFeedbackiv, uint, uint, int *) +OGL_DEF4(glGetTransformFeedbacki_v, uint, uint, uint, int *) +OGL_DEF4(glGetTransformFeedbacki64_v, uint, uint, uint, long *) +OGL_DEF2(glCreateBuffers, int, uint *) +OGL_DEF4(glNamedBufferStorage, uint, ssize, const void *, uint) +OGL_DEF4(glNamedBufferData, uint, ssize, const void *, uint) +OGL_DEF4(glNamedBufferSubData, uint, iptr, ssize, const void *) +OGL_DEF5(glCopyNamedBufferSubData, uint, uint, iptr, iptr, ssize) +OGL_DEF5(glClearNamedBufferData, uint, uint, uint, uint, const void *) +OGL_DEF7(glClearNamedBufferSubData, uint, uint, iptr, ssize, uint, uint, const void *) +OGL_DEFR2(void *, glMapNamedBuffer, uint, uint) +OGL_DEFR4(void *, glMapNamedBufferRange, uint, iptr, ssize, uint) +OGL_DEFR1(byte, glUnmapNamedBuffer, uint) +OGL_DEF3(glFlushMappedNamedBufferRange, uint, iptr, ssize) +OGL_DEF3(glGetNamedBufferParameteriv, uint, uint, int *) +OGL_DEF3(glGetNamedBufferParameteri64v, uint, uint, long *) +OGL_DEF3(glGetNamedBufferPointerv, uint, uint, void **) +OGL_DEF4(glGetNamedBufferSubData, uint, iptr, ssize, void *) +OGL_DEF2(glCreateFramebuffers, int, uint *) +OGL_DEF4(glNamedFramebufferRenderbuffer, uint, uint, uint, uint) +OGL_DEF3(glNamedFramebufferParameteri, uint, uint, int) +OGL_DEF4(glNamedFramebufferTexture, uint, uint, uint, int) +OGL_DEF5(glNamedFramebufferTextureLayer, uint, uint, uint, int, int) +OGL_DEF2(glNamedFramebufferDrawBuffer, uint, uint) +OGL_DEF3(glNamedFramebufferDrawBuffers, uint, int, const uint *) +OGL_DEF2(glNamedFramebufferReadBuffer, uint, uint) +OGL_DEF3(glInvalidateNamedFramebufferData, uint, int, const uint *) +OGL_DEF7(glInvalidateNamedFramebufferSubData, uint, int, const uint *, int, int, int, int) +OGL_DEF4(glClearNamedFramebufferiv, uint, uint, int, const int *) +OGL_DEF4(glClearNamedFramebufferuiv, uint, uint, int, const uint *) +OGL_DEF4(glClearNamedFramebufferfv, uint, uint, int, const float *) +OGL_DEF5(glClearNamedFramebufferfi, uint, uint, int, float, int) +OGL_DEF12(glBlitNamedFramebuffer, uint, uint, int, int, int, int, int, int, int, int, uint, uint) +OGL_DEFR2(uint, glCheckNamedFramebufferStatus, uint, uint) +OGL_DEF3(glGetNamedFramebufferParameteriv, uint, uint, int *) +OGL_DEF4(glGetNamedFramebufferAttachmentParameteriv, uint, uint, uint, int *) +OGL_DEF2(glCreateRenderbuffers, int, uint *) +OGL_DEF4(glNamedRenderbufferStorage, uint, uint, int, int) +OGL_DEF5(glNamedRenderbufferStorageMultisample, uint, int, uint, int, int) +OGL_DEF3(glGetNamedRenderbufferParameteriv, uint, uint, int *) +OGL_DEF3(glCreateTextures, uint, int, uint *) +OGL_DEF3(glTextureBuffer, uint, uint, uint) +OGL_DEF5(glTextureBufferRange, uint, uint, uint, iptr, ssize) +OGL_DEF4(glTextureStorage1D, uint, int, uint, int) +OGL_DEF5(glTextureStorage2D, uint, int, uint, int, int) +OGL_DEF6(glTextureStorage3D, uint, int, uint, int, int, int) +OGL_DEF6(glTextureStorage2DMultisample, uint, int, uint, int, int, byte) +OGL_DEF7(glTextureStorage3DMultisample, uint, int, uint, int, int, int, byte) +OGL_DEF7(glTextureSubImage1D, uint, int, int, int, uint, uint, const void *) +OGL_DEF9(glTextureSubImage2D, uint, int, int, int, int, int, uint, uint, const void *) +OGL_DEF11(glTextureSubImage3D, uint, int, int, int, int, int, int, int, uint, uint, const void *) +OGL_DEF7(glCompressedTextureSubImage1D, uint, int, int, int, uint, int, const void *) +OGL_DEF9(glCompressedTextureSubImage2D, uint, int, int, int, int, int, uint, int, const void *) +OGL_DEF11(glCompressedTextureSubImage3D, uint, int, int, int, int, int, int, int, uint, int, const void *) +OGL_DEF6(glCopyTextureSubImage1D, uint, int, int, int, int, int) +OGL_DEF8(glCopyTextureSubImage2D, uint, int, int, int, int, int, int, int) +OGL_DEF9(glCopyTextureSubImage3D, uint, int, int, int, int, int, int, int, int) +OGL_DEF3(glTextureParameterf, uint, uint, float) +OGL_DEF3(glTextureParameterfv, uint, uint, const float *) +OGL_DEF3(glTextureParameteri, uint, uint, int) +OGL_DEF3(glTextureParameterIiv, uint, uint, const int *) +OGL_DEF3(glTextureParameterIuiv, uint, uint, const uint *) +OGL_DEF3(glTextureParameteriv, uint, uint, const int *) +OGL_DEF1(glGenerateTextureMipmap, uint) +OGL_DEF2(glBindTextureUnit, uint, uint) +OGL_DEF6(glGetTextureImage, uint, int, uint, uint, int, void *) +OGL_DEF4(glGetCompressedTextureImage, uint, int, int, void *) +OGL_DEF4(glGetTextureLevelParameterfv, uint, int, uint, float *) +OGL_DEF4(glGetTextureLevelParameteriv, uint, int, uint, int *) +OGL_DEF3(glGetTextureParameterfv, uint, uint, float *) +OGL_DEF3(glGetTextureParameterIiv, uint, uint, int *) +OGL_DEF3(glGetTextureParameterIuiv, uint, uint, uint *) +OGL_DEF3(glGetTextureParameteriv, uint, uint, int *) +OGL_DEF2(glCreateVertexArrays, int, uint *) +OGL_DEF2(glDisableVertexArrayAttrib, uint, uint) +OGL_DEF2(glEnableVertexArrayAttrib, uint, uint) +OGL_DEF2(glVertexArrayElementBuffer, uint, uint) +OGL_DEF5(glVertexArrayVertexBuffer, uint, uint, uint, iptr, int) +OGL_DEF6(glVertexArrayVertexBuffers, uint, uint, int, const uint *, const iptr *, const int *) +OGL_DEF3(glVertexArrayAttribBinding, uint, uint, uint) +OGL_DEF6(glVertexArrayAttribFormat, uint, uint, int, uint, byte, uint) +OGL_DEF5(glVertexArrayAttribIFormat, uint, uint, int, uint, uint) +OGL_DEF5(glVertexArrayAttribLFormat, uint, uint, int, uint, uint) +OGL_DEF3(glVertexArrayBindingDivisor, uint, uint, uint) +OGL_DEF3(glGetVertexArrayiv, uint, uint, int *) +OGL_DEF4(glGetVertexArrayIndexediv, uint, uint, uint, int *) +OGL_DEF4(glGetVertexArrayIndexed64iv, uint, uint, uint, long *) +OGL_DEF2(glCreateSamplers, int, uint *) +OGL_DEF2(glCreateProgramPipelines, int, uint *) +OGL_DEF3(glCreateQueries, uint, int, uint *) +OGL_DEF4(glGetQueryBufferObjecti64v, uint, uint, uint, iptr) +OGL_DEF4(glGetQueryBufferObjectiv, uint, uint, uint, iptr) +OGL_DEF4(glGetQueryBufferObjectui64v, uint, uint, uint, iptr) +OGL_DEF4(glGetQueryBufferObjectuiv, uint, uint, uint, iptr) +OGL_DEF1(glMemoryBarrierByRegion, uint) +OGL_DEF12(glGetTextureSubImage, uint, int, int, int, int, int, int, int, uint, uint, int, void *) +OGL_DEF10(glGetCompressedTextureSubImage, uint, int, int, int, int, int, int, int, int, void *) +OGL_DEFR(uint, glGetGraphicsResetStatus) +OGL_DEF4(glGetnCompressedTexImage, uint, int, int, void *) +OGL_DEF6(glGetnTexImage, uint, int, uint, uint, int, void *) +OGL_DEF4(glGetnUniformdv, uint, int, int, double *) +OGL_DEF4(glGetnUniformfv, uint, int, int, float *) +OGL_DEF4(glGetnUniformiv, uint, int, int, int *) +OGL_DEF4(glGetnUniformuiv, uint, int, int, uint *) +OGL_DEF8(glReadnPixels, int, int, int, int, uint, uint, int, void *) +OGL_DEF4(glGetnMapdv, uint, uint, int, double *) +OGL_DEF4(glGetnMapfv, uint, uint, int, float *) +OGL_DEF4(glGetnMapiv, uint, uint, int, int *) +OGL_DEF3(glGetnPixelMapfv, uint, int, float *) +OGL_DEF3(glGetnPixelMapuiv, uint, int, uint *) +OGL_DEF3(glGetnPixelMapusv, uint, int, ushort *) +OGL_DEF2(glGetnPolygonStipple, int, byte *) +OGL_DEF5(glGetnColorTable, uint, uint, uint, int, void *) +OGL_DEF5(glGetnConvolutionFilter, uint, uint, uint, int, void *) +OGL_DEF8(glGetnSeparableFilter, uint, uint, uint, int, void *, int, void *, void *) +OGL_DEF6(glGetnHistogram, uint, byte, uint, uint, int, void *) +OGL_DEF6(glGetnMinmax, uint, byte, uint, uint, int, void *) +OGL_DEF(glTextureBarrier) +// GL_API_4_6 +OGL_DEF5(glSpecializeShader, uint, const char *, uint, const uint *, const uint *) +OGL_DEF5(glMultiDrawArraysIndirectCount, uint, const void *, iptr, int, int) +OGL_DEF6(glMultiDrawElementsIndirectCount, uint, uint, const void *, iptr, int, int) +OGL_DEF3(glPolygonOffsetClamp, float, float, float) diff --git a/gl_loader.h b/gl_loader.h new file mode 100644 index 0000000..e27e9c1 --- /dev/null +++ b/gl_loader.h @@ -0,0 +1,195 @@ + +#ifdef GLFNC_DEBUG + +void gl_check_error(const char *name); + +#define OGL_NNAME(f) gl_debug_ ## f +#define OGL_DFUNCV(f, t, u) void gl_checked_ ## f t { OGL_NNAME(f)u; gl_check_error(STRINGIZE(f)); } +#define OGL_DFUNCR(r, f, t, u) r gl_checked_ ## f t { r ret; ret = OGL_NNAME(f)u; gl_check_error(STRINGIZE(f)); return ret; } +#else + +void sys_panic(const char *error, const char *fmt, ...); + +#define OGL_DEFINE(r, f, z) r (*OGL_NNAME(f))z; +#define OGL_CHECKPTR(f) sys_assert(OGL_NNAME(f)); logt(LOG_GFX, "%s -> $%llx", STRINGIZE(f), OGL_NNAME(f)); cnt++; +#define OGL_ISETPTR(r, f, z) OGL_NNAME(f) = (r (*)z)(load(STRINGIZE(f))); OGL_CHECKPTR(f) + +#define OGL_NNAME(f) f +#define OGL_DFUNCV(f, t, u) +#define OGL_DFUNCR(r, f, t, u) +#endif + +#ifdef GLFNC_DEBUG +#define OGL_SETPTR(r, f, z) OGL_ISETPTR(r, f, z) f = gl_checked_ ## f; +#else +#define OGL_SETPTR(r, f, z) OGL_ISETPTR(r, f, z) +#endif + +#define OGL_DEF(f) OGL_DEFINE(void, f, ()) \ +OGL_DFUNCV(f, (), ()) +#define OGL_DEFR(r, f) OGL_DEFINE(r, f, ()) \ +OGL_DFUNCR(r, f, (), ()) +#define OGL_DEF1(f, p1) OGL_DEFINE(void, f, (p1)) \ +OGL_DFUNCV(f, (p1 v1), (v1)) +#define OGL_DEFR1(r, f, p1) OGL_DEFINE(r, f, (p1)) \ +OGL_DFUNCR(r, f, (p1 v1), (v1)) +#define OGL_DEF2(f, p1, p2) OGL_DEFINE(void, f, (p1, p2)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2), (v1, v2)) +#define OGL_DEFR2(r, f, p1, p2) OGL_DEFINE(r, f, (p1, p2)) \ +OGL_DFUNCR(r, f, (p1 v1, p2 v2), (v1, v2)) +#define OGL_DEF3(f, p1, p2, p3) OGL_DEFINE(void, f, (p1, p2, p3)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3), (v1, v2, v3)) +#define OGL_DEFR3(r, f, p1, p2, p3) OGL_DEFINE(r, f, (p1, p2, p3)) \ +OGL_DFUNCR(r, f, (p1 v1, p2 v2, p3 v3), (v1, v2, v3)) +#define OGL_DEF4(f, p1, p2, p3, p4) OGL_DEFINE(void, f, (p1, p2, p3, p4)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4), (v1, v2, v3, v4)) +#define OGL_DEFR4(r, f, p1, p2, p3, p4) OGL_DEFINE(r, f, (p1, p2, p3, p4)) \ +OGL_DFUNCR(r, f, (p1 v1, p2 v2, p3 v3, p4 v4), (v1, v2, v3, v4)) +#define OGL_DEF5(f, p1, p2, p3, p4, p5) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5), (v1, v2, v3, v4, v5)) +#define OGL_DEF6(f, p1, p2, p3, p4, p5, p6) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6), (v1, v2, v3, v4, v5, v6)) +#define OGL_DEF7(f, p1, p2, p3, p4, p5, p6, p7) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7), (v1, v2, v3, v4, v5, v6, v7)) +#define OGL_DEF8(f, p1, p2, p3, p4, p5, p6, p7, p8) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8), (v1, v2, v3, v4, v5, v6, v7, v8)) +#define OGL_DEFR8(r, f, p1, p2, p3, p4, p5, p6, p7, p8) OGL_DEFINE(r, f, (p1, p2, p3, p4, p5, p6, p7, p8)) \ +OGL_DFUNCR(r, f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8), (v1, v2, v3, v4, v5, v6, v7, v8)) +#define OGL_DEF9(f, p1, p2, p3, p4, p5, p6, p7, p8, p9) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8, p9 v9), (v1, v2, v3, v4, v5, v6, v7, v8, v9)) +#define OGL_DEF10(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8, p9 v9, p10 v10), (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10)) +#define OGL_DEF11(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8, p9 v9, p10 v10, p11 v11), (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)) +#define OGL_DEF12(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8, p9 v9, p10 v10, p11 v11, p12 v12), (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)) +#define OGL_DEF15(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) OGL_DEFINE(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15)) \ +OGL_DFUNCV(f, (p1 v1, p2 v2, p3 v3, p4 v4, p5 v5, p6 v6, p7 v7, p8 v8, p9 v9, p10 v10, p11 v11, p12 v12, p13 v13, p14 v14, p15 v15), (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + +#include "gl_functions.h" + +#undef OGL_DEF +#undef OGL_DEFR +#undef OGL_DEF1 +#undef OGL_DEFR1 +#undef OGL_DEF2 +#undef OGL_DEFR2 +#undef OGL_DEF3 +#undef OGL_DEFR3 +#undef OGL_DEF4 +#undef OGL_DEFR4 +#undef OGL_DEF5 +#undef OGL_DEF6 +#undef OGL_DEF7 +#undef OGL_DEF8 +#undef OGL_DEFR8 +#undef OGL_DEF9 +#undef OGL_DEF10 +#undef OGL_DEF11 +#undef OGL_DEF12 +#undef OGL_DEF15 + +#ifdef GLFNC_DEBUG + +#define OGL_TESTERROR OGL_NNAME(glGetError) + +void gl_check_error(const char *name) { + uint error; + if((error = OGL_TESTERROR()) != GL_NO_ERROR) { + loge(LOG_GFX, STR_OGL_ERR, error, name); + } +} + +#endif + +#define OGL_DEF(f) OGL_SETPTR(void, f, ()) +#define OGL_DEFR(r, f) OGL_SETPTR(r, f, ()) +#define OGL_DEF1(f, p1) OGL_SETPTR(void, f, (p1)) +#define OGL_DEFR1(r, f, p1) OGL_SETPTR(r, f, (p1)) +#define OGL_DEF2(f, p1, p2) OGL_SETPTR(void, f, (p1, p2)) +#define OGL_DEFR2(r, f, p1, p2) OGL_SETPTR(r, f, (p1, p2)) +#define OGL_DEF3(f, p1, p2, p3) OGL_SETPTR(void, f, (p1, p2, p3)) +#define OGL_DEFR3(r, f, p1, p2, p3) OGL_SETPTR(r, f, (p1, p2, p3)) +#define OGL_DEF4(f, p1, p2, p3, p4) OGL_SETPTR(void, f, (p1, p2, p3, p4)) +#define OGL_DEFR4(r, f, p1, p2, p3, p4) OGL_SETPTR(r, f, (p1, p2, p3, p4)) +#define OGL_DEF5(f, p1, p2, p3, p4, p5) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5)) +#define OGL_DEF6(f, p1, p2, p3, p4, p5, p6) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6)) +#define OGL_DEF7(f, p1, p2, p3, p4, p5, p6, p7) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7)) +#define OGL_DEF8(f, p1, p2, p3, p4, p5, p6, p7, p8) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8)) +#define OGL_DEFR8(r, f, p1, p2, p3, p4, p5, p6, p7, p8) OGL_SETPTR(r, f, (p1, p2, p3, p4, p5, p6, p7, p8)) +#define OGL_DEF9(f, p1, p2, p3, p4, p5, p6, p7, p8, p9) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9)) +#define OGL_DEF10(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)) +#define OGL_DEF11(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)) +#define OGL_DEF12(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12)) +#define OGL_DEF15(f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) OGL_SETPTR(void, f, (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15)) + +int +#ifdef GLFNC_DEBUG +gl_load_dbg_funcs +#else +gl_load_functions +#endif +(void * (*load)(const char *)) { + int cnt = 0; +#include "gl_functions.h" + logd(LOG_GFX, STR_OGL_LOADED, cnt); + return cnt; +} + +#undef OGL_DEF +#undef OGL_DEFR +#undef OGL_DEF1 +#undef OGL_DEFR1 +#undef OGL_DEF2 +#undef OGL_DEFR2 +#undef OGL_DEF3 +#undef OGL_DEFR3 +#undef OGL_DEF4 +#undef OGL_DEFR4 +#undef OGL_DEF5 +#undef OGL_DEF6 +#undef OGL_DEF7 +#undef OGL_DEF8 +#undef OGL_DEFR8 +#undef OGL_DEF9 +#undef OGL_DEF10 +#undef OGL_DEF11 +#undef OGL_DEF12 +#undef OGL_DEF15 + +#ifdef GLFNC_DEBUG + +#define OGL_TESTSTRING OGL_NNAME(glGetString) + +byte gl_init(void * (*load)(const char *), byte debug) { + int major, minor; + const char* version; + OGL_TESTSTRING = (const byte * (*)(uint))(load("glGetString")); + if((OGL_TESTSTRING == NULL) || !(version = OGL_TESTSTRING(GL_VERSION))) { + loge(LOG_GFX, STR_OGL_LOADER_ERR); + return 0; + } + if(!strncmp(version, "OpenGL ES", 9) || (sscanf(version, "%d.%d", &major, &minor) != 2)) { + loge(LOG_GFX, STR_OGL_COMPAT, version); + return 0; + } + if(major < 4 || ((major == 4) && (minor < 6))) { + loge(LOG_GFX, STR_OGL_OLD, major, minor); + return 0; + } + if(debug) + gl_load_dbg_funcs(load); + else + gl_load_functions(load); + return 1; +} + +#undef GLFNC_DEBUG +#else +#define GLFNC_DEBUG +#endif + +#undef OGL_NNAME +#undef OGL_DFUNCV +#undef OGL_DFUNCR +#undef OGL_SETPTR diff --git a/gl_types.h b/gl_types.h new file mode 100644 index 0000000..76a6802 --- /dev/null +++ b/gl_types.h @@ -0,0 +1,9 @@ + +#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)) && defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) && (__SIZEOF_POINTER__ > __SIZEOF_LONG__) +typedef intptr_t iptr; +#else +typedef signed long int iptr; +#endif +typedef signed long int ssize; +typedef struct __GLsync *sync_t; +typedef void (*gldebug_t)(uint, uint, uint, uint, int, const char *, const void *); diff --git a/glx_stub.h b/glx_stub.h new file mode 100644 index 0000000..91aa1b6 --- /dev/null +++ b/glx_stub.h @@ -0,0 +1,58 @@ + +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_SAMPLE_BUFFERS 0x186a0 + +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const byte *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +#define glXGetFBConfigs wcf.GetFBConfigs +#define glXGetFBConfigAttrib wcf.GetFBConfigAttrib +#define glXGetClientString wcf.GetClientString +#define glXQueryExtension wcf.QueryExtension +#define glXQueryVersion wcf.QueryVersion +#define glXDestroyContext wcf.DestroyContext +#define glXMakeCurrent wcf.MakeCurrent +#define glXSwapBuffers wcf.SwapBuffers +#define glXQueryExtensionsString wcf.QueryExtensionsString +#define glXCreateNewContext wcf.CreateNewContext +#define glXGetVisualFromFBConfig wcf.GetVisualFromFBConfig +#define glXCreateWindow wcf.CreateWindow +#define glXDestroyWindow wcf.DestroyWindow diff --git a/gui.h b/gui.h new file mode 100644 index 0000000..c0e43cf --- /dev/null +++ b/gui.h @@ -0,0 +1,2252 @@ + +byte gui_inside(gui_t *elem, int x, int y) { + return (x >= elem->pos_x) && (x < (elem->pos_x + elem->size_x)) && (y >= elem->pos_y) && (y < (elem->pos_y + elem->size_y)); +} + +gui_t *gui_clicked(window_t *win, int x, int y) { + if(win->selected && win->selected->visible && (win->selected->type == GUI_DROPDOWN_HANDLE) && gui_inside(win->selected, x, y)) + return win->selected; // fix (?) + for(int z = 0; z < win->n_elems; z++) { + gui_t *elem = &win->elems[z]; + if(elem->visible && elem->enabled && gui_inside(elem, x, y)) { + return elem; + } + } + return NULL; +} + +uint gui_darken_color(uint color, float factor) { + return (color & 0xff000000) | ((uint)((float)((color >> 16) & 0xff) * factor) << 16) | ((uint)((float)((color >> 8) & 0xff) * factor) << 8) | + (uint)((float)(color & 0xff) * factor); +} + +gui_t *gui_set_style(gui_t *elem, byte type, byte border) { + if(elem->type != GUI_SLIDER && elem->precision == 0xff) + return elem; + type = elem->type != GUI_SLIDER && elem->precision ? elem->precision : type; + border = elem->type != GUI_SLIDER && elem->precision ? (elem->border ? 0 : 2) | (border & 1) : border; + uint top = (type == GUI_LABEL) ? 0 : ((type == GUI_FIELD) ? sys.style.field_top : (type == GUI_CUSTOM ? ((elem->window->focused ^ (elem->type == GUI_TOGGLE_ON)) ? sys.style.win_top : sys.style.win_btm) : + sys.style.fill_top)); + uint bottom = (type == GUI_LABEL) ? 0 : ((type == GUI_FIELD) ? sys.style.field_btm : (type == GUI_CUSTOM ? ((elem->window->focused ^ (elem->type == GUI_TOGGLE_ON)) ? sys.style.win_btm : sys.style.win_top) : + sys.style.fill_btm)); + elem->color_fill_t = (type == GUI_TOGGLE_ON || type == GUI_SLIDER) ? bottom : top; + elem->color_fill_b = (type == GUI_TOGGLE_ON || type == GUI_SLIDER) ? top : bottom; + elem->color_brdr_t = (border & 2) ? 0 : (type == GUI_CUSTOM ? sys.style.wbrdr_top : sys.style.brdr_top); + elem->color_brdr_b = (border & 2) ? 0 : (type == GUI_CUSTOM ? sys.style.wbrdr_btm : sys.style.brdr_btm); + elem->color_text = (type == GUI_CUSTOM ? sys.style.text_win : (elem->type == GUI_LABEL ? sys.style.text_label : + (elem->type == GUI_FIELD ? sys.style.text_field : sys.style.text_base))); + if((type == GUI_CUSTOM && !elem->window->focused) || (type == GUI_TOGGLE_OFF)) + elem->color_text = gui_darken_color(elem->color_text, 0.8f); + if(border & 1) + elem->border = (border & 2) ? 0 : sys.style.border; + if(elem->type != GUI_SLIDER && type != elem->type) + elem->precision = type; + return elem; +} + +gui_t *gui_update_style(gui_t *elem, byte border) { + if(elem->type == GUI_CUSTOM) + return elem; + gui_set_style(elem, elem->type, (elem->type == GUI_LABEL ? 2 : 0) | border); + if(elem->type == GUI_SLIDER) { + elem->margin_x1 = (int)sys.style.fill_top; + elem->margin_y1 = (int)sys.style.fill_btm; + elem->margin_x2 = (int)sys.style.brdr_btm; + elem->margin_y2 = (int)sys.style.brdr_top; + if(border) { + elem->sel_end = ((elem->size_y * sys.style.slider_width) / 24) & ~1; + elem->sel_drag = sys.style.border; + } + } + // else { + // elem->margin_x1 = elem->margin_x2 = (elem->type == GUI_FIELD || elem->type == GUI_DROPDOWN_HANDLE) ? elem->border : 0; + // elem->margin_y1 = elem->margin_y2 = (elem->type == GUI_FIELD || elem->type == GUI_DROPDOWN_HANDLE) ? elem->border : 0; + // } + if(elem->visible) + elem->r_dirty = 1; + return elem; +} + +gui_t *gui_add_elem(window_t *win, int x, int y, int w, int h, byte type) { + gui_t *elem = &win->elems[win->n_elems]; + elem->window = win; + elem->type = type; + elem->func = NULL; + elem->format = NULL; + elem->font = &sys.font; + elem->text = &win->strings[GUI_STR_SIZE * win->n_elems]; + elem->text[0] = 0; + elem->capacity = GUI_STR_SIZE; + elem->texture = 0; + elem->pos_x = x; + elem->pos_y = y; + elem->size_x = w; + elem->size_y = h; + elem->text_x = elem->text_y = elem->tsize_x = elem->tsize_y = 0; + elem->line_space = 0; + elem->font_size = 1; + elem->value = elem->def = elem->min = elem->max = 0; + elem->sel_start = elem->sel_end = elem->sel_drag = -1; + elem->id = win->n_elems; + elem->visible = elem->enabled = elem->t_dirty = elem->r_dirty = 1; + elem->pad = elem->xbreak = elem->precision = 0; + gui_update_style(elem, 1); + if(type != GUI_SLIDER) { + elem->margin_x1 = elem->margin_x2 = (type == GUI_FIELD || type == GUI_DROPDOWN_HANDLE) ? elem->border : 0; + elem->margin_y1 = elem->margin_y2 = (type == GUI_FIELD || type == GUI_DROPDOWN_HANDLE) ? elem->border : 0; + } + win->n_elems += 1; + win->min_x = MIN_VALUE(win->min_x, x); + win->min_y = MIN_VALUE(win->min_y, y); + win->max_x = MAX_VALUE(win->max_x, x + w); + win->max_y = MAX_VALUE(win->max_y, y + h); + return elem; +} + +void gui_update_dropdown(window_t *win) { + if(win->selected && win->selected->type == GUI_DROPDOWN_HANDLE && win->selected->visible) + win->selected->r_dirty = 1; +} + +void gui_update_text(gui_t *elem) { + if(elem->type != GUI_DROPDOWN_HANDLE) { + txt_size(&elem->tsize_x, &elem->tsize_y, elem->pos_x + ((elem->type != GUI_SLIDER) ? elem->margin_x1 : 0), elem->pos_y + ((elem->type != GUI_SLIDER) ? elem->margin_y1 : 0), 0, + elem->font_size * elem->font->yglyph, elem->font_size * (elem->font->yglyph + elem->line_space), + elem->pos_x + ((elem->type != GUI_SLIDER) ? elem->margin_x1 : 0), elem->pos_y + ((elem->type != GUI_SLIDER) ? elem->margin_y1 : 0), + elem->xbreak ? (elem->pos_x + (elem->size_x - ((elem->type != GUI_SLIDER) ? (elem->margin_x1 + elem->margin_x2) : 0))) : 0x7fffffff, 0x7fffffff, elem->font, elem->text); + if(elem->type != GUI_FIELD) { + if((elem->type != GUI_LABEL) || !elem->min) + elem->text_x = (elem->size_x - elem->tsize_x) / 2; + if((elem->type != GUI_LABEL) || !elem->max) + elem->text_y = (elem->size_y - elem->tsize_y - (elem->font_size * elem->font->yglyph)) / 2; + } + // logd("DBG", "s = %d %d; o = %d %d", elem->tsize_x, elem->tsize_y, elem->text_x, elem->text_y); + gui_update_dropdown(elem->window); + } + elem->t_dirty = 1; +} + +void gui_set_text(gui_t *elem, const char *str) { + snprintf(elem->text, elem->capacity, "%s", str); + gui_update_text(elem); +} + +int gui_slider_value(gui_t *elem) { + int r = elem->min + (((float)(int)(elem->sel_start - (elem->sel_end / 2))) * ((float)(int)(elem->max - elem->min)) / ((float)(int)(elem->size_x + 1 - elem->sel_end))); + return CLAMP_VALUE(r, elem->min, elem->max); +} + +int gui_slider_pixel(gui_t *elem) { + int r = ((int)(float)(((float)(int)(elem->value - elem->min)) * ((float)(int)(elem->size_x + 1 - elem->sel_end)) / ((float)(int)(elem->max - elem->min)))) + (elem->sel_end / 2); + return CLAMP_VALUE(r, elem->sel_end / 2, elem->size_x - (elem->sel_end / 2)); +} + +void gui_set_slider(gui_t *elem, int value) { + elem->value = CLAMP_VALUE(value, elem->min, elem->max); + elem->sel_start = gui_slider_pixel(elem); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } +} + +void gui_reformat(window_t *win) { + gui_t *elem; + for(int z = 0; z < win->n_elems; z++) { + elem = &win->elems[z]; + if(elem->type == GUI_SLIDER) + elem->sel_start = gui_slider_pixel(elem); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + if(elem->visible ^ 1) + elem->t_dirty = 0; + } + } +} + +void gui_restyle(window_t *win) { + gui_t *elem; + for(int z = 0; z < win->n_elems; z++) { + elem = &win->elems[z]; + gui_update_style(elem, 0); + if(elem->type == GUI_SLIDER) + elem->sel_start = gui_slider_pixel(elem); + } +} + +void gui_deselect(window_t *win) { + if(win->selected && win->selected->type == GUI_FIELD) { + win->selected->sel_start = win->selected->sel_end = win->selected->sel_drag = -1; + win->tmr_leftmb = 0ULL; + win->selected->t_dirty = 1; + } + win->selected = NULL; +} + +void gui_select(window_t *win, gui_t *elem) { + if(elem && win->selected != elem && elem->type == GUI_FIELD) { + if(elem->func) { + elem->func(elem, 4); + } + } + win->selected = elem; +} + +void gui_open(window_t *win, byte type) { + win->queue = type; +} + +void gui_reopen(window_t *win) { + win->queue = win->open; // GUI_REFRESH +} + +void gui_fmt_keep(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s", elem->format_text); +} + +void gui_fmt_toggle(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %s", elem->format_text, value ? STR_ON : STR_OFF); +} + +void gui_fmt_enum(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %s", elem->format_text, ((const char **)elem->aux_data)[value]); +} + +void gui_fmt_enums(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s", ((const char **)elem->aux_data)[value]); +} + +void gui_fmt_enump(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %s", elem->format_text, *(elem->format_data[value])); +} + +void gui_fmt_enumps(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s", *(elem->format_data[value])); +} + +void gui_fmt_slider(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d", elem->format_text, value); +} + +void gui_fmt_fslider(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %.1f", elem->format_text, ((float)value) / 1000.0f); +} + +void gui_fmt_fslider2(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %.2f", elem->format_text, ((float)value) / 1000.0f); +} + +void gui_fmt_fslider0(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d", elem->format_text, value / 1000); +} + +void gui_fmt_pslider(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d %%", elem->format_text, (int)(((float)value) / 10.0f)); +} + +void gui_fmt_drop(gui_t *elem, int value) { + int pos = 0; + for(int z = 0; z < elem->max && pos < (elem->capacity - 1); z++) { + pos += snprintf(&elem->text[pos], elem->capacity - pos, z ? "\n%s" : "%s", ((const char **)elem->aux_data)[z]); + } +} + +void gui_fmt_dropp(gui_t *elem, int value) { + int pos = 0; + for(int z = 0; z < elem->max && pos < (elem->capacity - 1); z++) { + pos += snprintf(&elem->text[pos], elem->capacity - pos, z ? "\n%s" : "%s", *(elem->format_data[z])); + } +} + +void gui_fnc_debug(gui_t *elem, int value) { + // logd(LOG_SYS, "%d -> %d", elem->id, value); +} + +void gui_fnc_sdebug(gui_t *elem, int value) { + // logd(LOG_SYS, "%d -> %d (%d)", elem->id, value, elem->sel_start); +} + +void gui_fnc_cvar_bool(gui_t *elem, int value) { + cvar_sbool(elem->cv_data, (byte)value); +} + +void gui_fnc_cvar_int(gui_t *elem, int value) { + cvar_sint(elem->cv_data, value); + // if(gui.open == GUI_STYLE) + // gui_reopen(); +} + +void gui_fnc_cvar_float(gui_t *elem, int value) { + cvar_sfloat(elem->cv_data, ((float)value) / 1000.0f); +} + +void gui_fnc_cvar_color(gui_t *elem, int value) { + cvar_scolor(elem->cv_data, (uint)value); +} + +void gui_fnc_cvar_text(gui_t *elem, int value) { + if(value == 3) + cvar_sdef(elem->cv_data); + else if((value != 0 && value != -4) || !cvar_sstr(elem->cv_data, elem->text)) { + if(value == -4) { + cvar_format(elem->cv_data, elem->text); + gui_update_text(elem); + } + return; + } + cvar_format(elem->cv_data, elem->text); + gui_update_text(elem); + if(value != -4) + gui_deselect(elem->window); + // if(elem->window->open == GUI_STYLE) + // gui_restyle(elem->window); +} + +gui_t *gui_add_custom(window_t *win, int x, int y, int w, int h, gui_func *func) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_CUSTOM); + elem->func = func; + return elem; +} + +gui_t *gui_add_label(window_t *win, int x, int y, int w, int h, const char *text) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_LABEL); + snprintf(elem->text, elem->capacity, "%s", text); + gui_update_text(elem); + return elem; +} + +gui_t *gui_add_llabel(window_t *win, int x, int y, int w, int h, const char *text) { + gui_t *elem = gui_add_label(win, x, y, w, h, text); + elem->min = 1; + elem->text_x = 0; + return elem; +} + +gui_t *gui_add_fill(window_t *win, int x, int y, int w, int h, const char *text) { + return gui_set_style(gui_add_label(win, x, y, w, h, text), GUI_BUTTON, 3); +} + +void gui_fnc_nav(gui_t *elem, int value) { + gui_open(elem->window, (byte)elem->value); +} + +gui_t *gui_add_button(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_BUTTON); + snprintf(elem->text, elem->capacity, "%s", text); + gui_update_text(elem); + elem->func = func; + return elem; +} + +gui_t *gui_add_nbutton(window_t *win, int x, int y, int w, int h, const char *text, byte gui) { + gui_t *elem = gui_add_button(win, x, y, w, h, text, gui_fnc_nav); + elem->value = (int)gui; + return elem; +} + +gui_t *gui_add_fbutton(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, gui_func *format) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_BUTTON); + elem->format_text = text; + elem->func = func; + elem->format = format; + return elem; +} + +gui_t *gui_add_toggle(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, byte def, byte init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, init ? GUI_TOGGLE_ON : GUI_TOGGLE_OFF); + elem->format_text = text; + elem->func = func; + elem->format = gui_fmt_toggle; + elem->value = init; + elem->def = def; + return elem; +} + +gui_t *gui_add_cvtoggle(window_t *win, int x, int y, int w, int h, const char *text, const char *name) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_toggle(win, x, y, w, h, text, gui_fnc_cvar_bool, (byte)cv->def, (byte)cv->value); + elem->cv_data = cv; + return elem; +} + +gui_t *gui_add_enum(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, const char **names, int states, int def, int init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_ENUM); + elem->format_text = text; + elem->aux_data = names; + elem->func = func; + elem->format = gui_fmt_enum; + elem->value = init; + elem->max = states; + elem->def = def; + return elem; +} + +gui_t *gui_add_cvbenum(window_t *win, int x, int y, int w, int h, const char *text, const char *name, const char ***names) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_enum(win, x, y, w, h, text, gui_fnc_cvar_bool, NULL, cv->max, cv->def, cv->value); + elem->format_data = names; + elem->cv_data = cv; + elem->format = gui_fmt_enump; + return elem; +} + +gui_t *gui_add_cvienum(window_t *win, int x, int y, int w, int h, const char *text, const char *name, const char ***names) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_enum(win, x, y, w, h, text, gui_fnc_cvar_int, NULL, cv->max, cv->def, cv->value); + elem->format_data = names; + elem->cv_data = cv; + elem->format = gui_fmt_enump; + return elem; +} + +gui_t *gui_add_slider(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, int min, int max, int def, int init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_SLIDER); + elem->format_text = text; + elem->func = func; + elem->format = gui_fmt_slider; + elem->value = init; + elem->min = min; + elem->max = max; + elem->def = def; + return elem; +} + +gui_t *gui_add_cvslider(window_t *win, int x, int y, int w, int h, const char *text, const char *name) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_slider(win, x, y, w, h, text, gui_fnc_cvar_int, cv->min, cv->max, cv->def, cv->value); + elem->cv_data = cv; + return elem; +} + +gui_t *gui_add_fslider(window_t *win, int x, int y, int w, int h, int prec, const char *text, gui_func *func, float min, float max, float def, float init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_SLIDER); + elem->format_text = text; + elem->func = func; + elem->format = (prec == 1) ? gui_fmt_fslider : ((prec == 2) ? gui_fmt_fslider2 : ((prec == -1) ? gui_fmt_pslider : gui_fmt_fslider0)); + elem->value = (int)(float)(init * 1000.0f); + elem->min = (int)(float)(min * 1000.0f); + elem->max = (int)(float)(max * 1000.0f); + elem->def = (int)(float)(def * 1000.0f); + elem->precision = (prec < 0) ? 0 : (byte)prec; + return elem; +} + +gui_t *gui_add_cvfslider(window_t *win, int x, int y, int w, int h, int prec, const char *text, const char *name) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_fslider(win, x, y, w, h, prec, text, gui_fnc_cvar_float, cvar_itf(cv->min), cvar_itf(cv->max), cvar_itf(cv->def), cvar_itf(cv->value)); + elem->cv_data = cv; + return elem; +} + +gui_t *gui_add_dropdown(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, const char **names, int states, int def, int init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_DROPDOWN); + gui_t *drop = gui_add_elem(win, x, y + h, w, sys.font.yglyph * states + sys.style.border * 2, GUI_DROPDOWN_HANDLE); + elem->format_text = text; + elem->aux_data = drop->aux_data = names; + elem->func = func; + elem->format = gui_fmt_enum; + drop->format = gui_fmt_drop; + elem->value = init; + elem->max = drop->max = states; + elem->def = def; + drop->visible = 0; + drop->t_dirty = drop->r_dirty = 0; + return elem; +} + +gui_t *gui_add_dropup(window_t *win, int x, int y, int w, int h, const char *text, gui_func *func, const char **names, int states, int def, int init) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_DROPDOWN); + gui_t *drop = gui_add_elem(win, x, y - (sys.font.yglyph * states + sys.style.border * 2), w, + sys.font.yglyph * states + sys.style.border * 2, GUI_DROPDOWN_HANDLE); + elem->format_text = text; + elem->aux_data = drop->aux_data = names; + elem->func = func; + elem->format = gui_fmt_enum; + drop->format = gui_fmt_drop; + elem->value = init; + elem->max = drop->max = states; + elem->def = def; + drop->visible = 0; + drop->t_dirty = drop->r_dirty = 0; + return elem; +} + +gui_t *gui_add_cvbdrop(window_t *win, int x, int y, int w, int h, const char *text, const char *name, const char ***names) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_dropdown(win, x, y, w, h, text, gui_fnc_cvar_bool, NULL, cv->max, cv->def, cv->value); + gui_t *drop = elem + 1; + elem->format_data = names; + drop->format_data = names; + elem->cv_data = cv; + drop->cv_data = cv; + elem->format = gui_fmt_enump; + drop->format = gui_fmt_dropp; + return elem; +} + +gui_t *gui_add_cvidrop(window_t *win, int x, int y, int w, int h, const char *text, const char *name, const char ***names) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_dropdown(win, x, y, w, h, text, gui_fnc_cvar_int, NULL, cv->max, cv->def, cv->value); + gui_t *drop = elem + 1; + elem->format_data = names; + drop->format_data = names; + elem->cv_data = cv; + drop->cv_data = cv; + elem->format = gui_fmt_enump; + drop->format = gui_fmt_dropp; + return elem; +} + +gui_t *gui_add_field(window_t *win, int x, int y, int w, int h, char *buf, int capacity, gui_func *func, byte sidescroll, byte edit) { + gui_t *elem = gui_add_elem(win, x, y, w, h, GUI_FIELD); + // if(mono) + // elem->font = &sys.mono; + if(buf && capacity) { + elem->text = buf; + elem->capacity = capacity; + } + else if(buf) { + snprintf(elem->text, elem->capacity, "%s", buf); + } + gui_update_text(elem); + elem->func = func; + elem->value = edit; + elem->xbreak = !sidescroll; + if(sidescroll) + elem->text_y = (elem->size_y - (elem->margin_y1 + elem->margin_y2 + (elem->font->yglyph * elem->font_size))) / 2; + // logd("t", "%dbb", elem->text_y); + return elem; +} + +gui_t *gui_add_cvfield(window_t *win, int x, int y, int w, int h, const char *name) { + cvar_t *cv = cvar_get(name); + gui_t *elem = gui_add_field(win, x, y, w, h, NULL, 0, gui_fnc_cvar_text, 1, 1); + elem->cv_data = cv; + cvar_format(cv, elem->text); + gui_update_text(elem); + return elem; +} + +void gui_text_select(gui_t *elem, int x, int y, byte drag) { + int x1 = elem->pos_x + elem->margin_x1; + int y1 = elem->pos_y + elem->margin_y1; + int x2 = elem->size_x - (elem->margin_x1 + elem->margin_x2); + int y2 = elem->size_y - (elem->margin_y1 + elem->margin_y2); + int offset = txt_offset(&elem->min, &elem->max, x, y, x1 + (elem->xbreak ? 0 : elem->text_x), y1 + elem->text_y, elem->font_size * elem->font->yglyph, + x1 + elem->text_x, y1 + elem->text_y, elem->xbreak ? (elem->pos_x + x2) : 0x7fffffff, 0x7fffffff, elem->font, elem->text); + // logd("tst", "@A %d %d --> %d %d", x1, y1, x2, y2); + // logd("tst", "@C %d %d --> %d, %d %d", x, y, offset, elem->min, elem->max); + if(!drag) { + elem->sel_drag = elem->sel_start = elem->sel_end = offset; + } + else if(drag && elem->sel_drag >= 0 && offset >= elem->sel_drag) { + elem->sel_start = elem->sel_drag; + elem->sel_end = offset; + } + else if(drag && elem->sel_drag >= 0 && offset < elem->sel_drag) { + elem->sel_start = offset; + elem->sel_end = elem->sel_drag; + } + // logd("tst", "@S %d . %d . %d", elem->sel_start, elem->sel_drag, elem->sel_end); + elem->t_dirty = 1; + if(x < x1) + elem->window->scrollx = x1 - x; + else if(x >= (x1 + x2)) + elem->window->scrollx = -(x - (x1 + x2)); + if(y < y1) + elem->window->scrolly = y1 - y; + else if(y >= (y1 + y2)) + elem->window->scrolly = -(y - (y1 + y2)); +} + +void gui_mouse(window_t *win, int btn, int x, int y, byte ctrl, byte shift) { + int prev; + gui_t *elem = gui_clicked(win, x, y); + if(win->selected != NULL && win->selected != elem && win->selected->type == GUI_FIELD) { + win->selected->sel_start = win->selected->sel_end = win->selected->sel_drag = -1; + win->tmr_leftmb = 0ULL; + win->selected->t_dirty = 1; + if(win->selected->func) { + win->selected->func(win->selected, -4); + } + if(elem && elem->type == GUI_FIELD && elem->func) { + elem->func(elem, 4); + } + } + else if(win->selected != NULL && win->selected != elem && win->selected->type == GUI_DROPDOWN_HANDLE) { + win->selected->visible = 0; + win->selected->r_dirty = 1; + return; + } + else if(elem && win->selected != elem && elem->type == GUI_FIELD) { + if(elem->func) { + elem->func(elem, 4); + } + } + win->selected = elem; + if(elem == NULL) { + return; + } + else if(elem->type == GUI_BUTTON) { + elem->func(elem, (ctrl || (btn == 3)) ? -1 : ((shift || (btn == 2)) ? 1 : 0)); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + else if((elem->type == GUI_TOGGLE_OFF) || (elem->type == GUI_TOGGLE_ON)) { + prev = elem->value; + if((elem->value = (ctrl || (btn == 3)) ? elem->def : (elem->value ^ 1)) != prev) { + elem->type = elem->value ? GUI_TOGGLE_ON : GUI_TOGGLE_OFF; + gui_update_style(elem, 1); + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else if(elem->type == GUI_ENUM) { + prev = elem->value; + elem->value = (ctrl || (btn == 3)) ? elem->def : (elem->value + ((shift || (btn == 2)) ? -1 : 1)); + elem->value = (elem->value == elem->max) ? 0 : ((elem->value == -1) ? (elem->max - 1) : elem->value); + if(elem->value != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else if(elem->type == GUI_SLIDER) { + prev = elem->value; + if(ctrl || (btn == 3)) { + elem->value = elem->def; + elem->sel_start = gui_slider_pixel(elem); + if(elem->value != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else { + elem->sel_start = x - elem->pos_x; + elem->value = gui_slider_value(elem); + elem->sel_start = gui_slider_pixel(elem); + if(elem->value != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + } + else if(elem->type == GUI_DROPDOWN) { + prev = elem->value; + if(ctrl || (btn == 3)) { + if((elem->value = elem->def) != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else { + gui_t *drop = elem + 1; + drop->visible = 1; + drop->r_dirty = 1; + win->selected = drop; + } + } + else if(elem->type == GUI_DROPDOWN_HANDLE) { + gui_t *drop = elem - 1; + prev = drop->value; + drop->value = (y - (elem->pos_y + elem->margin_y1)) * drop->max / (elem->size_y - (elem->margin_y1 + elem->margin_y2)); + drop->value = CLAMP_VALUE(drop->value, 0, drop->max - 1); + if(drop->value != prev) { + drop->func(drop, drop->value); + if(drop->format) { + drop->format(drop, drop->value); + gui_update_text(drop); + } + } + elem->visible = 0; + elem->r_dirty = 1; + } + else if(elem->type == GUI_FIELD) { + if(btn == 1) { + if((!shift) && ((sys.tmr_current - win->tmr_leftmb) <= (((ulong)sys.dclick_delay) * 1000ULL))) { + elem->sel_start = elem->sel_drag = 0; + elem->sel_end = strlen(elem->text); + elem->t_dirty = 1; + } + else { + gui_text_select(elem, x, y, shift); + } + win->tmr_leftmb = sys.tmr_current; + } + else if((btn == 3) && elem->func) { + elem->func(elem, 3); + } + } +} + +void gui_mouserel(window_t *win) { + win->scrollx = win->scrolly = 0; +} + +byte gui_drag(window_t *win, int x, int y) { + int prev; + gui_t *elem = win->selected; + if(elem == NULL) { + return 0; + } + else if(elem->type == GUI_SLIDER) { + x = (x < elem->pos_x) ? elem->pos_x : ((x >= (elem->pos_x + elem->size_x)) ? (elem->pos_x - 1 + elem->size_x) : x); + prev = elem->value; + elem->sel_start = x - elem->pos_x; + elem->value = gui_slider_value(elem); + elem->value = CLAMP_VALUE(elem->value, elem->min, elem->max); + elem->sel_start = gui_slider_pixel(elem); + if(elem->value != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else if(elem->type == GUI_FIELD) { + gui_text_select(elem, x, y, 1); + } + return elem->type != GUI_LABEL; +} + +void gui_scroll(window_t *win, int scr_x, int scr_y, int x, int y, byte ctrl, byte shift) { + int prev; + gui_t *elem = gui_clicked(win, x, y); + if(elem == NULL) { + return; + } + else if((elem->type == GUI_SLIDER) && scr_y) { + prev = elem->value; + elem->value += (scr_y < 0 ? 1 : -1) * (ctrl ? (elem->precision ? 1 : 10) : ((elem->precision >= 2) ? 10 : (elem->precision ? 100 : 1))) * (shift ? (elem->precision ? 50 : 5) : 1); + elem->value = CLAMP_VALUE(elem->value, elem->min, elem->max); + elem->sel_start = gui_slider_pixel(elem); + if(elem->value != prev) { + elem->func(elem, elem->value); + if(elem->format) { + elem->format(elem, elem->value); + gui_update_text(elem); + } + } + } + else if(elem->type == GUI_FIELD) { + if(scr_y && elem->xbreak) { + int limit = (elem->font->yglyph * elem->font_size) + elem->tsize_y - (elem->size_y - (elem->margin_y1 + elem->margin_y2)); + limit = CLAMP_VALUE(limit, 0, 0x7fffffff); + prev = elem->text_y; + elem->text_y += (scr_y < 0 ? -1 : 1) * (ctrl ? 1 : (elem->font->yglyph * elem->font_size)) * GUI_SCROLL_LN * (shift ? 10 : 1); + elem->text_y = CLAMP_VALUE(elem->text_y, -limit, 0); + if(elem->sel_start >= 0) + elem->max += (elem->text_y - prev); + elem->t_dirty = 1; + } + else if(scr_y || scr_x) { + int limit = (elem->font->xglyph * elem->font_size) + elem->tsize_x - (elem->size_x - (elem->margin_x1 + elem->margin_x2)); + limit = CLAMP_VALUE(limit, 0, 0x7fffffff); + prev = elem->text_x; + elem->text_x += ((scr_y ? scr_y : (-scr_x)) < 0 ? -1 : 1) * (ctrl ? 1 : (elem->font->xglyph * elem->font_size)) * GUI_SCROLL_LN * (shift ? 10 : 1); + elem->text_x = CLAMP_VALUE(elem->text_x, -limit, 0); + if(elem->sel_start >= 0) + elem->min += (elem->text_x - prev); + elem->t_dirty = 1; + } + } +} + +void gui_text_clamp_scroll(gui_t *elem) { + int limit; + if(elem->xbreak) { + limit = (elem->font->yglyph * elem->font_size) + elem->tsize_y - (elem->size_y - (elem->margin_y1 + elem->margin_y2)); + limit = CLAMP_VALUE(limit, 0, 0x7fffffff); + elem->text_y = CLAMP_VALUE(elem->text_y, -limit, 0); + } + else { + limit = (elem->font->xglyph * elem->font_size) + elem->tsize_x - (elem->size_x - (elem->margin_x1 + elem->margin_x2)); + limit = CLAMP_VALUE(limit, 0, 0x7fffffff); + elem->text_x = CLAMP_VALUE(elem->text_x, -limit, 0); + } +} + +void gui_text_update_cur(gui_t *elem, int offset, byte shift) { + int x1 = elem->pos_x + elem->margin_x1; + int y1 = elem->pos_y + elem->margin_y1; + int x2 = elem->size_x - (elem->margin_x1 + elem->margin_x2); + int y2 = elem->size_y - (elem->margin_y1 + elem->margin_y2); + gui_text_clamp_scroll(elem); + txt_coord(&elem->min, &elem->max, offset, x1 + (elem->xbreak ? 0 : elem->text_x), y1 + elem->text_y, elem->font_size * elem->font->yglyph, + x1 + elem->text_x, y1 + elem->text_y, elem->xbreak ? (elem->pos_x + x2) : 0x7fffffff, 0x7fffffff, elem->font, elem->text); + if(shift) { + if(elem->xbreak && elem->max < y1) + elem->text_y += y1 - elem->max; + else if(elem->xbreak && (elem->max + (elem->font->yglyph * elem->font_size)) >= (y1 + y2)) + elem->text_y -= (elem->max + (elem->font->yglyph * elem->font_size)) - (y1 + y2); + if(!(elem->xbreak) && elem->min < x1) + elem->text_x += x1 - elem->min; + else if(!(elem->xbreak) && (elem->min + (elem->font->xglyph * elem->font_size)) >= (x1 + x2)) + elem->text_x -= (elem->min + (elem->font->xglyph * elem->font_size)) - (x1 + x2); + gui_text_update_cur(elem, offset, 0); + } + else { + elem->t_dirty = 1; + } +} + +void gui_text_insert(gui_t *elem, const char *str) { + if(!str || (elem->sel_start == -1)) + return; + int olen = strlen(elem->text); + int slen = elem->sel_end - elem->sel_start; + int plen; + str_strip(str, &sys.work_buf[1 + olen - elem->sel_end], WORK_BUF - (1 + olen - elem->sel_end), -1, elem->xbreak ? '\n' : ' ', ' ', 0); + plen = strlen(&sys.work_buf[1 + olen - elem->sel_end]); + // logd("t", "%d %d %d", olen, slen, plen); + if((plen + olen - slen) >= elem->capacity) + return; + memcpy(sys.work_buf, &elem->text[elem->sel_end], 1 + olen - elem->sel_end); + memcpy(&elem->text[elem->sel_start], &sys.work_buf[1 + olen - elem->sel_end], plen); + memcpy(&elem->text[elem->sel_start += plen], sys.work_buf, 1 + olen - elem->sel_end); + elem->sel_end = elem->sel_drag = elem->sel_start; + gui_update_text(elem); + gui_text_update_cur(elem, elem->sel_end, 1); +} + +void gui_key(window_t *win, int key, byte ctrl, byte shift) { + gui_t *elem = win->selected; + if(elem == NULL) { + return; + } + else if(elem->type == GUI_FIELD) { + if(ctrl && key == KEYSYM_A) { + elem->sel_start = elem->sel_drag = 0; + elem->sel_end = strlen(elem->text); + elem->t_dirty = 1; + } + else if(ctrl && (key == KEYSYM_C) || (elem->value && (key == KEYSYM_X))) { + if(elem->sel_start >= 0 && elem->sel_start != elem->sel_end) { // fix empty + // char end = elem->text[elem->sel_end]; + // elem->text[elem->sel_end] = 0; + str_strip(&elem->text[elem->sel_start], sys.work_buf, WORK_BUF, elem->sel_end - elem->sel_start, '\n', 0, '?'); + wcf_setclip(sys.work_buf); + // elem->text[elem->sel_end] = end; + if(key == KEYSYM_X) + gui_text_insert(elem, ""); + } + } + else if(elem->value && ctrl && key == KEYSYM_V) { + gui_text_insert(elem, wcf_getclip()); + } + else if(elem->value && !ctrl && key == KEYSYM_RETURN) { + if(elem->xbreak) { + gui_text_insert(elem, "\n"); + } + else if(elem->func) { + elem->func(elem, shift ? 2 : 0); + } + } + else if(elem->value && (!ctrl) && (key == KEYSYM_BACKSPACE || key == KEYSYM_DELETE)) { + if(elem->sel_start != elem->sel_end) { + gui_text_insert(elem, ""); + } + else if(key == KEYSYM_DELETE && elem->sel_start >= 0) { + if(utf_readn(elem->text, &elem->sel_end)) { + gui_text_insert(elem, ""); + } + else { + elem->sel_end = elem->sel_start; + } + } + else if(key == KEYSYM_BACKSPACE && elem->sel_start > 0) { + utf_rreadn(elem->text, &elem->sel_start); + gui_text_insert(elem, ""); + } + } + else if(!ctrl && (key == KEYSYM_RIGHT || key == KEYSYM_LEFT)) { + if(key == KEYSYM_RIGHT && elem->sel_start != elem->sel_end) { + elem->sel_start = elem->sel_drag = elem->sel_end; + } + else if(key == KEYSYM_LEFT && elem->sel_start != elem->sel_end) { + elem->sel_end = elem->sel_drag = elem->sel_start; + } + if(key == KEYSYM_RIGHT && elem->sel_start >= 0) { + if(utf_readn(elem->text, &elem->sel_end)) { + elem->sel_start = elem->sel_drag = elem->sel_end; + } + else { + elem->sel_end = elem->sel_drag = elem->sel_start; + } + gui_text_update_cur(elem, elem->sel_end, 1); + } + else if(key == KEYSYM_LEFT && elem->sel_start >= 0) { + if(elem->sel_start > 0) + utf_rreadn(elem->text, &elem->sel_start); + elem->sel_end = elem->sel_drag = elem->sel_start; + gui_text_update_cur(elem, elem->sel_end, 1); + } + } + else if(!ctrl && (key == KEYSYM_DOWN || key == KEYSYM_UP)) { + if(elem->xbreak) { + if(key == KEYSYM_DOWN && elem->sel_start != elem->sel_end) { + elem->sel_start = elem->sel_drag = elem->sel_end; + } + else if(key == KEYSYM_UP && elem->sel_start != elem->sel_end) { + elem->sel_end = elem->sel_drag = elem->sel_start; + } + if(key == KEYSYM_DOWN && elem->sel_start >= 0) { + uint ch; + if(ch = utf_readn(elem->text, &elem->sel_end)) { + while(ch && ch != '\n') { + ch = utf_readn(elem->text, &elem->sel_end); + } + if(!ch) + elem->sel_end -= 1; + elem->sel_start = elem->sel_drag = elem->sel_end; + } + else { + elem->sel_end = elem->sel_drag = elem->sel_start; + } + gui_text_update_cur(elem, elem->sel_end, 1); + } + else if(key == KEYSYM_UP && elem->sel_start >= 0) { + uint ch; + if(elem->sel_start > 0) { + do { + ch = utf_rreadn(elem->text, &elem->sel_start); + } + while(ch && ch != '\n'); + } + elem->sel_end = elem->sel_drag = elem->sel_start; + gui_text_update_cur(elem, elem->sel_end, 1); + } + } + else if(elem->func) { + elem->func(elem, (key == KEYSYM_DOWN) ? -1 : 1); + } + } + else if((!ctrl) && key == KEYSYM_TAB) { + if(elem->func) { + elem->func(elem, shift ? -6 : 6); + } + } + } +} + +void gui_char(window_t *win, uint code) { + gui_t *elem = win->selected; + if(elem == NULL) { + return; + } + else if(elem->type == GUI_FIELD && elem->value) { + int pos = 0; + char chr[8]; + utf_rwriten(chr, &pos, 8, code); + chr[pos] = 0; + gui_text_insert(elem, chr); + } +} + +gui_t *gui_get(window_t *win, int id) { + return &win->elems[id]; +} + +void gui_fnc_quit(gui_t *elem, int value) { + sys.interrupted = 1; +} + +byte gui_init_none(window_t *win, int frame_x, int frame_y, byte open) { + return 0; +} + + + + +void gui_fmt_degrees(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %.1f °", elem->format_text, ((float)value) / 1000.0f); +} + +void gui_fmt_millis(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d ms", elem->format_text, value); +} + +void gui_fmt_seconds(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d s", elem->format_text, value); +} + +void gui_fmt_units(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d U", elem->format_text, value); +} + +void gui_fmt_funits(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %.1f U", elem->format_text, ((float)value) / 1000.0f); +} + +void gui_fmt_chunks(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d %s", elem->format_text, value, value == 1 ? STR_CHUNK : STR_CHUNKS); +} + +void gui_fmt_uchunks(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %d %s [%d]", elem->format_text, value, value == 1 ? STR_CHUNK : STR_CHUNKS, value * CHUNK_SIZE); +} + +void gui_fnc_con_line(gui_t *elem, int value) { + if(value == -4 || value == 3 || value == 2) { + return; + } + else if(value == -1 || value == 1) { + // ... + } + else if(value == -6 || value == 6) { + // ... + } + else if(value == 0) { + con_exec(elem->text); + elem->text[0] = 0; + // if(sys.con_autoclose) + // gui_open(elem->window, GUI_CONSOLE); + } + gui_update_text(elem); + elem->sel_start = elem->sel_end = elem->sel_drag = strlen(elem->text); + gui_text_update_cur(elem, elem->sel_start, 1); + // if(!value) + // gui_deselect(); +} + +void gui_fnc_con_clear(gui_t *elem, int value) { + elem = gui_get(elem->window, 5); + con_reset(); + gui_update_text(elem); + elem->sel_start = elem->sel_end = elem->sel_drag = sys.log_len; + gui_text_update_cur(elem, elem->sel_start, 1); +} + +static const char **gui_log_levels[] = { + &STR_LOG_LEVEL_SILENT, + &STR_LOG_LEVEL_USER, + &STR_LOG_LEVEL_ERROR, + &STR_LOG_LEVEL_WARN, + &STR_LOG_LEVEL_INFO, + &STR_LOG_LEVEL_PERF, + &STR_LOG_LEVEL_DEBUG, + &STR_LOG_LEVEL_TRACE +}; + +byte gl_isdebug(); +const char *gl_getsuffix(); +const char *framecode(); +const char *tpscode(); +ulong perf_get(byte section); +int gui_render_text(gui_t *elem, int x, int y, uint color, const char *text); + +void gui_render_stats(gui_t *elem, int value) { + elem->t_dirty = 1; + if(!value) + return; + sprintf(&sys.work_buf[4096], sys.win_vsync ? COL_DGRAY"VSYNC" : (sys.sync_limited ? COL_GREEN"%d" : COL_RED"UNL"), sys.sync_limit); + sprintf(&sys.work_buf[5096], sys.ticked ? COL_GREEN"%.1f" : COL_DGRAY"PAUSE", (float)sys.tick_target / 1000.0f); + if(wcf.active) + sprintf(&sys.work_buf[6096], (sys.win_full == wcf.active) ? ", W %d x %d @ %d Hz" : ", W %d x %d", wcf.active->frame_x, wcf.active->frame_y, sys.win_refresh); + sprintf(sys.work_buf, + "%s%.2f"COL_RESET" %s [%s"COL_RESET"], %.3f ms%s\n" + "%s%.2f"COL_RESET" %s [%s"COL_RESET"], %.3f ms, E %d ms\n" + "S+%lld Speicherblöcke, %.2f MB, P %.2f MB\n" + "%d Shader; W+%d Bildpuffer (~%.2f MB, P ~%.2f MB)\n" + "1+%d Puffer (%.2f MB, P %.2f MB)\n" + "%d+%d Texturen (%.2f MB, P %.2f MB)\n" + "%d Drawlists, M %d (D %d, T %d)\n" + "%d/%d dyn. Lichter (D %.1f, V %.1f); %d Objekte (G %d), L %d, C %d" + , + framecode(), (sys.tmr_framerate < 1.0f) ? (1.0f / sys.tmr_framerate) : sys.tmr_framerate, sys.tmr_framerate < 1.0f ? "SPF" : "FPS", &sys.work_buf[4096], + ((float)(sys.tmr_ttime - perf_get(PERF_WAIT))) / 1000.0f, wcf.active ? &sys.work_buf[6096] : "", + sys.ticked ? tpscode() : COL_DGRAY, sys.ticked ? ((sys.tick_tickrate < 1.0f) ? (1.0f / sys.tick_tickrate) : sys.tick_tickrate) : 0.0f, + (sys.ticked && sys.tick_tickrate < 1.0f) ? "SPT" : "TPS", &sys.work_buf[5096], + ((float)sys.tick_time) / 1000.0f, sys.tick_timeout, + sys.mem_blocks, ((float)sys.mem_alloc) / 1024.0 / 1024.0, ((float)sys.mem_peak) / 1024.0 / 1024.0, + gdr.shd_loaded, gdr.fb_loaded, ((float)gdr.fb_mem) / 1024.0 / 1024.0, ((float)gdr.fb_peak) / 1024.0 / 1024.0, + gdr.buf_loaded, ((float)gdr.buf_mem) / 1024.0 / 1024.0, ((float)gdr.buf_peak) / 1024.0 / 1024.0, + sys.font_pages + 1, gdr.tex_loaded, ((float)gdr.tex_mem) / 1024.0 / 1024.0, ((float)gdr.tex_peak) / 1024.0 / 1024.0, + gdr.drawlists.stored, gdr.materials.stored, gdr.lists_drawn, gdr.tris_drawn, + (wcf.active && wcf.active->world) ? wcf.active->world->s_lights : 0, gdr.light_max, gdr.light_dist_cam, gdr.light_dist_vert, + (wcf.active && wcf.active->world) ? wcf.active->world->n_objects : 0, + (wcf.active && wcf.active->world) ? wcf.active->world->global_geom.stored : 0, + (wcf.active && wcf.active->world) ? wcf.active->world->n_lights : 0, + (wcf.active && wcf.active->world) ? wcf.active->world->n_chunks : 0 + ); + gui_render_text(elem, 0, 0, sys.style.text_label, sys.work_buf); +} + +void gui_render_profiler(gui_t *elem, int value) { + elem->t_dirty = 1; + if(!value) + return; + int pos = 0; + for(int z = 0; z < PERF_SECTIONS; z++) { + pos += sprintf(&sys.work_buf[pos], "%s\n", perf_sections[z]); + } + gui_render_text(elem, 10, 0, sys.style.text_label, sys.work_buf); + pos = 0; + for(int z = 0; z < PERF_SECTIONS; z++) { + pos += sprintf(&sys.work_buf[pos], "%.3f ms\n", ((float)perf_get(z)) / 1000.0f); + } + gui_render_text(elem, 120, 0, sys.style.text_label, sys.work_buf); + pos = 0; + for(int z = 0; z < PERF_SECTIONS; z++) { + pos += sprintf(&sys.work_buf[pos], "%.2f %%\n", (((float)perf_get(z)) / ((float)sys.tmr_ttime)) * 100.0f); + } + gui_render_text(elem, 240, 0, sys.style.text_label, sys.work_buf); +} + +void gui_fnc_optwin(gui_t *elem, int value); +void gui_fnc_plrwin(gui_t *elem, int value); +void gui_fnc_testwin(gui_t *elem, int value); +void gui_fnc_infowin(gui_t *elem, int value); + +byte gui_init_console(window_t *win, int frame_x, int frame_y, byte open) { + gui_t *elem; + int y = 2 + 8; + gui_add_cvtoggle(win, 0, y * sys.font.yglyph, 160, 24, STR_CON_AUTOSCROLL, "con_autoscroll"); + gui_add_cvtoggle(win, 160, y * sys.font.yglyph, 160, 24, STR_CON_TIMESTAMPS, "con_timestamps"); + gui_add_cvbdrop(win, 320, y * sys.font.yglyph, 160, 24, STR_CON_LOGLEVEL, "con_loglevel", gui_log_levels); + gui_add_button(win, 480, y * sys.font.yglyph, 160, 24, STR_CON_CLEAR, gui_fnc_con_clear); + elem = gui_add_field(win, 0, y * sys.font.yglyph + 24, frame_x, frame_y - (y * sys.font.yglyph + 48 + 24), sys.con_buf, CON_BUF, NULL, 0, 0); + elem->color_fill_t &= 0x80ffffff; + elem->color_fill_b &= 0x80ffffff; + elem->color_brdr_t &= 0x80ffffff; + elem->color_brdr_b &= 0x80ffffff; + elem->precision = 0xff; + // gui_reformat(); + elem->sel_start = elem->sel_end = elem->sel_drag = sys.log_len; + gui_update_text(elem); + gui_text_update_cur(elem, elem->sel_start, 1); + gui_select(win, gui_add_field(win, 0, frame_y - 48, frame_x, 24, sys.con_line, CON_LINE, gui_fnc_con_line, 1, 1)); + y = 0; + sprintf(sys.work_buf, "OpenGL %s%s%s (GLSL %s)", + glGetString(GL_VERSION), gl_getsuffix(), gl_isdebug() ? " debug" : "", glGetString(GL_SHADING_LANGUAGE_VERSION)); + gui_add_llabel(win, 0, (y++ * sys.font.yglyph), 640, sys.font.yglyph, sys.work_buf); + sprintf(sys.work_buf, "%s (%s)", + glGetString(GL_RENDERER), glGetString(GL_VENDOR)); + gui_add_llabel(win, 0, (y++ * sys.font.yglyph), 640, sys.font.yglyph, sys.work_buf); + gui_add_custom(win, 0, (y * sys.font.yglyph), 640, 8 * sys.font.yglyph, gui_render_stats); + gui_add_custom(win, frame_x - (960 - 640), 0, 960 - 640, 10 * sys.font.yglyph, gui_render_profiler); + // if(frame_x > 640) + gui_add_fill(win, 640, ((2 + 8) * sys.font.yglyph), frame_x - 640, 24, ""); + + gui_add_button(win, 0, frame_y - 24, 160, 24, STR_TITLE_OPTIONS, gui_fnc_optwin); + gui_add_button(win, 160, frame_y - 24, 160, 24, STR_TITLE_PLAYER, gui_fnc_plrwin); + gui_add_button(win, 320, frame_y - 24, 160, 24, STR_TEST, gui_fnc_testwin); + gui_add_button(win, 480, frame_y - 24, 160, 24, STR_TITLE_INFO_SHORT, gui_fnc_infowin); + gui_add_fill(win, 640, frame_y - 24, frame_x - 640, 24, ""); + return 0; +} + +byte gui_init_main(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(520, 424); + gui_t *elem = gui_add_label(win, 20, 20, 480, 96, ""); + elem->color_fill_t = elem->color_fill_b = 0xffffffff; + elem->border = 0; + elem->texture = gdr.tex_logo; + elem->precision = 0xff; + gui_add_label(win, 20, 120, 480, 48, SYS_PROGRAM); + // gui_add_nbutton(win, 80, 220, 360, 24, STR_TITLE_OPTIONS, GUI_CONTROL); + // gui_add_nbutton(win, 80, 260, 360, 24, STR_TITLE_PLAYER, GUI_PLAYER); + // gui_add_nbutton(win, 80, 300, 360, 24, STR_TITLE_INFO, GUI_INFO); + gui_add_button(win, 80, 380, 360, 24, STR_QUIT, gui_fnc_quit); + return 1; +} + +byte gui_init_world(window_t *win, int frame_x, int frame_y, byte open) { + win->title = open ? STR_TITLE_MENU : STR_TITLE_NONE; + return 0; +} + +static const char **gui_options_titles[] = { + &STR_TITLE_BINDS, &STR_TITLE_STYLE, &STR_TITLE_CONTROL, &STR_TITLE_DISPLAY, &STR_TITLE_GRAPHICS, &STR_TITLE_SOUND +}; + +byte gui_init_binds(window_t *win, int frame_x, int frame_y, byte open); +byte gui_init_style(window_t *win, int frame_x, int frame_y, byte open); +byte gui_init_control(window_t *win, int frame_x, int frame_y, byte open); +byte gui_init_display(window_t *win, int frame_x, int frame_y, byte open); +byte gui_init_graphics(window_t *win, int frame_x, int frame_y, byte open); +byte gui_init_sound(window_t *win, int frame_x, int frame_y, byte open); + +static gui_initfnc *gui_options_init[] = { + gui_init_binds, gui_init_style, gui_init_control, gui_init_display, gui_init_graphics, gui_init_sound +}; + +byte gui_init_options(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(740, 444); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_OPTIONS); + int x = 0; + int y = 0; + gui_t *elem; + win->title = *(gui_options_titles[open - 1]); + gui_options_init[open - 1](win, frame_x, frame_y, open); + for(int z = 0; z < 6; z++) { + elem = gui_add_nbutton(win, 240 * x, 24 * y, 240, 24, *(gui_options_titles[z]), z + 1); + if(open == z + 1) + gui_set_style(elem, GUI_TOGGLE_ON, 1); + if(++x == 5) { + x = 0; + ++y; + } + } + // gui_add_nbutton(240 * 0, 0, 240, 24, 1, STR_TITLE_BINDS, GUI_BINDS); + // gui_add_nbutton(240 * 1, 0, 240, 24, 1, STR_TITLE_STYLE, GUI_STYLE); + // gui_add_nbutton(240 * 2, 0, 240, 24, 1, STR_TITLE_CONTROL, GUI_CONTROL); + // gui_add_nbutton(240 * 3, 0, 240, 24, 1, STR_TITLE_DISPLAY, GUI_DISPLAY); + // gui_add_nbutton(240 * 4, 0, 240, 24, 1, STR_TITLE_GRAPHICS, GUI_GRAPHICS); + // gui_add_nbutton(240 * 0, 24, 240, 24, 1, STR_TITLE_SOUND, GUI_SOUND); + + // gui_add_nbutton(win, 0, frame_y - 24, frame_x, 24, STR_BACK, GUI_MENU); + // gui_reformat(); + return 0; +} + +void gui_fnc_bind(gui_t *elem, int value) { + int bind = (elem->id - 1) / 2; + if(value == -1) { + cvar_skey(bind, cvar_defkey(bind)); + gui_reformat(elem->window); + } + else if(value == 1) { + cvar_skey(bind, 0); + gui_reformat(elem->window); + } + else { + sys.input = 0; + sys.keywait = bind + 1; + sys.keywin = elem->window; + } +} + +byte gui_bind_dupe(byte bind) { + int key = sys.binds[bind].key; + if(!key) + return 0; + for(int z = 0; z < KEY_BINDS; z++) { + if((z != bind) && sys.binds[z].key && (sys.binds[z].key == key)) + return 1; + } + return 0; +} + +void gui_fmt_bind(gui_t *elem, int value) { + int bind = (elem->id - 1) / 2; + char v[32]; + if(sys.keywait && ((sys.keywait - 1) == bind)) + snprintf(elem->text, elem->capacity, FMT_BLINK COL_BLUE"***"); + // else if(sys.binds[bind].key >= 33 && sys.binds[bind].key <= 126) + // snprintf(elem->text, elem->capacity, gui_bind_dupe(bind) ? COL_RED"<%c>" : COL_YELLOW"<%c>", (char)sys.binds[bind].key); + else + snprintf(elem->text, elem->capacity, gui_bind_dupe(bind) ? COL_RED"%s" : COL_YELLOW"%s", cvar_getkey(sys.binds[bind].key, v)); +} + +void gui_setkey(int key, byte release) { + if(sys.keywait) { + if(release) { + if((key > 0) && key == sys.key_release) { + cvar_skey(sys.keywait - 1, key); + sys.keywait = 0; + sys.key_release = 0; + gui_reformat(sys.keywin); + sys.keywin = NULL; + } + } + else if(!(sys.key_release)) { + if(key > 0) { + sys.key_release = key; + } + else { + cvar_skey(sys.keywait - 1, key); + sys.keywait = 0; + gui_reformat(sys.keywin); + sys.keywin = NULL; + } + } + } +} + +void gui_fnc_bind_reset(gui_t *elem, int value) { + for(int z = 0; z < KEY_BINDS; z++) { + cvar_skey(z, cvar_defkey(z)); + } + gui_reformat(elem->window); +} + +byte gui_init_binds(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(740, 544); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_BINDS); + int y = 0; + int x = 0; + for(int z = 0; z < KEY_BINDS; z++) { + gui_add_label(win, 20 + x * 180, 120 + y * 50, 160, 20, cvar_getbind(z)); + gui_add_fbutton(win, 20 + x * 180, 140 + y * 50, 160, 24, "", gui_fnc_bind, gui_fmt_bind); + if(++x == 4) { + x = 0; + y++; + } + } + // y += x ? 1 : 0; + gui_add_button(win, 20, 500, 340, 24, STR_RESET, gui_fnc_bind_reset); + // gui_add_nbutton(380, 500, 340, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +void gui_fnc_style_reset(gui_t *elem, int value) { + cvar_t *cv; + window_t *win; + for(int z = 0; z < sys.n_cvars; z++) { + cv = &sys.cvars[z]; + if(cv->category == CVAR_STYLE) + cvar_sdef(cv); + } + for(win = wcf.window_list; win; win = win->next) { + gui_reopen(win); + } +} + +void gui_fnc_style_redraw(gui_t *elem, int value) { + window_t *win; + for(win = wcf.window_list; win; win = win->next) { + gui_reopen(win); + } +} + +static const char *gui_drop_test[] = {"VALUE 1", "VALUE 2", "VALUE 3", "VALUE 4"}; + +static const char *gui_style_cvars[] = { + "color_select", + "color_hover_t", + "color_press_t", + "color_background_t", + "color_border_top", + "color_wborder_top", + + "color_cursor", + "color_hover_b", + "color_press_b", + "color_background_b", + "color_border_btm", + "color_wborder_btm", + + "color_button_top", + "color_textbox_top", + "color_window_top", + "color_label_text", + "color_button_text", + "border_gui", + + "color_button_btm", + "color_textbox_btm", + "color_window_btm", + "color_textbox_text", + "color_window_text", + "width_handle" +}; + +static const char **gui_style_labels[] = { + &STR_GUI_SELCUR_COLOR, + &STR_GUI_HOVER_COLOR, + &STR_GUI_PRESS_COLOR, + &STR_GUI_BACK_COLOR, + &STR_GUI_BORDER_COLOR, + &STR_GUI_WBORDER_COLOR, + + NULL, NULL, NULL, NULL, NULL, NULL, + + &STR_GUI_DESC_BUTTON, + &STR_GUI_DESC_FIELD, + &STR_GUI_DESC_WINDOW, + &STR_GUI_DESC_LABEL, + &STR_GUI_DESC_BUTTON, + &STR_GUI_BORDER_WIDTH, + + NULL, NULL, NULL, + &STR_GUI_DESC_FIELD, + &STR_GUI_DESC_WINDOW, + &STR_GUI_SLIDER_WIDTH +}; + +static const byte gui_style_y[] = { + 1, 2, 4, 5 +}; + +void gui_fnc_style_preset(gui_t *elem, int value) { + memcpy(&sys.style, elem->aux_data, sizeof(style_t)); + cvar_t *cv; + window_t *win; + for(int z = 0; z < 24; z++) { + cv = cvar_get(gui_style_cvars[z]); + if(cv->type == CVAR_INT) + cvar_sint(cv, *((int*)cv->data)); + else if(cv->type == CVAR_COLOR || cv->type == CVAR_COLOR_ALPHA) + cvar_scolor(cv, *((uint*)cv->data)); + } + for(win = wcf.window_list; win; win = win->next) { + gui_reopen(win); + } +} + +void gui_fmt_pixels(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%d px", value); +} + +byte gui_init_style(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(980, 694); + // gui_add_label(20, 20, 140 * 6 + 20 * 5, 24, 1, STR_TITLE_STYLE); + int z = 0; + int s; + ulong iter = 0; + cvar_t *cv; + style_t *theme; + gui_add_label(win, 20, 80 + 10, 140 * 6 + 20 * 5, 20, STR_GUI_GENERAL); + gui_add_label(win, 20, 80 + 3 * 40 + 10, 140 * 3 + 20 * 2, 20, STR_GUI_FILL_COLOR); + gui_add_label(win, 20 + 3 * 160, 80 + 3 * 40 + 10, 140 * 2 + 20 * 1, 20, STR_GUI_TEXT_COLOR); + gui_add_label(win, 20 + 5 * 160, 80 + 3 * 40 + 10, 140, 20, STR_GUI_OTHER); + for(z = 0; z < 24; z++) { + cv = cvar_get(gui_style_cvars[z]); + s = z < 21 ? 0 : 10; + if(cv->type == CVAR_INT) + gui_add_cvslider(win, 20 + (z % 6) * 160, 100 + s + gui_style_y[z / 6] * 40, 140, 24, cv->name, cv->name)->format = gui_fmt_pixels; + else if(cv->type == CVAR_COLOR || cv->type == CVAR_COLOR_ALPHA) + gui_add_cvfield(win, 20 + (z % 6) * 160, 100 + s + gui_style_y[z / 6] * 40, 140, 24, cv->name); + if(gui_style_labels[z]) + gui_add_label(win, 20 + (z % 6) * 160, 80 + s + gui_style_y[z / 6] * 40, 140, 20, *(gui_style_labels[z])); + } + z = 0; + while(theme = tbl_iter(&sys.themes, &iter)) { + gui_add_button(win, 20 + (z % 3) * 320, 80 + (z / 3 + 8) * 40, 300, 24, theme->name, gui_fnc_style_preset)->aux_data = theme; + z++; + } + + gui_add_dropdown(win, 20, 540, 300, 24, "DROPDOWN", gui_fnc_debug, gui_drop_test, 4, 1, 0); + gui_add_button(win, 340, 540, 300, 24, "BUTTON", gui_fnc_debug); + gui_add_toggle(win, 660, 540, 140, 24, "TOGGLE", gui_fnc_debug, 0, 1); + gui_add_toggle(win, 820, 540, 140, 24, "TOGGLE", gui_fnc_debug, 1, 0); + gui_add_enum(win, 20, 580, 300, 24, "ENUM", gui_fnc_debug, gui_drop_test, 4, 2, 0); + gui_add_slider(win, 340, 580, 300, 24, "SLIDER", gui_fnc_sdebug, -20, 827, 60, 120); + gui_add_field(win, 660, 580, 300, 24, "FIELD", 0, NULL, 1, 1); + + gui_add_button(win, 20, 650, 300, 24, STR_APPLY, gui_fnc_style_redraw); + gui_add_button(win, 340, 650, 300, 24, STR_RESET, gui_fnc_style_reset); + // gui_add_nbutton(660, 650, 300, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +void gui_fmt_dclick(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: <= %d ms", elem->format_text, value); +} + +byte gui_init_control(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(740, 544); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_CONTROL); + + gui_add_cvfslider(win, 20, 80, 340, 24, 2, STR_PHY_SENSITIVITY, "phy_sensitivity"); + gui_add_cvfslider(win, 380, 80, 340, 24, 2, STR_PHY_SPEED, "phy_speed"); + + // gui_add_cvfslider(win, 20, 120, 340, 24, 2, STR_PHY_GRAVITY, "phy_gravity"); + // gui_add_cvfslider(win, 380, 120, 340, 24, 2, STR_PHY_FRICTION, "phy_friction"); + + gui_add_cvslider(win, 20, 120, 340, 24, STR_GUI_DCLICK_DELAY, "gui_dclick_delay")->format = gui_fmt_dclick; + + // gui_add_nbutton(200, 500, 340, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +void gui_fmt_sync(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s: %.0d%s", elem->format_text, (value > 0 && value < elem->max) ? (value + 9) : 0, (value > 0 && value < elem->max) ? " FPS" : (value ? STR_UNLIMITED : STR_WIN_VSYNC)); +} + +void gui_fnc_sync(gui_t *elem, int value) { + cvar_sint(elem->cv_data, (value > 0 && value < elem->max) ? (value + 9) : (value ? -1 : 0)); +} + +void gui_list_modes() { + int offset = 0; + int pos = 0; + sys.n_modes = 0; + sys.vid_mode = 0; + const vidmode_t *modes = wcf_getmodes(&sys.n_modes); + const vidmode_t *wmode; + vidmode_t *mode; + if(sys.n_modes > VID_MODES) { + offset = sys.n_modes - VID_MODES; + sys.n_modes = VID_MODES; + } + for(int z = 0; z < sys.n_modes; z++) { + mode = &sys.vid_modes[z]; + wmode = &modes[z + offset]; + mode->width = wmode->width; + mode->height = wmode->height; + mode->refresh = wmode->refresh; + if(sys.win_xfull == mode->width && sys.win_yfull == mode->height && sys.win_refresh == mode->refresh) + sys.vid_mode = z; + } +} + +void gui_fmt_mode(gui_t *elem, int value) { + vidmode_t *mode = &sys.vid_modes[value]; + snprintf(elem->text, elem->capacity, "%s: %dx%d @ %d Hz", elem->format_text, mode->width, mode->height, mode->refresh); +} + +void gui_fmt_mdrop(gui_t *elem, int value) { + vidmode_t *mode; + int pos = 0; + for(int z = 0; z < elem->max && pos < (elem->capacity - 1); z++) { + mode = &sys.vid_modes[z]; + pos += snprintf(&elem->text[pos], elem->capacity - pos, z ? "\n%dx%d @ %d Hz" : "%dx%d @ %d Hz", mode->width, mode->height, mode->refresh); + } +} + +// void win_full(byte full); + +void gui_fnc_mode(gui_t *elem, int value) { + vidmode_t *mode = &sys.vid_modes[value]; + sys.win_xfull = mode->width; + sys.win_yfull = mode->height; + sys.win_refresh = mode->refresh; + // win_full(1); +} + +// void gui_fnc_full(gui_t *elem, int value) { + // win_full((byte)value); +// } + +static const char **gui_mipmap_types[] = { + &STR_GFX_MIPMAP_OFF, + &STR_GFX_MIPMAP_NEAREST, + &STR_GFX_MIPMAP_LINEAR +}; + +static const char **gui_con_positions[] = { + &STR_CON_POS_TOP, + &STR_CON_POS_BTM +}; + +byte gui_init_display(window_t *win, int frame_x, int frame_y, byte open) { + gui_t *elem; + cvar_t *cv; + // gui_size(740, 544); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_DISPLAY); + + // gui_add_toggle(win, 20, 80, 340, 24, STR_WIN_FULLSCREEN, gui_fnc_full, 0, sys.win_full); + gui_list_modes(); + elem = gui_add_dropdown(win, 20, 80, 340, 24, STR_WIN_FULLRES, gui_fnc_mode, NULL, sys.n_modes, sys.n_modes - 1, sys.vid_mode); + elem->format = gui_fmt_mode; + elem = elem + 1; + elem->capacity = VID_MODES*VID_MODE_STR; + elem->text = sys.vid_mode_list; + elem->format = gui_fmt_mdrop; + + cv = cvar_get("win_sync"); + elem = gui_add_slider(win, 20, 120, 340, 24, STR_WIN_SYNC, gui_fnc_sync, 0, 360 - 8, 0, (cv->value < 0) ? (360 - 8) : (cv->value ? ((cv->value < 10) ? 1 : (cv->value - 9)) : 0)); + elem->cv_data = cv; + elem->format = gui_fmt_sync; + gui_add_cvtoggle(win, 380, 120, 340, 24, STR_WIN_FLUSH, "gl_vsync_flush"); + + gui_add_cvtoggle(win, 20, 200, 340, 24, STR_CON_OVERLAY, "con_overlay"); + gui_add_cvslider(win, 380, 200, 340, 24, STR_CON_OPACITY, "con_opacity"); + gui_add_cvslider(win, 20, 240, 340, 24, STR_CON_HEIGHT, "con_size"); + gui_add_cvslider(win, 380, 240, 340, 24, STR_CON_FADEOUT, "con_fadeout")->format = gui_fmt_millis; + gui_add_cvbenum(win, 20, 280, 340, 24, STR_CON_POS, "con_position", gui_con_positions); + // gui_add_cvtoggle(win, 380, 280, 340, 24, STR_CON_MONOFONT, "con_monospace"); + + // gui_add_nbutton(200, 500, 340, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +byte gui_init_graphics(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(740, 544); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_GRAPHICS); + + gui_add_cvfslider(win, 20, 80, 340, 24, 1, STR_GFX_FOV, "gl_fov")->format = gui_fmt_degrees; + + gui_add_label(win, 20, 120, 160, 24, STR_GFX_AMBIENT); + gui_add_cvfield(win, 200, 120, 160, 24, "gl_light_color"); + gui_add_cvfslider(win, 380, 120, 340, 24, 2, STR_GFX_AMBIENT_X, "gl_ambient_x"); + + gui_add_cvfslider(win, 20, 160, 340, 24, 2, STR_GFX_AMBIENT_Y, "gl_ambient_y"); + gui_add_cvfslider(win, 380, 160, 340, 24, 2, STR_GFX_AMBIENT_Z, "gl_ambient_z"); + + gui_add_cvslider(win, 20, 200, 340, 24, STR_GFX_LIGHT_MAX, "gl_dynlight_max"); + gui_add_cvslider(win, 380, 200, 340, 24, STR_GFX_LIGHT_CHUNK, "gl_dynlight_chunkrange")->format = gui_fmt_uchunks; + + gui_add_cvfslider(win, 20, 240, 340, 24, 1, STR_GFX_LIGHT_VIEW, "gl_dynlight_viewdist"); + gui_add_cvfslider(win, 380, 240, 340, 24, 1, STR_GFX_LIGHT_VERT, "gl_dynlight_maxdist"); + + gui_add_cvfslider(win, 20, 280, 340, 24, 2, STR_GFX_LIGHT_BLEND, "gl_light_blend")->format = gui_fmt_pslider; + + gui_add_cvtoggle(win, 20, 320, 340, 24, STR_GFX_TEXFILTER, "gl_tex_filter"); + gui_add_cvbenum(win, 380, 320, 340, 24, STR_GFX_MIPMAPS, "gl_tex_mipmaps", gui_mipmap_types); + + gui_add_cvfslider(win, 20, 360, 340, 24, 1, STR_GFX_ANISOTROPIC, "gl_tex_anisotropic"); // ->enabled = gdr.aniso_avail; + + gui_add_cvslider(win, 20, 400, 340, 24, STR_GFX_DRAWDIST_H, "chunk_view_horizontal")->format = gui_fmt_uchunks; + gui_add_cvslider(win, 380, 400, 340, 24, STR_GFX_DRAWDIST_V, "chunk_view_vertical")->format = gui_fmt_uchunks; + + // gui_add_nbutton(200, 500, 340, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +void snd_restart(); + +void gui_fnc_reset_snd(gui_t *elem, int value) { + snd_restart(); +} + +static const char **gui_sample_types[] = { + &STR_SND_FMT_S16LE, + &STR_SND_FMT_S32LE +}; + +static const char **gui_device_types[] = { + &STR_SND_DEV_DEFAULT, + &STR_SND_DEV_NULL, + &STR_SND_DEV_JACK, + &STR_SND_DEV_PULSE, + &STR_SND_DEV_HARDWARE, + &STR_SND_DEV_SOFTWARE, + &STR_SND_DEV_NONE +}; + +static const char **gui_volumes[] = { + &STR_SND_VOL_MASTER, + &STR_SND_VOL_MUSIC, + &STR_SND_VOL_SFX, + &STR_SND_VOL_GUI +}; + +static const int snd_sample_rates[] = {22050, 44100, 48000, 96000, 192000}; +static const char *snd_sample_srates[] = {"22050 Hz [LQ]", "44100 Hz [CD]", "48000 Hz [DAT]", "96000 Hz [HQ]", "192000 Hz [XQ]"}; + +void gui_fnc_snd_rate(gui_t *elem, int value) { + cvar_setint("snd_sample_rate", snd_sample_rates[value]); +} + +void gui_fmt_velofunc(gui_t *elem, int value) { + snprintf(elem->text, elem->capacity, "%s %d: %s", elem->format_text, value, (value ? (value == -128 ? STR_MID_VELO_ONE : (value < 0 ? STR_MID_VELO_ATTN : STR_MID_VELO_LOG)) : STR_MID_VELO_LIN)); +} + +byte gui_init_sound(window_t *win, int frame_x, int frame_y, byte open) { + // gui_size(740, 604 + 40 * (SND_VOLUMES / 2)); + // gui_add_label(20, 20, 700, 24, 1, STR_TITLE_SOUND); + + int rate = cvar_int("snd_sample_rate"); + byte rpos; + for(rpos = 4; rpos > 0; rpos--) { + if(rate >= snd_sample_rates[rpos]) + break; + } + + gui_add_cvbdrop(win, 20, 80, 340, 24, STR_SND_DEVTYPE, "snd_device_type", gui_device_types); + + gui_add_label(win, 20, 120, 280, 24, STR_SND_DEVICE_ID); + gui_add_cvfield(win, 300, 120, 60, 24, "snd_device_index"); + gui_add_label(win, 380, 120, 280, 24, STR_SND_DEVICE_SUB); + gui_add_cvfield(win, 660, 120, 60, 24, "snd_device_sub"); + + gui_add_dropdown(win, 20, 160, 340, 24, STR_SND_SAMPLERATE, gui_fnc_snd_rate, snd_sample_srates, 5, 2, rpos); + gui_add_cvbdrop(win, 380, 160, 340, 24, STR_SND_FORMAT, "snd_sample_format", gui_sample_types); + + gui_add_cvslider(win, 20, 200, 340, 24, STR_SND_BUFSIZE, "snd_buffer_size"); + gui_add_cvslider(win, 380, 200, 340, 24, STR_SND_FRAMESIZE, "snd_frame_size"); + + gui_add_button(win, 20, 260, 700, 24, STR_SND_RESTART, gui_fnc_reset_snd); + + gui_add_label(win, 20, 320, 700, 24, STR_TITLE_MIDI); + + gui_add_cvtoggle(win, 20, 360, 340, 24, STR_MID_USEUNKN, "mid_play_unknown"); + gui_add_cvtoggle(win, 380, 360, 340, 24, STR_MID_KEEPNOTES, "mid_keep_notes"); + gui_add_cvtoggle(win, 20, 400, 340, 24, STR_MID_DONTFADE, "mid_dont_fade"); + gui_add_cvtoggle(win, 380, 400, 340, 24, STR_MID_DEBUGEVT, "mid_debug_events"); + gui_add_cvslider(win, 20, 440, 340, 24, STR_MID_VELOFUNC, "mid_velocity_func")->format = gui_fmt_velofunc; + gui_add_cvslider(win, 380, 440, 340, 24, STR_MID_OPVOICES, "mid_opl_voices"); + + gui_add_label(win, 20, 500, 700, 24, STR_TITLE_VOLUME); + + int x = 20; + int y = 540; + for(int z = 0; z < SND_VOLUMES; z++) { + gui_add_cvfslider(win, x, y, 340, 24, -1, *(gui_volumes[z]), snd_cvar_volume[z]); + x = (x == 20) ? 380 : 20; + if(x == 20) + y += 40; + } + + // gui_add_nbutton(200, y + 20, 340, 24, 1, STR_BACK, GUI_OPTIONS); + // gui_reformat(); + // gui_init_options(win, frame_x, frame_y, open); + return 0; +} + +byte gui_init_info(window_t *win, int frame_x, int frame_y, byte open) { + gui_add_field(win, 0, 0, frame_x, frame_y, SYS_INFO, sizeof(SYS_INFO), NULL, 0, 0); + return 0; +} + +void plr_mode(byte repeat); +void plr_pause(); +void plr_stop(); +void plr_resume(); +void plr_jump(int pos); +void plr_next(); +void plr_prev(); +void plr_rand(); + +void gui_plr_update_text(window_t *win) { + gui_t *elem = gui_get(win, 17); + const char *name = ""; + int pos = (snd.mid_queue && (snd.mid_playpos != INT_MIN)) ? ((snd.mid_playpos < -1) ? (-(snd.mid_playpos) - 3) : snd.mid_playpos) : -1; + if(pos >= 0) { + if(pos != (snd.mid_queued - 1)) + snd.mid_queue[pos+1][-1] = 0; + if(name = strrchr(&(snd.mid_queue[pos])[6], '/')) + name += 1; + else + name = &(snd.mid_queue[pos])[6]; + snprintf(elem->text, elem->capacity, "%s: %d/%d - %s", STR_PLR_INFO, pos + 1, snd.mid_queued, name); + if(pos != (snd.mid_queued - 1)) + snd.mid_queue[pos+1][-1] = '\n'; + } + else { + snprintf(elem->text, elem->capacity, "%s", STR_PLR_INFO); + } + gui_update_text(elem); +} + +void gui_plr_update(window_t *win) { + gui_t *elem = gui_get(win, 2); + snprintf(elem->text, elem->capacity, snd.mid_queue && (snd.mid_playpos >= -1) ? COL_GREEN SYM_PLAY : SYM_PLAY); + gui_update_text(elem); + elem = gui_get(win, 3); + snprintf(elem->text, elem->capacity, !snd.mid_queue || (snd.mid_playpos == INT_MIN) ? COL_RED SYM_STOP : SYM_STOP); + gui_update_text(elem); + elem = gui_get(win, 4); + snprintf(elem->text, elem->capacity, snd.mid_queue && (snd.mid_playpos != INT_MIN) && (snd.mid_playpos < -1) ? COL_YELLOW SYM_PAUSE : SYM_PAUSE); + gui_update_text(elem); + gui_plr_update_text(win); +} + +void gui_fnc_plr_start(gui_t *elem, int value) { + plr_resume(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_stop(gui_t *elem, int value) { + plr_stop(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_pause(gui_t *elem, int value) { + plr_pause(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_mode(gui_t *elem, int value) { + plr_mode((byte)value); +} + +void gui_fnc_plr_prev(gui_t *elem, int value) { + plr_prev(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_next(gui_t *elem, int value) { + plr_next(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_rand(gui_t *elem, int value) { + plr_rand(); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); +} + +void gui_fnc_plr_clear(gui_t *elem, int value) { + plr_stop(); + plr_end(); + elem = gui_get(elem->window, 1); + elem->text = &elem->window->strings[GUI_STR_SIZE * elem->id]; + elem->text[0] = 0; + elem->capacity = GUI_STR_SIZE; + gui_update_text(elem); + elem->sel_start = elem->sel_end = elem->sel_drag = 0; + gui_text_update_cur(elem, elem->sel_start, 1); + elem->sel_start = elem->sel_end = elem->sel_drag = -1; + gui_plr_update(elem->window); +} + +void gui_fnc_plr_open(gui_t *elem, int value) { +} + +static const char *gui_plr_modes[] = { + SYM_CONTINUOS, + SYM_REPEAT, + SYM_LOOPED, + SYM_SHUFFLE +}; + +void gui_fnc_plr_jump(gui_t *elem, int value) { + if(!value && str_parse_int(elem->text, &value, -10) && value && (value <= snd.mid_queued)) { + plr_jump(value - 1); + snprintf(elem->text, elem->capacity, SYM_JUMPTO "#"); + gui_update_text(elem); + gui_deselect(elem->window); + gui_text_update_cur(elem, elem->sel_start, 1); + gui_update_text(gui_get(elem->window, 1)); + gui_plr_update(elem->window); + } + else if(value == 4) { + elem->text[0] = 0; + gui_update_text(elem); + gui_text_update_cur(elem, elem->sel_start, 1); + } + else if(value == -4) { + snprintf(elem->text, elem->capacity, SYM_JUMPTO "#"); + gui_update_text(elem); + gui_text_update_cur(elem, elem->sel_start, 1); + } +} + +void gui_fnc_plr_load(gui_t *elem, int value) { + if(!value) { + int count; + const char **files = file_list(&count, elem->text, mid_extensions, 3); + if(files) { + count = count > 999 ? 999 : count; + plr_queue(snd_bankfiles[snd.mid_bank], NULL, files, count); + mem_free(files); + elem->text[0] = 0; + gui_update_text(elem); + gui_deselect(elem->window); + gui_text_update_cur(elem, elem->sel_start, 1); + elem = gui_get(elem->window, 1); + elem->text = snd.mid_list; + elem->capacity = strlen(snd.mid_list) + 1; + gui_update_text(elem); + elem->sel_start = elem->sel_end = elem->sel_drag = 0; + gui_text_update_cur(elem, elem->sel_start, 1); + elem->sel_start = elem->sel_end = elem->sel_drag = -1; + gui_plr_update(elem->window); + } + } +} + +// void gui_fnc_plr_bank(gui_t *elem, int value) { +// snd.mid_bank = (byte)value; +// } + +void gui_fmt_bankdrop(gui_t *elem, int value) { + int pos = 0; + for(int z = 0; z < elem->max && pos < (elem->capacity - 1); z++) { + pos += snprintf(&elem->text[pos], elem->capacity - pos, z ? "\n%s%s" : "%s%s", ((const char **)elem->aux_data)[z], z < BNK_IDX_MELO_ONLY ? "" : (z < BNK_IDX_DRUM_ONLY ? " [0-127]" : " [128-255]")); + } +} + +static const uint plr_waveforms[] = {0xff00ff00, 0xffff0000, 0xff0000ff, 0xff00ffff, 0xffff00ff, 0xffffff00, 0xff80ff00, 0xff00ff80}; + +int gui_render_bar(gui_t *elem, int x, int y, int w, int h, uint top, uint bottom, uint bg, int v) { + // if(v > 0) + v = (v < 0) ? 0 : v; + gfx_draw_rect(elem->pos_x + x, elem->pos_y + y, w, h, 0, 0, 0xff000000 | top, 0xff000000 | bottom, 0, 0); + if(++v < h) + gfx_draw_rect(elem->pos_x + x, elem->pos_y + y, w, h - v, 0, 0, 0xff000000 | bg, 0xff000000 | bg, 0, 0); + return h; +} + +int gui_render_grad(gui_t *elem, int x, int y, int w, int h, uint top, uint bottom) { + gfx_draw_rect(elem->pos_x + x, elem->pos_y + y, w, h, 0, 0, 0xff000000 | top, 0xff000000 | bottom, 0, 0); + return h; +} + +int gui_render_rect(gui_t *elem, int x, int y, int w, int h, uint color) { + gfx_draw_rect(elem->pos_x + x, elem->pos_y + y, w, h, 0, 0, 0xff000000 | color, 0xff000000 | color, 0, 0); + return h; +} + +int gui_render_text(gui_t *elem, int x, int y, uint color, const char *text) { + txt_draw(elem->pos_x + x, elem->pos_y + y, 0, sys.font.yglyph, sys.font.yglyph, elem->pos_x + x, elem->pos_y + y, elem->pos_x + elem->size_x, elem->pos_y + elem->size_y, + 0xff000000 | color, 0x00000000, &sys.font, text); + return sys.font.yglyph; +} + +void gui_render_player(gui_t *elem, int value) { + elem->t_dirty = 1; + if(!value) + // return; + // gfx_draw_rect(elem->pos_x, elem->pos_y, elem->size_x, elem->size_y, 0, 0, 0xff404040, 0xff404040, 0, 0); + gui_render_grad(elem, 0, 0, elem->size_x, elem->size_y, sys.style.field_btm, sys.style.field_top); + if(snd.mid_visual ^ 1) + return; + pthread_mutex_lock(&sgt.lock); + if(!sgt.chip || !sgt.bank) { + pthread_mutex_unlock(&sgt.lock); + return; + } + opl3_chip *chip = sgt.chip; + bank_handle *bank = sgt.bank; + int nch = chip->n_voices; + int bx = elem->size_x / 2 - (nch * 6 + 2) / 2; + int by = elem->size_y / 2 - (698 + 20 * 4) / 2; + int x = bx; + int y = by; + if(value) { + x = bx - 84; + sprintf(&sys.work_buf[str_time(sys.work_buf, sgt.mid_time)], "\n%c%c%c%c %c", + (sgt.mid.tpqn && (((sgt.mid_tick / sgt.mid.tpqn) & 3) == 0)) ? '*' : '.', + (sgt.mid.tpqn && (((sgt.mid_tick / sgt.mid.tpqn) & 3) == 1)) ? '*' : '.', + (sgt.mid.tpqn && (((sgt.mid_tick / sgt.mid.tpqn) & 3) == 2)) ? '*' : '.', + (sgt.mid.tpqn && (((sgt.mid_tick / sgt.mid.tpqn) & 3) == 3)) ? '*' : '.', + plr_wheel[(sgt.mid_tick >> 5) & 3]); + gui_render_text(elem, x, y + 20, 0xffffff, sys.work_buf); + sprintf(sys.work_buf, COL_NEON"%s"COL_RESET"\n%d", STR_OPL_TICK, sgt.mid_tick); + gui_render_text(elem, x, y + 60, 0xffffff, sys.work_buf); + sprintf(sys.work_buf, COL_NEON"%s"COL_RESET"\n%d / %d", STR_OPL_ACTIVE, bank->v_used, bank->v_avail); + gui_render_text(elem, x, y + 100, 0xffffff, sys.work_buf); + sprintf(sys.work_buf, COL_NEON"%s"COL_RESET"\n%d +%dQ", STR_OPL_TEMPO, sgt.mid.uspb ? (60000000 / sgt.mid.uspb) : 0, sgt.mid.tpqn); + gui_render_text(elem, x, y + 140, 0xffffff, sys.work_buf); + x = bx; + pthread_mutex_unlock(&sgt.lock); + y += 3; + gui_render_text(elem, x, y, 0xffffff, STR_OPL_CHANNEL); + gui_render_text(elem, x, y + 20 + 226, 0xffffff, STR_OPL_MODULATOR); + gui_render_text(elem, x, y + 40 + 226 + 232, 0xffffff, STR_OPL_CARRIER); + gui_render_text(elem, x, y + 60 + 226 + 232 * 2, 0xffffff, STR_OPL_POINTER); + y -= 3; + // x -= 86; + x += nch * 6 + 2 + 1; + y += 20; + gui_render_text(elem, x, y + 0, 0xffffff, STR_OPL_OUTPUT); + gui_render_text(elem, x, y + 70, 0xffffff, STR_OPL_MODE); + gui_render_text(elem, x, y + 84, 0xffffff, STR_OPL_FEEDBACK); + gui_render_text(elem, x, y + 98, 0xffffff, STR_OPL_FREQUENCY); + y += 226 + 20; + for(int o = 0; o < 2; o++) { + gui_render_text(elem, x, y + 6, 0xffffff, STR_OPL_FLAGS); + gui_render_text(elem, x, y + 24, 0xffffff, STR_OPL_MULTIPLIER); + gui_render_text(elem, x, y + 42, 0xffffff, STR_OPL_LEVEL); + gui_render_text(elem, x, y + 96, 0xffffff, STR_OPL_KEYSCALE); + gui_render_text(elem, x, y + 110, 0xffffff, STR_OPL_WAVEFORM); + gui_render_text(elem, x, y + 124, 0xffffff, STR_OPL_ATTACK); + gui_render_text(elem, x, y + 142, 0xffffff, STR_OPL_DECAY); + gui_render_text(elem, x, y + 160, 0xffffff, STR_OPL_SUSTAIN); + gui_render_text(elem, x, y + 178, 0xffffff, STR_OPL_RELEASE); + gui_render_text(elem, x, y + 196, 0xffffff, STR_OPL_ENVELOPE); + y += 232 + 20; + } + return; + } + y += 20; + y += gui_render_rect(elem, x, y, nch * 6 + 2, 226, 0x3f0000) + 20; + y += gui_render_rect(elem, x, y, nch * 6 + 2, 232, 0x003f00) + 20; + y += gui_render_rect(elem, x, y, nch * 6 + 2, 232, 0x00003f) + 20; + y += gui_render_rect(elem, x, y, nch * 6 + 2, 8, 0x2f2f2f); + for(int c = 0; c < nch; c++) { + opl3_channel *channel = &chip->channel[c]; + x = bx + 2 + 6 * c; + y = by + 2; + short **out = channel->out; + short accm = *out[0] + *out[1] + *out[2] + *out[3]; + int chnlvl = 0; + for(int n = 0; n < 2; n++) { + chnlvl += (short)((accm * channel->level[n]) >> 16); + } + chnlvl = chnlvl < 0 ? (-chnlvl) : chnlvl; + chnlvl = (chnlvl * 16) / 2048; + chnlvl = chnlvl > 64 ? 64 : chnlvl; + int freq = (int)((((float)channel->f_num * 49716.0f) / powf(2.0f, 20.0f - (float)channel->block)) / 6210.0f * 128.0f); + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10, 4, 64, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 - chnlvl, 4, chnlvl, 0, 0, 0xffff0000, 0xff00ff00, 0, 0); + */ + y += 20; + y += gui_render_bar(elem, x, y, 4, 64, 0xff0000, 0x00ff00, 0x000000, chnlvl) + 2; + // int vid = c + 1; + // byte active = (channel->slots[0]->eg_out <= 0x100) || (channel->slots[1]->eg_out <= 0x100); + y += gui_render_rect(elem, x, y, 4, 4, ((channel->slots[0]->eg_out <= 0x100) || (channel->slots[1]->eg_out <= 0x100)) ? 0x00ff00 : 0x000000) + 2; + y += gui_render_rect(elem, x - (c & 1), y, 5, 4, channel->chtype != ch_2op ? 0xff00ff : 0x000000) + 2; + y += gui_render_grad(elem, x, y, 4, 4, channel->con ? 0x00ff00 : 0x808080, 0x0000ff) + 2; + y += gui_render_bar(elem, x, y, 4, 8, 0x00ff00, 0xffff00, 0x000000, channel->fb) + 2; + y += gui_render_bar(elem, x, y, 4, 128, 0x707070, 0x505050, 0x000000, freq) + 2; + gui_render_rect(elem, x, y - 130 + (127 - freq), 4, 1, 0xff0000); + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 2, 4, 4, 0, 0, active ? 0xff00ff00 : 0xff000000, active ? 0xff00ff00 : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c - (c & 1), elem->pos_y + 10 + 64 + 8, 5, 4, 0, 0, channel->chtype != ch_2op ? 0xffff00ff : 0xff000000, + channel->chtype != ch_2op ? 0xffff00ff : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 14, 4, 4, 0, 0, channel->con ? 0xff00ff00 : 0xff808080, 0xff0000ff, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 20, 4, 4 * (7 - channel->fb), 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 20 + 4 * (7 - channel->fb), 4, 4 * channel->fb, 0, 0, 0xff00ff00, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 50, 4, 128, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 50 + 128 - freq, 4, 1, 0, 0, 0xffff0000, 0xffff0000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + 10 + 64 + 50 + 129 - freq, 4, freq - 1, 0, 0, 0xff707070, 0xff505050, 0, 0); + */ + for(int o = 0; o < 2; o++) { + opl3_slot *slot = channel->slots[o]; + /* + byte trem = slot->trem != (byte*)&slot->chip->zeromod; + byte sustain = (slot->reg_sl > 15) ? 0 : (15 - slot->reg_sl); + */ + y += 2; + y += 20; + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c + 0, elem->pos_y + y, 2, 4, 0, 0, o ? 0xff000000 : 0xff00ff00, o ? 0xff000000 : 0xff00ff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c + 2, elem->pos_y + y, 2, 4, 0, 0, o ? 0xff0000ff : 0xff000000, o ? 0xff0000ff : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 6), 4, 4, 0, 0, slot->key ? 0xff00ff00 : 0xff000000, slot->key ? 0xff00ff00 : 0xff000000, 0, 0); + */ + // gui_render_rect(elem, x, y, 2, 4, o ? 0x000000 : 0x00ff00); + // y += gui_render_rect(elem, x + 2, y, 2, 4, o ? 0x0000ff : 0x000000) + 2; + y += gui_render_rect(elem, x, y, 4, 4, slot->key ? 0x00ff00 : 0x000000) + 2; + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 6), 4, 4, 0, 0, trem ? 0xff00ff00 : 0xff000000, trem ? 0xff00ff00 : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 4), 4, 4, 0, 0, slot->reg_vib ? 0xffffff00 : 0xff000000, slot->reg_vib ? 0xffffff00 : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 4), 4, 4, 0, 0, slot->reg_type ? 0xffff0000 : 0xff000000, slot->reg_type ? 0xffff0000 : 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 4), 4, 4, 0, 0, slot->reg_ksr ? 0xffbf00bf : 0xff000000, slot->reg_ksr ? 0xffbf00bf : 0xff000000, 0, 0); + */ + y += gui_render_rect(elem, x, y, 4, 4, (slot->trem != (byte*)&slot->chip->zeromod) ? 0x5fdf00 : 0x000000); + y += gui_render_rect(elem, x, y, 4, 4, slot->reg_vib ? 0xffff00 : 0x000000); + y += gui_render_rect(elem, x, y, 4, 4, slot->reg_type ? 0xff0000 : 0x000000); + y += gui_render_rect(elem, x, y, 4, 4, slot->reg_ksr ? 0x5f00bf : 0x000000) + 2; + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 6), 4, 2 * (15 - slot->reg_mult), 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 * (15 - slot->reg_mult)), 4, 2 * slot->reg_mult, 0, 0, 0xffff0000, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + 2 * slot->reg_mult), 4, slot->reg_tl, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += slot->reg_tl), 4, 63 - slot->reg_tl, 0, 0, 0xffff0000, 0xff00ff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + 63 - slot->reg_tl), 4, 2 * (3 - slot->reg_ksl), 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 * (3 - slot->reg_ksl)), 4, 2 * slot->reg_ksl, 0, 0, 0xff00ff00, 0xffffff00, 0, 0); + */ + y += gui_render_bar(elem, x, y, 4, 16, 0xff0000, 0xffff00, 0x000000, slot->reg_mult) + 2; + y += gui_render_bar(elem, x, y, 4, 64, 0xff0000, 0x00ff00, 0x000000, 63 - slot->reg_tl) + 2; + y += gui_render_bar(elem, x, y, 4, 4, 0x7f00ff, 0x3f00ff, 0x000000, slot->reg_ksl) + 2; + y += gui_render_rect(elem, x, y, 4, 8, 0x000000) + 2; + gui_render_rect(elem, x, y - 10 + (7 - slot->reg_wf), 4, 1, plr_waveforms[slot->reg_wf]); + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + 2 * slot->reg_ksl), 4, 2 * 8, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 * slot->reg_wf), 4, 2, 0, 0, plr_waveforms[slot->reg_wf], plr_waveforms[slot->reg_wf], 0, 0); + */ + + y += gui_render_bar(elem, x, y, 4, 16, slot->eg_gen == 0 ? 0xff0000 : 0xaf0000, slot->eg_gen == 0 ? 0x00ffff : 0x00afaf, 0x000000, slot->reg_ar) + 2; + y += gui_render_bar(elem, x, y, 4, 16, slot->eg_gen == 1 ? 0xff0000 : 0xaf0000, slot->eg_gen == 1 ? 0x00ffff : 0x00afaf, 0x000000, slot->reg_dr) + 2; + y += gui_render_bar(elem, x, y, 4, 16, slot->eg_gen == 2 ? 0xff0000 : 0xaf0000, slot->eg_gen == 2 ? 0x00ffff : 0x00afaf, 0x000000, (slot->reg_sl > 15) ? 0 : (15 - slot->reg_sl)) + 2; + y += gui_render_bar(elem, x, y, 4, 16, slot->eg_gen == 3 ? 0xff0000 : 0xaf0000, slot->eg_gen == 3 ? 0x00ffff : 0x00afaf, 0x000000, slot->reg_rr) + 2; + y += gui_render_bar(elem, x, y, 4, 32, 0xff0000, 0x0000ff, 0x000000, (0x1ff - slot->eg_rout) / 16) + 2; + /* + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + 2 * (8 - slot->reg_wf)), 4, 15 - slot->reg_ar, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 15 - slot->reg_ar), 4, slot->reg_ar, 0, 0, slot->eg_gen == 0 ? 0xff00ff00 : 0xffff0000, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + slot->reg_ar), 4, 15 - slot->reg_dr, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 15 - slot->reg_dr), 4, slot->reg_dr, 0, 0, slot->eg_gen == 1 ? 0xff00ff00 : 0xffff0000, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + slot->reg_dr), 4, 15 - sustain, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 15 - sustain), 4, sustain, 0, 0, slot->eg_gen == 2 ? 0xff00ff00 : 0xffff0000, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + sustain), 4, 15 - slot->reg_rr, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 15 - slot->reg_rr), 4, slot->reg_rr, 0, 0, slot->eg_gen == 3 ? 0xff00ff00 : 0xffff0000, 0xffffff00, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2 + slot->reg_rr), 4, slot->eg_rout / 16, 0, 0, 0xff000000, 0xff000000, 0, 0); + gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += slot->eg_rout / 16), 4, (0x1ff - slot->eg_rout) / 16, 0, 0, 0xffff0000, 0xff0000ff, 0, 0); + y += (0x1ff - slot->eg_rout) / 16; + */ + } + y += 2; + y += 20; + gui_render_rect(elem, x, y, 4, 4, bank->voiceindex == c ? 0x00ffff : 0x000000); + // gfx_draw_rect(elem->pos_x + 10 + 6 * c, elem->pos_y + (y += 2), 4, 4, 0, 0, bank->voiceindex == c ? 0xff00ffff : 0xff000000, bank->voiceindex == c ? 0xff00ffff : 0xff000000, 0, 0); + } + pthread_mutex_unlock(&sgt.lock); +} + +byte gui_init_player(window_t *win, int frame_x, int frame_y, byte open) { + gui_add_fill(win, 0, 0, 696, 24, STR_TITLE_PLR_FULL); + + gui_add_field(win, 0, 24, 720, frame_y - 316, snd.mid_queue ? snd.mid_list : "", snd.mid_queue ? (strlen(snd.mid_list) + 1) : 0, NULL, 0, 0); + + gui_add_button(win, 0, frame_y - 24, 24, 24, SYM_PLAY, gui_fnc_plr_start); + gui_add_button(win, 24, frame_y - 24, 24, 24, SYM_STOP, gui_fnc_plr_stop); + gui_add_button(win, 48, frame_y - 24, 24, 24, SYM_PAUSE, gui_fnc_plr_pause); + gui_add_enum(win, 72, frame_y - 24, 24, 24, STR_PLR_MODE, gui_fnc_plr_mode, gui_plr_modes, 4, 0, snd.mid_repeat)->format = gui_fmt_enums; + + gui_add_button(win, 120, frame_y - 24, 24, 24, SYM_PREV, gui_fnc_plr_prev); + gui_add_button(win, 144, frame_y - 24, 24, 24, SYM_NEXT, gui_fnc_plr_next); + gui_add_button(win, 168, frame_y - 24, 24, 24, SYM_PRANDOM, gui_fnc_plr_rand); + gui_add_field(win, 192, frame_y - 24, 48, 24, SYM_JUMPTO "#", 0, gui_fnc_plr_jump, 1, 1); + + gui_add_button(win, 264, frame_y - 24, 24, 24, SYM_DELETE, gui_fnc_plr_clear); + cvar_t *cv = cvar_get("mid_opl_bank"); + gui_t *elem = gui_add_dropup(win, 288, frame_y - 24, 192, 24, STR_MID_BANK, gui_fnc_cvar_bool, snd_banknames, cv->max, cv->def, cv->value); + elem->cv_data = cv; + elem = elem + 1; + elem->capacity = GUI_AUX_STR; + elem->text = win->str_aux; + elem->format = gui_fmt_bankdrop; + elem->cv_data = cv; + gui_add_field(win, 480, frame_y - 24, 216, 24, "", 0, gui_fnc_plr_load, 1, 1); + gui_add_button(win, 696, frame_y - 24, 24, 24, SYM_FOLDER, gui_fnc_plr_open); + + gui_add_field(win, 0, frame_y - 268, 720, 120, snd.mid_info, 6 * SND_INFO, NULL, 0, 0); + gui_add_field(win, 0, frame_y - 124, 720, 100, snd.mid_klogger, SND_KLOG * SND_KAR, NULL, 0, 0); + + gui_add_fill(win, 0, frame_y - 292, 720, 24, STR_PLR_INFO); + gui_add_fill(win, 96, frame_y - 24, 24, 24, ""); + gui_add_fill(win, 240, frame_y - 24, 24, 24, ""); + gui_add_fill(win, 0, frame_y - 148, 720, 24, STR_PLR_KARAOKE); + + gui_add_custom(win, 720, 0, frame_x - 720, frame_y, gui_render_player); + + gui_add_cvtoggle(win, 696, 0, 24, 24, "V", "mid_visualizer")->format = gui_fmt_keep; + + // gui_add_nbutton(win, 0, 0, 160, 24, STR_BACK, GUI_MENU); + gui_plr_update(win); + // gui_reformat(); + return 0; +} + + +window_t *win_open(int sx, int sy, int wx, int wy, int mx, int my, byte fixed, const char *title, gui_initfnc *initializer, byte type); + +byte win_isopen(gui_initfnc *initializer) { + window_t *win; + for(win = wcf.window_list; win; win = win->next) { + if(win->initializer == initializer) + return 1; + } + return 0; +} + +void gui_fnc_optwin(gui_t *elem, int value) { + if(!win_isopen(gui_init_options)) + win_open(-1, 0, 1280, 800, 1280, 800, 0, "", gui_init_options, 3); +} + +void gui_fnc_plrwin(gui_t *elem, int value) { + if(!snd.player) + snd.player = win_open(-1, 0, 1280, 800, 1280, 800, 0, STR_TITLE_PLAYER, gui_init_player, 1); +} + +void test(window_t *win); + +void gui_fnc_testwin(gui_t *elem, int value) { + test(win_open(1, 0, 960, 600, 0, 0, 0, "", gui_init_world, 1)); +} + +void gui_fnc_infowin(gui_t *elem, int value) { + if(!win_isopen(gui_init_info)) + win_open(1, 0, 700, 410, 0, 0, 1, STR_TITLE_INFO, gui_init_info, 1); +} + + +void gui_fnc_win_minimize(gui_t *elem, int value) { + wcf_minimize(elem->window); +} + +void gui_fnc_win_float(gui_t *elem, int value) { + wcf_floating(elem->window, (byte)value); +} + +void gui_fnc_win_close(gui_t *elem, int value) { + win_closed(elem->window); +} + +void gui_shift(window_t *win, byte shift) { + int shift_x = win->offset_x + (shift ? ((win->frame_x - (win->max_x - win->min_x)) / 2 - win->min_x) : 0); + int shift_y = win->offset_y + (shift ? ((win->frame_y - (win->max_y - win->min_y)) / 2 - win->min_y) : 0); + gui_t *elem; + for(int z = 0; z < win->n_elems; z++) { + elem = &win->elems[z]; + elem->pos_x += shift_x; + elem->pos_y += shift_y; + if(elem->type == GUI_FIELD) { + elem->min += shift_x; + elem->max += shift_y; + } + } +} + +void gui_init(window_t *win) { + byte shift; + // sys.mouse_clickx = INT_MIN; + // sys.mouse_clicky = INT_MIN; + win->selected = NULL; + win->tmr_leftmb = 0ULL; + win->min_x = win->min_y = INT_MAX; + win->max_x = win->max_y = INT_MIN; + win->n_elems = 0; + shift = win->initializer(win, win->frame_x, win->frame_y, win->open); + gui_reformat(win); + if(win->fb_x && win->fb_y) + gui_shift(win, shift); + if(sys.win_full == win) + return; + gui_t *header; + gui_set_style(gui_add_button(win, win->fb_x - WIN_TITLEBAR * 3, 0, WIN_TITLEBAR, WIN_TITLEBAR, "_", gui_fnc_win_minimize), GUI_CUSTOM, 1); + header = gui_set_style(gui_add_toggle(win, win->fb_x - WIN_TITLEBAR * 2, 0, WIN_TITLEBAR, WIN_TITLEBAR, "#", gui_fnc_win_float, 0, win->floating), GUI_CUSTOM, 1); + header->format = gui_fmt_keep; + header->format(header, header->value); + gui_update_text(header); + gui_set_style(gui_add_button(win, win->fb_x - WIN_TITLEBAR * 1, 0, WIN_TITLEBAR, WIN_TITLEBAR, "X", gui_fnc_win_close), GUI_CUSTOM, 1); + header = gui_set_style(gui_add_label(win, 0, 0, win->fb_x - WIN_TITLEBAR * 3, WIN_TITLEBAR, ""), GUI_CUSTOM, 3); + snprintf(header->text, header->capacity, "%s - %s", SYS_PROGRAM, win->title); + gui_update_text(header); + wcf_title(win, header->text); + // layout +} + +void win_menu(window_t *win, byte menu); + +void gui_check(window_t *win) { + if(win->queue != GUI_INVALID) { + // if(win->queue != GUI_REFRESH) + win->open = win->queue; + win->queue = GUI_INVALID; + win_menu(win, win->open ? 1 : 0); + gui_init(win); + win->redraw = 1; + if(!win->open) + sys.mouse_bind = 0; + } +} + +void gui_await(window_t *win) { + if(win->selected && ((win->scrollx && !(win->selected->xbreak)) || (win->scrolly && win->selected->xbreak))) { + int n; + if(!(win->selected->xbreak)) + win->selected->text_x += (n = (int)((float)((float)win->tmr_scroll) / 1000000.0f * 4.0f * ((float)win->scrollx))); + else + win->selected->text_y += (n = (int)((float)((float)win->tmr_scroll) / 1000000.0f * 4.0f * ((float)win->scrolly))); + if(n) { + gui_text_clamp_scroll(win->selected); + win->selected->t_dirty = 1; + } + if((((ulong)n) * 1000000ULL) <= win->tmr_scroll) + win->tmr_scroll -= ((ulong)n) * 1000000ULL; + else + win->tmr_scroll = 0ULL; + win->tmr_scroll += sys.tmr_delta; + } +} diff --git a/inttypes.h b/inttypes.h new file mode 100644 index 0000000..b87382c --- /dev/null +++ b/inttypes.h @@ -0,0 +1,5 @@ + +typedef unsigned char byte; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; diff --git a/lighting.h b/lighting.h new file mode 100644 index 0000000..95919ac --- /dev/null +++ b/lighting.h @@ -0,0 +1,215 @@ + +/* +void light_set_shader(light_t *light, uint id) { + int pos; + char str[32]; + pos = sprintf(str, "lights[%d].", id); + sprintf(str+pos, "position"); + shd_vec3(str, light->pos); + sprintf(str+pos, "ambient"); + shd_vec3(str, light->ambient); + sprintf(str+pos, "diffuse"); + shd_vec3(str, light->diffuse); + sprintf(str+pos, "specular"); + shd_vec3(str, light->specular); + sprintf(str+pos, "range"); + shd_vec3(str, light->range); + sprintf(str+pos, "constant"); + shd_float(str, light->constant); + sprintf(str+pos, "linear"); + shd_float(str, light->linear); + sprintf(str+pos, "quadratic"); + shd_float(str, light->quadratic); + sprintf(str+pos, "enabled"); + shd_bool(str, 1); +} + +void light_unset_shader(uint id) { + char str[32]; + sprintf(str, "lights[%d].enabled", id); + shd_bool(str, 0); +} +*/ + +void light_calc(world_t *world) { + int x1 = ((int)world->camera.pos_x) / CHUNK_SIZE; + int y1 = ((int)world->camera.pos_y) / CHUNK_SIZE; + int z1 = ((int)world->camera.pos_z) / CHUNK_SIZE; + int x2 = x1 + gdr.light_dist_chunk; + int y2 = y1 + gdr.light_dist_chunk; + int z2 = z1 + gdr.light_dist_chunk; + ulong pos; + chunk_t *chunk; + light_t *light; + x1 -= gdr.light_dist_chunk; + y1 -= gdr.light_dist_chunk; + z1 -= gdr.light_dist_chunk; + // x1 = CHUNK_CLAMP(world, x1, x); + // y1 = CHUNK_CLAMP(world, y1, y); + // z1 = CHUNK_CLAMP(world, z1, z); + // x2 = CHUNK_CLAMP(world, x2, x); + // y2 = CHUNK_CLAMP(world, y2, y); + // z2 = CHUNK_CLAMP(world, z2, z); + // glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 18 * gdr.light_max, NULL, GL_DYNAMIC_DRAW); + // memset(world->lights, 0, sizeof(light_t) * gdr.light_max); + world->s_lights = 0; + if(!gdr.light_max) + return; + glBindBuffer(GL_UNIFORM_BUFFER, world->light_buf); + for(int cx = x1; cx <= x2; cx++) { + for(int cz = z1; cz <= z2; cz++) { + for(int cy = y1; cy <= y2; cy++) { + if(!(chunk = chunk_get(world, cx, cy, cz, 0))) + continue; + pos = 0; + while(light = tbl_iter(&chunk->light_table, &pos)) { + // if(nndist) { + // if(light_add(world, light) == 0xffffffff) + // return; + glBufferSubData(GL_UNIFORM_BUFFER, LIGHT_SSIZE * world->s_lights, LIGHT_SSIZE, light); + // memcpy(&world->lights[world->s_lights], light, sizeof(shdlight_t)); + if((world->s_lights += 1) >= gdr.light_max) { + glBindBuffer(GL_UNIFORM_BUFFER, 0); + return; + } + // } + } + } + } + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +/* +uint light_add(float x, float y, float z, uint color) { + light_t *light; + uint id; + if(!(world->lights)) + return 0xffffffff; + if((id = lst_push(&world->light_list)) == 0xffffffff) + return 0xffffffff; + light = &world->lights[id]; + light_init_def(light, x, y, z, color); + shd_sel(gdr.shd_world, 0); + light_set_shader(light, id); + world->s_lights += 1; + shd_int("n_lights", world->s_lights); + logd("TST", "l %d @ %.3f %.3f %.3f: %06x, %.1f %.1f %.1f", id, x, y, z, color, light->diffuse[0], light->diffuse[1], light->diffuse[2]); + return id; +} +*/ + +// void light_remove(world_t *world, uint id) { + // lst_pop(&world->light_list, id); + // shd_sel(gdr.shd_world, world); + // glBindBuffer(GL_UNIFORM_BUFFER, world->light_buf); + // light_unset_shader(id); + // world->s_lights -= 1; + // shd_int("n_lights", world->s_lights); +// } + +// void light_refresh() { + // if(world->world) + // light_calc(world->world); + // /* + // for(int z = 0; z < gdr.light_max; z++) { + // light_add(((float)(rand() % 20000)) / 100.0f - 100.0f, 1.0f, ((float)(rand() % 20000)) / 100.0f - 100.0f, (rand() % 0xffffff) | 0x404040); + // } + // */ +// } + +// void light_end(world_t *world) { + // if(world->lights) { + // mem_free(world->lights); + // world->lights = NULL; + // } +// } + +// void light_init(world_t *world) { + // light_end(world); + // if(gdr.light_max) + // world->lights = mem_alloc(sizeof(light_t) * gdr.light_max, MEM_LIGHTS); + // light_calc(world); +// } + +void light_setup(int max) { + window_t *win; + gdr.light_max = max; + shd_refresh(); + for(win = wcf.window_list; win; win = win->next) { + if(win->world) { + glNamedBufferData(win->world->light_buf, LIGHT_SSIZE * gdr.light_max, NULL, GL_DYNAMIC_DRAW); + light_calc(win->world); + } + } +} + +/* +void light_map_fill(const float *polys, int n_polys, uint color, float fog) { + buf_data(gdr.buf_lightmap, polys, n_polys * 3); + glBindVertexArray(gdr.vao_lightmap); + shd_sel(gdr.shd_lightmap, 1); + shd_vec2v("size", (float)(TEMP_LM_SIZE * CHUNK_SIZE), (float)(TEMP_LM_SIZE * CHUNK_SIZE)); + shd_scolor("color", color); + shd_float("fog", fog); + glBindFramebuffer(GL_FRAMEBUFFER, gdr.fb_lightmap); + glViewport(0, 0, TEMP_LM_SIZE * CHUNK_SIZE, TEMP_LM_SIZE * CHUNK_SIZE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDrawArrays(GL_TRIANGLES, 0, n_polys * 3); + glEnable(GL_BLEND); + glViewport(0, 0, wcf.current->fb_x, wcf.current->fb_y); + buf_data(gdr.buf_lightmap, NULL, 0); +} + +void light_map_rect(int x1, int z1, int x2, int z2, uint color, float fog) { + x2 -= x1; + z2 -= z1; + x1 += (CHUNK_SIZE * (TEMP_LM_SIZE / 2)); + z1 += (CHUNK_SIZE * (TEMP_LM_SIZE / 2)); + glBindFramebuffer(GL_FRAMEBUFFER, gdr.fb_lightmap); + glClearColor( + (float)((uint)((color >> 16) & 0xff)) / 255.0f, + (float)((uint)((color >> 8) & 0xff)) / 255.0f, + (float)((uint)(color & 0xff)) / 255.0f, + fog); + glViewport(0, 0, TEMP_LM_SIZE * CHUNK_SIZE, TEMP_LM_SIZE * CHUNK_SIZE); + glScissor(x1, z1, x2, z2); + // logd(LOG_SYS, "n %d %d %d %d", x1, z1, x2, z2); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + glViewport(0, 0, wcf.current->fb_x, wcf.current->fb_y); +} + +void light_map_clear() { + fb_size(gdr.fbt_lightmap, TEMP_LM_SIZE * CHUNK_SIZE, TEMP_LM_SIZE * CHUNK_SIZE); + glBindFramebuffer(GL_FRAMEBUFFER, gdr.fb_lightmap); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); +} +*/ + +void light_init_base(light_t *light, light_b *lamp) { + float r = (float)((uint)((lamp->color >> 16) & 0xff)) / 255.0f; + float g = (float)((uint)((lamp->color >> 8) & 0xff)) / 255.0f; + float b = (float)((uint)(lamp->color & 0xff)) / 255.0f; + VEC3_SET(light->ambient, r * lamp->amb_fact, g * lamp->amb_fact, b * lamp->amb_fact); + VEC3_SET(light->diffuse, r * lamp->dif_fact, g * lamp->dif_fact, b * lamp->dif_fact); + VEC3_SET(light->specular, r, g, b); + VEC3_SET(light->range, lamp->range_xz, lamp->range_y, lamp->range_xz); + light->constant = lamp->constant; + light->linear = lamp->linear; + light->quadratic = lamp->quadratic; +} + +void light_init_def(light_b *light, uint color, float brightness) { + light->color = color; + light->amb_fact = brightness; + light->dif_fact = MIN_VALUE(brightness * 16.0f, 1.0f); + light->range_xz = 1.0f; + light->range_y = 1.0f; + light->constant = 1.0f; + light->linear = 0.09f; + light->quadratic = 0.032f; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..5fbea8c --- /dev/null +++ b/list.h @@ -0,0 +1,85 @@ + +uint lst_push(list_t *list) { + list_e *elem = list->free; + if(elem == NULL) { + return 0xffffffff; + } + list->free = list->free->f_next ? list->free->f_next : list->free->f_prev; + if(elem->f_prev) { + elem->f_prev->f_next = elem->f_next; + } + if(elem->f_next) { + elem->f_next->f_prev = elem->f_prev; + } + elem->f_next = elem->f_prev = NULL; + if(list->used && list->used->u_prev) { + list->used->u_prev->u_next = elem; + } + elem->u_prev = list->used ? list->used->u_prev : NULL; // unused + elem->u_next = list->used; + if(list->used) { + list->used->u_prev = elem; + } + list->used = elem; + + list->stored += 1; + return elem->id; +} + +void lst_pop(list_t *list, uint id) { + list_e *elem = &list->elems[id]; + if(elem == list->used) { + list->used = list->used->u_next ? list->used->u_next : list->used->u_prev; + } + if(elem->u_prev) { + elem->u_prev->u_next = elem->u_next; + } + if(elem->u_next) { + elem->u_next->u_prev = elem->u_prev; + } + elem->u_next = elem->u_prev = NULL; + if(list->free && list->free->f_prev) { + list->free->f_prev->f_next = elem; + } + elem->f_prev = list->free ? list->free->f_prev : NULL; + elem->f_next = list->free; + if(list->free) { + list->free->f_prev = elem; + } + list->free = elem; + + list->stored -= 1; +} + +uint lst_iter(list_t *list, int *ptr) { + list_e *elem = ((*ptr) < 0) ? NULL : (((*ptr) == 0) ? list->used : &list->elems[(*ptr)-1]); + if(elem == NULL) { + return 0xffffffff; + } + *ptr = elem->u_next ? (elem->u_next->id + 1) : -1; + return elem->id; +} + +void lst_clear(list_t *list) { + list_e *elem; + list->stored = 0; + list->used = NULL; + list->free = list->elems; + for(int z = 0; z < list->size; z++) { + elem = &list->elems[z]; + elem->f_prev = (z == 0) ? NULL : &list->elems[z-1]; + elem->f_next = (z == (list->size - 1)) ? NULL : &list->elems[z+1]; + elem->u_prev = elem->u_next = NULL; + elem->id = z; + } +} + +void lst_init(list_t *list, uint size, byte cat) { + list->size = size; + list->elems = mem_alloc(list->size * sizeof(table_e), cat); + lst_clear(list); +} + +void lst_free(list_t *list) { + mem_free(list->elems); +} diff --git a/locale.h b/locale.h new file mode 100644 index 0000000..71bb82b --- /dev/null +++ b/locale.h @@ -0,0 +1,150 @@ + +#ifdef LOCALE_LOADER +#define STR(v, f, d) loc_set(&sys.str_table[n*LOCALE_ELEM], STRINGIZE(v), sys.str_format[n], smap_get(map, STRINGIZE(v))); n++; +#undef LOCALE_LOADER + +void loc_read(smap_t *map); + +void loc_load(const char *filename) { + char *lines; + char *line = NULL; + smap_t map; + int pos; + if(!file_sread(&lines, filename)) { + return; + } + smap_init(&map, 8, MEM_SYSTEM); + while(line = strtok(line ? NULL : lines, "\n")) { + for(pos = 0; line[pos]; pos++) { + if(line[pos] == '=') + break; + } + if(line[pos]) { + line[pos++] = 0; + smap_put(&map, line, line+pos); + } + } + loc_read(&map); + smap_clear(&map); + mem_free(lines); +} + +void loc_read(smap_t *map) { + +#endif + +#ifdef LOCALE_DEFAULTS +#define STR(v, f, d) loc_set(&sys.str_table[n*LOCALE_ELEM], STRINGIZE(v), sys.str_format[n], d); n++; +#undef LOCALE_DEFAULTS +#define LOCALE_LOADER + +void loc_def() { + +#endif + +#ifdef LOCALE_VARS +#define STR(v, f, d) STR_ ## v = &sys.str_table[n*LOCALE_ELEM]; sys.str_format[n++] = f; +#undef LOCALE_VARS +#define LOCALE_DEFAULTS + +void loc_init() { + +#endif + +#ifndef LOCALE_BASE +#define STR(v, f, d) const char * STR_ ## v; + +uint file_read_hdr(byte **data, const char *filename, const char *hdr, uint hdrsize, byte flags); + +void loc_set(char *str, const char *name, const char *fmt, const char *loc) { + byte fstr[16]; + byte pos = 0; + byte lpos = 0; + byte narg = 0; + char c; + if((!loc) || (loc[0] == 0)) { + sprintf(str, fmt[0] ? "$%s$ [%s]" : "$%s$", name, fmt); + return; + } + while(c = fmt[pos++]) { + if(c == ' ') { + fstr[narg++] = lpos; + lpos = pos; + } + } + if(fmt[lpos]) { + fstr[narg++] = lpos; + } + pos = lpos = 0; + while(c = *(loc++)) { + if(pos == LOCALE_ELEM - 1) + break; + if(lpos) { + if((c >= '1') && (c <= '9')) { + lpos = 1 + c - '1'; + } + else if((c >= 'a') && (c <= 'g')) { + lpos = 10 + c - 'a'; + } + else if((c >= 'A') && (c <= 'G')) { + lpos = 10 + c - 'A'; + } + else { + lpos = 0; + } + if(lpos && (lpos <= narg)) { + if(pos >= LOCALE_ELEM - ((lpos < 10) ? 4 : 5)) + break; + str[pos++] = '%'; + str[pos++] = (lpos < 10) ? ('0' + lpos) : '1'; + if(lpos >= 10) + str[pos++] = ('0' + (lpos - 10)); + str[pos++] = '$'; + for(lpos = fstr[lpos-1];; lpos++) { + c = fmt[lpos]; + if((c == 0) || (c == ' ')) { + break; + } + else if(pos == LOCALE_ELEM - 1) { + while(str[--pos] != '%') { + ; + } + str[pos] = 0; + return; + } + str[pos++] = c; + } + lpos = 0; + continue; + } + } + else if(c == '$') { + lpos = 1; + continue; + } + if(c == '%') { + if(pos == LOCALE_ELEM - 2) + break; + str[pos++] = '%'; + str[pos++] = '%'; + } + else { + str[pos++] = c; + } + } + str[pos] = 0; +} + +#define LOCALE_VARS +#else + int n = 0; +#endif + +#include "strings.h" + +#ifdef LOCALE_BASE +} +#else +#define LOCALE_BASE +#endif +#undef STR diff --git a/log.h b/log.h new file mode 100644 index 0000000..b8836bd --- /dev/null +++ b/log.h @@ -0,0 +1,118 @@ + +void log_sys(const char *msg, int length, int offset); + +ulong tmr_rtime(); + +void log_out(const char *str) { + char c; + int pos = 0; + int last = 0; + uint color; + // fprintf(stderr, CON_DGRAY"["CON_GREEN"%s"CON_DGRAY"]["CON_LGRAY"%s"CON_DGRAY"][%s%s"CON_DGRAY"] %s", + // tm, prefix, log_concolor[level], log_levels[level], log_concolor[level]); + while(c = str[pos]) { + if(((c >= CHR_COLORS1) && (c <= CHR_COLORE1)) || ((c >= CHR_COLORS2) && (c <= CHR_COLORE2))) { + if(pos - last) + fwrite(&str[last], 1, pos - last, stderr); + color = (c >= CHR_COLORS2) && (c <= CHR_COLORE2) ? gdr.aux_colors[c - CHR_COLORS2] : text_colors[c - CHR_COLORS1]; + fprintf(stderr, "\x1b[38;2;%d;%d;%dm", (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + last = ++pos; + continue; + } + else if(c == CHR_CRESET) { + if(pos - last) + fwrite(&str[last], 1, pos - last, stderr); + fprintf(stderr, "\x1b[0m"); + last = ++pos; + continue; + } + else if(c == CHR_UNK) { + if(pos - last) + fwrite(&str[last], 1, pos - last, stderr); + fprintf(stderr, "?"); + last = ++pos; + continue; + } + else if(c >= 0 && c < CHR_SPC && c != CHR_NLN) { + if(pos - last) + fwrite(&str[last], 1, pos - last, stderr); + last = ++pos; + continue; + } + pos++; + if(!str[pos] && (pos - last)) + fwrite(&str[last], 1, pos - last, stderr); + } + fprintf(stderr, "\x1b[0m\n"); +} + +void log_msg(const char *prefix, byte level, const char *fmt, va_list ap) { + char tm[32]; + int pos = 0; + int len; + if(level > sys.log_level) + return; + str_time(tm, tmr_rtime()); + pos = sprintf(sys.log_buf, COL_DGRAY"["COL_GREEN"%s"COL_DGRAY"]["COL_LGRAY"%s"COL_DGRAY"][%s%s"COL_DGRAY"] %s", + tm, prefix, log_colors[level], log_levels[level], log_colors[level]); + len = pos + vsnprintf((sys.log_buf)+pos, LOG_BUF-pos, fmt, ap); + log_sys(sys.log_buf, len, pos - 1); + log_out(sys.log_buf); +} + +void log_str(const char *prefix, byte level, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + log_msg(prefix, level, fmt, ap); + va_end(ap); +} + +LOG_FUNC(logu, LOG_USER) +LOG_FUNC(loge, LOG_ERROR) +LOG_FUNC(logw, LOG_WARN) +LOG_FUNC(logi, LOG_INFO) +LOG_FUNC(logp, LOG_PERF) +LOG_FUNC(logd, LOG_DEBUG) +LOG_FUNC(logt, LOG_TRACE) + +void snd_log(byte level, const char *fmt, va_list ap) { + while(sgt.log_queue == SND_LOG) { + ; + } + pthread_mutex_lock(&sgt.log_lock); + char *msg = &sgt.logs[sgt.log_push * SND_LOG_LEN]; + msg[0] = (char)level; + vsnprintf(&msg[1], SND_LOG_LEN - 1, fmt, ap); + if(++(sgt.log_push) == SND_LOG) + sgt.log_push = 0; + sgt.log_queue += 1; + pthread_mutex_unlock(&sgt.log_lock); +} + +SLOG_FUNC(snd_logu, LOG_USER) +SLOG_FUNC(snd_loge, LOG_ERROR) +SLOG_FUNC(snd_logw, LOG_WARN) +SLOG_FUNC(snd_logi, LOG_INFO) +SLOG_FUNC(snd_logp, LOG_PERF) +SLOG_FUNC(snd_logd, LOG_DEBUG) +SLOG_FUNC(snd_logt, LOG_TRACE) +SLOG_FUNC(snd_logx, SND_LOGX) + +void plr_tlog(int type, const char *text); + +void snd_log_flush() { + if(sgt.disabled) + return; + pthread_mutex_lock(&sgt.log_lock); + while(sgt.log_queue) { + char *msg = &sgt.logs[sgt.log_pop * SND_LOG_LEN]; + if(msg[0] == SND_LOGX) + plr_tlog(msg[1] - '0', &msg[2]); + else + log_str(LOG_SND, msg[0], "%s", &msg[1]); + if(++(sgt.log_pop) == SND_LOG) + sgt.log_pop = 0; + sgt.log_queue -= 1; + } + pthread_mutex_unlock(&sgt.log_lock); +} diff --git a/map.h b/map.h new file mode 100644 index 0000000..937b7a9 --- /dev/null +++ b/map.h @@ -0,0 +1,201 @@ + +#define HASHM_BUCKETS 2 + +uint map_f_inthash(void *ptr) { + uint hash = (uint)((ulong)ptr); + return ((hash << 8) & 0xf000) | ((hash >> 8) & 0x0fff); // (hash >> 24) | ((hash >> 8) & 0x0000ff00) | ((hash << 8) & 0x00ff0000) | (hash << 24); +} + +byte map_f_inteq(void *ptr1, void *ptr2) { + return ptr1 == ptr2; +} + +uint map_f_strhash(void *str) { + int pos = 0; + uint hash = 0; + uint c; + while(c = (uint)(((char*)str)[pos++])) { + hash = 31 * hash + c; + } + return hash; +} + +byte map_f_streq(void *str1, void *str2) { + return strcmp((char*)str1, (char*)str2) == 0; +} + +void map_init(map_t *map, map_hashfunc *hash, map_eqfunc *eq, uint load, byte cat) { + map->hash_func = hash; + map->eq_func = eq; + map->elems = NULL; + map->load = load; + map->cat = cat; + map->stored = 0; +} + +/* +void **map_talloc(void ***ptr, byte offset) { + void **list = *ptr; + if(list == NULL) { + *ptr = list = mem_alloc(sizeof(void **) * 256); + memset(list, 0, sizeof(void **) * 256); + } + return list[offset]; +} +*/ + +byte map_put(map_t *map, void *key, void *elem) { + uint hash = map->hash_func(key); + uint offset; + uint size; + void ***ptr = &map->elems; + void **list = *ptr; + void **elems; + uint stored; + for(int z = HASHM_BUCKETS - 1; z >= 0; z--) { + offset = (hash >> (z << 3)) & 0xff; + // list = *ptr; + if(list == NULL) { + *ptr = list = mem_alloc(sizeof(void **) * 256, map->cat); + memset(list, 0, sizeof(void **) * 256); + } + ptr = (void***)&list[offset]; + list = list[offset]; + } + if(list == NULL) { + *ptr = list = mem_alloc(sizeof(void *) * map->load * 2 + sizeof(uint) * 2, map->cat); + memset(list, 0, sizeof(void *) * map->load * 2 + sizeof(uint) * 2); + ((uint*)list)[0] = size = map->load; + stored = 0; + } + else { + size = ((uint*)list)[0]; + stored = ((uint*)list)[1]; + } + elems = (void**)(((uint*)list) + 2); + for(uint z = 0; z < size; z++) { + if((elems[z*2] != NULL) && map->eq_func(key, elems[z*2])) { + elems[z*2+0] = key; + elems[z*2+1] = elem; + return 0; + } + } + if(stored == size) { + *ptr = list = mem_realloc(list, sizeof(void *) * (size+(map->load)) * 2 + sizeof(uint) * 2, map->cat); + elems = (void**)(((uint*)list) + 2); + memset(&elems[size*2], 0, sizeof(void *) * map->load * 2); // + sizeof(uint) * 2); + ((uint*)list)[0] = size += map->load; + } + for(uint z = 0; z < size; z++) { + if(elems[z*2] == NULL) { + elems[z*2+0] = key; + elems[z*2+1] = elem; + break; + } + } + ((uint*)list)[1] = stored + 1; + map->stored += 1; + return 1; +} + +void *map_get(map_t *map, void *key) { + uint hash = map->hash_func(key); + uint offset; + uint size; + void **list = map->elems; + void **elems; + for(int z = HASHM_BUCKETS - 1; z >= 0; z--) { + if(list == NULL) { + return NULL; + } + offset = (hash >> (z << 3)) & 0xff; + list = list[offset]; + } + if(list == NULL) { + return NULL; + } + size = ((uint*)list)[0]; + elems = (void**)(((uint*)list) + 2); + for(uint z = 0; z < size; z++) { + if((elems[z*2] != NULL) && map->eq_func(key, elems[z*2+0])) { + return elems[z*2+1]; + } + } + return NULL; +} + +void *map_remove(map_t *map, void *key) { + uint hash = map->hash_func(key); + uint offset; + uint size; + uint stored; + void **lists[HASHM_BUCKETS]; + void **list = map->elems; + void **elems; + void *elem; + int s; + for(int z = HASHM_BUCKETS - 1; z >= 0; z--) { + if(list == NULL) { + return NULL; + } + offset = (hash >> (z << 3)) & 0xff; + lists[z] = list; + list = list[offset]; + } + size = ((uint*)list)[0]; + stored = ((uint*)list)[1]; + elems = (void**)(((uint*)list) + 2); + for(uint z = 0; z < size; z++) { + if((elems[z*2] != NULL) && map->eq_func(key, elems[z*2+0])) { + elem = elems[z*2+1]; + elems[z*2+0] = elems[z*2+1] = NULL; + ((uint*)list)[1] = stored -= 1; + // logd("t", "s %d", stored); + if(stored == 0) { + mem_free(list); + for(int n = 0; n < HASHM_BUCKETS; n++) { + // if(lists[n] == NULL) { + // break; + // } + lists[n][(hash >> (n << 3)) & 0xff] = NULL; + for(s = 0; s < 256; s++) { + if(lists[n][s] != NULL) { + break; + } + } + if(s == 256) { + mem_free(lists[n]); + if(n == HASHM_BUCKETS - 1) { + map->elems = NULL; + } + else { + lists[n+1][(hash >> ((n+1) << 3)) & 0xff] = NULL; + } + } + else { + break; + } + } + } + map->stored -= 1; + return elem; + } + } + return NULL; +} + +void map_rclear(void **list, int depth) { + for(int z = 0; z < 256 && depth < HASHM_BUCKETS; z++) { + if(list[z] != NULL) { + map_rclear(list[z], depth+1); + } + } + mem_free(list); +} + +void map_clear(map_t *map) { + map->stored = 0; + if(map->elems) + map_rclear(map->elems, 0); + map->elems = NULL; +} diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..32ec969 --- /dev/null +++ b/mem.h @@ -0,0 +1,94 @@ + +void *mem_alloc(ulong size, byte cat) { + byte *ptr = malloc(size + sizeof(ulong)); + if(ptr) { + sys.mem_alloc += size; + sys.mem_blocks += 1; + sys.mem_peak = sys.mem_alloc > sys.mem_peak ? sys.mem_alloc : sys.mem_peak; + sys.mem_pool_sizes[cat] += size; + sys.mem_pool_blocks[cat] += 1; + *((ulong*)ptr) = ((ulong)size) | (((ulong)cat) << 56); + ptr += sizeof(ulong); +#ifdef MEM_TRACING + logt(LOG_MEM, STR_MEM_ALLOC, size, (ulong)ptr, sys.mem_alloc, mem_types[cat]); +#endif + } + else { + sys_panic("OUT_OF_MEMORY", "Konnte nicht %lld Bytes Speicher für %s reservieren", size, mem_types[cat]); + } + return (void *)ptr; +} + +void *mem_zeros(ulong size, byte cat) { + byte *ptr = calloc(size + sizeof(ulong), 1); + if(ptr) { + sys.mem_alloc += size; + sys.mem_blocks += 1; + sys.mem_peak = sys.mem_alloc > sys.mem_peak ? sys.mem_alloc : sys.mem_peak; + sys.mem_pool_sizes[cat] += size; + sys.mem_pool_blocks[cat] += 1; + *((ulong*)ptr) = ((ulong)size) | (((ulong)cat) << 56); + ptr += sizeof(ulong); +#ifdef MEM_TRACING + logt(LOG_MEM, STR_MEM_ALLOC, size, (ulong)ptr, sys.mem_alloc, mem_types[cat]); +#endif + } + else { + sys_panic("OUT_OF_MEMORY", "Konnte nicht %lld Bytes Speicher für %s reservieren", size, mem_types[cat]); + } + return (void *)ptr; +} + +void mem_free(void *vptr) { + byte *ptr = (byte *)vptr; + ulong size; + byte cat; + if(ptr == NULL) { + return; + } + ptr -= sizeof(ulong); + size = (ulong)(((ulong)*((ulong*)ptr)) & 0x00ffffffffffffffULL); + cat = (byte)(((ulong)*((ulong*)ptr)) >> 56); + free(ptr); + sys.mem_alloc -= size; + sys.mem_blocks -= 1; + sys.mem_pool_sizes[cat] -= size; + sys.mem_pool_blocks[cat] -= 1; +#ifdef MEM_TRACING + logt(LOG_MEM, STR_MEM_FREE, size, (ulong)ptr, sys.mem_alloc, mem_types[cat]); +#endif +} + +void *mem_realloc(void *vptr, ulong size, byte cat) { + byte *ptr = (byte *)vptr; + ulong psize; + byte ocat; + byte *nptr; + if(ptr == NULL) { + return mem_alloc(size, cat); + } + else if(size == 0) { + mem_free(ptr); + return NULL; + } + ptr -= sizeof(ulong); + psize = (ulong)(((ulong)*((ulong*)ptr)) & 0x00ffffffffffffffULL); + ocat = (byte)(((ulong)*((ulong*)ptr)) >> 56); + nptr = realloc(ptr, size + sizeof(ulong)); + if(nptr) { + sys.mem_alloc -= psize; + sys.mem_alloc += size; + sys.mem_peak = sys.mem_alloc > sys.mem_peak ? sys.mem_alloc : sys.mem_peak; + sys.mem_pool_sizes[ocat] -= psize; + sys.mem_pool_sizes[cat] += size; + *((ulong*)nptr) = ((ulong)size) | (((ulong)cat) << 56); + nptr += sizeof(ulong); +#ifdef MEM_TRACING + logt(LOG_MEM, STR_MEM_REALLOC, psize, size, (ulong)nptr, sys.mem_alloc, mem_types[cat]); +#endif + } + else { + sys_panic("OUT_OF_MEMORY", "Konnte nicht %lld auf %lld Bytes Speicher bei $%llx für %s neu reservieren", psize, size, (ulong)ptr, mem_types[cat]); + } + return (void *)nptr; +} diff --git a/midi.h b/midi.h new file mode 100644 index 0000000..a783ca6 --- /dev/null +++ b/midi.h @@ -0,0 +1,518 @@ + +void snd_logd(const char *fmt, ...); +void snd_loge(const char *fmt, ...); +void snd_logx(const char *fmt, ...); + +void mid_dlog(const char *format, ...) { + if(sgt.log_debug) + snd_logd(format); +} + +void mid_settempo(mid_handle *mid, uint tempo) { + mid->uspb = tempo; + mid->ticktime = mid->uspb / mid->tpqn; +} + +uint mid_read_uint32(byte *data) { + uint value = 0; + for(int h = 0; h < 4; h++) { + value |= ((uint)data[h]); + value <<= h < 3 ? 8 : 0; + } + return value; +} + +ushort mid_read_uint16(byte *data) { + ushort value = 0; + for(int h = 0; h < 2; h++) { + value |= ((ushort)data[h]); + value <<= h < 1 ? 8 : 0; + } + return value; +} + +uint midt_read_uint32(mid_track *trk) { + uint value = 0; + for(int h = 0; h < 4; h++) { + if(trk->pos >= trk->size) { + break; + } + value |= ((uint)trk->buffer[trk->pos++]); + value <<= h < 3 ? 8 : 0; + } + return value; +} + +uint midt_read_uint24(mid_track *trk) { + uint value = 0; + for(int h = 0; h < 3; h++) { + if(trk->pos >= trk->size) { + break; + } + value |= ((uint)trk->buffer[trk->pos++]); + value <<= h < 2 ? 8 : 0; + } + return value; +} + +ushort midt_read_uint16(mid_track *trk) { + ushort value = 0; + for(int h = 0; h < 2; h++) { + if(trk->pos >= trk->size) { + break; + } + value |= ((ushort)trk->buffer[trk->pos++]); + value <<= h < 1 ? 8 : 0; + } + return value; +} + +byte midt_read_uint8(mid_track *trk) { + byte value = 0; + if(trk->pos < trk->size) { + value = trk->buffer[trk->pos++]; + } + return value; +} + +void midt_read_var(mid_track *trk, uint len, byte *chars) { + memset(chars, 0, len); + for(int h = 0; h < len; h++) { + if(trk->pos >= trk->size) { + break; + } + chars[h] = trk->buffer[trk->pos++]; + } +} + +uint mid_read_vlen(mid_track *trk) +{ + uint value; + byte bt; + + if(trk->pos >= trk->size) { + return 0; + } + if((value = trk->buffer[trk->pos++]) & 0x80) + { + value &= 0x7f; + do { + if(trk->pos >= trk->size) { + break; + } + value = (value << 7) + ((bt = trk->buffer[trk->pos++]) & 0x7f); + } + while(bt & 0x80); + } + return value; +} + +uint mid_read(mid_handle *mid, const char *filename) { + int err; + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) { + err = errno; + loge(LOG_IO, STR_MID_OPNERR, filename, strerror(err), err); + return 0; + } + if(fseek(fd, 0L, SEEK_END)) { + err = errno; + loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + long size = ftell(fd); + if(size < 0L) { + err = errno; + loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + if(fseek(fd, 0L, SEEK_SET)) { + err = errno; + loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + + if(size < 14) { + loge(LOG_IO, STR_MID_ESHORT, filename); + fclose(fd); + return 0; + } + byte header[14]; + if(fread(header, 1, 14, fd) != 14) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename); + fclose(fd); + return 0; + } + if(memcmp(header, mid_hdr, 4) != 0) { + loge(LOG_IO, STR_MID_EMFHDR, filename); + fclose(fd); + return 0; + } + if(mid_read_uint32(header+4) != 6) { + loge(LOG_IO, STR_MID_EHSIZE, filename); + fclose(fd); + return 0; + } + byte mtrack; + ushort type = mid_read_uint16(header+8); + if(type == 0) { + mtrack = 0; + } + else if((type == 1) || (type == 2)) { + mtrack = 1; + } + else { + loge(LOG_IO, STR_MID_ETKFMT, filename); + fclose(fd); + return 0; + } + mid->tracks = mid_read_uint16(header+10); + if(mid->tracks == 0) { + loge(LOG_IO, STR_MID_ENOTRK, filename); + fclose(fd); + return 0; + } + else if((mtrack ^ 1) && (mid->tracks > 1)) { + loge(LOG_IO, STR_MID_ESMULT, filename); + fclose(fd); + return 0; + } + mid->tpqn = mid_read_uint16(header+12); + if((mid->tpqn & 0x8000) > 0) { + loge(LOG_IO, STR_MID_ESMPTE, filename); + fclose(fd); + return 0; + } + if(mid->tpqn == 0) { + loge(LOG_IO, STR_MID_EDZERO, filename); + fclose(fd); + return 0; + } + uint esize = 0; + for(ushort trk = 0; trk < mid->tracks; trk++) { + if(fread(header, 1, 8, fd) != 8) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename); + fclose(fd); + return 0; + } + if(memcmp(header, mid_trk, 4) != 0) { + loge(LOG_IO, STR_MID_ETKHDR, filename); + fclose(fd); + return 0; + } + uint trks = mid_read_uint32(header+4); + if((14 + esize + 8 + trks) > size) { + loge(LOG_IO, STR_MID_EOUTOF, filename, trk+1); + fclose(fd); + return 0; + } + if(fseek(fd, trks, SEEK_CUR)) { + err = errno; + loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + esize += trks + 8; + } + if(fseek(fd, 14L, SEEK_SET)) { + err = errno; + loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err); + fclose(fd); + return 0; + } + esize -= (mid->tracks * 8); + byte *buf = mem_alloc((sizeof(mid_track) * mid->tracks) + esize, MEM_FILE); + mid->track = (mid_track*)buf; + buf += (sizeof(mid_track) * mid->tracks); + for(ushort trk = 0; trk < mid->tracks; trk++) { + memset((mid->track)+trk, 0, sizeof(mid_track)); + if(fread(header, 1, 8, fd) != 8) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename); + fclose(fd); + mem_free(mid->track); + return 0; + } + mid->track[trk].size = mid_read_uint32(header+4); + if(fread(buf, 1, mid->track[trk].size, fd) != mid->track[trk].size) { + err = feof(fd); + loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename); + fclose(fd); + mem_free(mid->track); + return 0; + } + mid->track[trk].buffer = buf; + mid->track[trk].pos = 0; + mid->track[trk].wait = 0; + mid->track[trk].trknum = trk; + buf += mid->track[trk].size; + } + fclose(fd); + mid_settempo(mid, MID_DEFTEMPO); + return (sizeof(mid_track) * mid->tracks) + esize; +} + +void mid_process_meta(mid_handle *mid, mid_track *trk) { + byte meta = midt_read_uint8(trk); + uint size = mid_read_vlen(trk); + switch(meta) { + case midmt_seqnum: + if(size == 0) { + mid_dlog(STR_MID_SEQNUMO, trk->trknum, trk->trknum); + return; + } + else if(size != 2) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 2, size); + return; + } + mid_dlog(STR_MID_SEQNUM, trk->trknum, midt_read_uint16(trk)); + return; + case midmt_text: + case midmt_copyright: + case midmt_trackname: + case midmt_instrname: + case midmt_lyric: + case midmt_marker: + case midmt_cuepoint: { + byte dt[size+1]; + dt[size] = 0; + midt_read_var(trk, size, dt); + snd_logx("%d%s", meta, dt); + return; + } + case midmt_chnprefix: + if(size != 1) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 1, size); + return; + } + mid_dlog(STR_MID_CHNPFX, trk->trknum, midt_read_uint8(trk)); + return; + case midmt_endtrack: + trk->pos += size; + trk->ending = 1; + mid_dlog(STR_MID_END, trk->trknum); + return; + case midmt_tempo: + if(size != 3) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 3, size); + return; + } + mid_settempo(mid, midt_read_uint24(trk)); + mid_dlog(STR_MID_TEMPO, 60000000 / mid->uspb); + return; + case midmt_smpte: + if(size != 5) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 5, size); + return; + } + trk->pos += 5; + mid_dlog(STR_MID_SMPTE); + return; + case midmt_timesig: { + if(size != 4) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 4, size); + return; + } + byte n = midt_read_uint8(trk); + uint d = 1 << ((uint)midt_read_uint8(trk)); + byte c = midt_read_uint8(trk); + byte b = midt_read_uint8(trk); + mid_dlog(STR_MID_TIMESIG, n, d, c, b); + return; + } + case midmt_keysig: { + if(size != 2) { + trk->pos += size; + snd_loge(STR_MID_METALEN, trk->trknum, meta, 2, size); + return; + } + int8_t s = midt_read_uint8(trk); + byte m = midt_read_uint8(trk); + mid_dlog(STR_MID_KEYSIG, s, (m == 0) ? "MAJOR" : ((m == 1) ? "MINOR" : "- ? -")); + return; + } + case midmt_seqspec: { + trk->pos += size; + mid_dlog(STR_MID_SEQDATA, trk->trknum, size); + return; + } + default: + trk->pos += size; + mid_dlog(STR_MID_UNKMETA, trk->trknum, meta, size); + return; + } +} + +#ifndef MID_NOEVT +void mid_process(mid_handle *mid, mid_track *trk, bank_handle *bank, opl3_chip *chip) { +#else +void mid_process(mid_handle *mid, mid_track *trk) { +#endif + if(trk->pos >= trk->size) { + return; + } + byte status = trk->buffer[trk->pos++]; + if((status & 0x80) == 0) { + status = trk->status; + trk->pos -= 1; + } + else { + trk->status = status; + } + if((status & 0xf0) != 0xf0) { + byte channel = status & 0x0f; + status &= 0xf0; + switch(status) { + case midev_noteoff: { + byte o_key = midt_read_uint8(trk); + byte o_velocity = midt_read_uint8(trk); +#ifndef MID_NOEVT + bank_noteoff(bank, chip, channel, o_key, o_velocity); +#endif + mid_dlog(STR_MID_NOTEOFF, channel+1, o_key, o_velocity); + break; + } + case midev_noteon: { + byte key = midt_read_uint8(trk); + byte velocity = midt_read_uint8(trk); +#ifndef MID_NOEVT + if(velocity == 0) { + bank_noteoff(bank, chip, channel, key, velocity); + } + else { + bank_noteon(bank, chip, channel, key, velocity); + } +#endif + mid_dlog(STR_MID_NOTEON, channel+1, key, velocity); + break; + } + case midev_aftertouch: { + byte pressure = midt_read_uint8(trk); + mid_dlog(STR_MID_PRESS, channel+1, pressure); + break; + } + case midev_control: { + byte control = midt_read_uint8(trk); + byte value = midt_read_uint8(trk); +#ifndef MID_NOEVT + bank_control(bank, chip, channel, control, value); +#endif + mid_dlog(STR_MID_CC, channel+1, control, value); + break; + } + case midev_progchg: { + byte program = midt_read_uint8(trk); +#ifndef MID_NOEVT + bank_progchange(bank, chip, channel, program); +#endif + mid_dlog(STR_MID_PROG, channel+1, program); + break; + } + case midev_chnpressure: { + byte cpressure = midt_read_uint8(trk); + mid_dlog(STR_MID_CPRESS, channel+1, cpressure); + break; + } + case midev_pitchbend: { + ushort pb = ((ushort)midt_read_uint8(trk)) | (((ushort)midt_read_uint8(trk)) << 7); + short pitch = ((short)pb) - 0x2000; +#ifndef MID_NOEVT + bank_pitchbend(bank, chip, channel, pitch); +#endif + mid_dlog(STR_MID_PITCH, channel+1, pitch); + break; + } + } + } + else { + switch(status) { + case midev_sysex: { + uint slen = mid_read_vlen(trk); + trk->pos += slen; + mid_dlog(STR_MID_SYSEX, slen); + break; + } + case midev_songpos: + mid_dlog(STR_MID_SONGPOS, ((ushort)midt_read_uint8(trk)) | (((ushort)midt_read_uint8(trk)) << 7)); + break; + case midev_songsel: + mid_dlog(STR_MID_SONGSEL, midt_read_uint8(trk)); + break; + case midev_tunereq: + mid_dlog(STR_MID_TUNE); + break; + case midev_endsysex: { + uint elen = mid_read_vlen(trk); + trk->pos += elen; + mid_dlog(STR_MID_ESYSEX, elen); + break; + } + case midev_clock: + case midev_start: + case midev_continue: + case midev_stop: + case midev_actsense: + mid_dlog(STR_MID_GENSTAT, status); + break; + case midev_meta: + mid_process_meta(mid, trk); + break; + default: + snd_loge(STR_MID_UNKSTAT, status); + break; + } + } +} + +#ifndef MID_NOEVT +byte mid_tick(mid_handle *mid, bank_handle *bank, opl3_chip *chip) { +#else +byte mid_tick(mid_handle *mid) { +#endif + byte end = 1; + for(int trk = 0; trk < mid->tracks; trk++) { + mid_track *track = (mid->track)+trk; + if(track->ending) { + continue; + } + if(track->wait > 0) { + track->wait -= 1; + if(track->wait > 0) { + end = 0; + continue; + } + } + while(1) { + if(track->pos > 0) { +#ifndef MID_NOEVT + mid_process(mid, track, bank, chip); +#else + mid_process(mid, track); +#endif + if((track->ending ^ 1) && (track->pos >= track->size)) { + snd_loge(STR_MID_EEND, track->trknum); + track->ending = 1; + } + if(track->ending) { + break; + } + } + track->wait = mid_read_vlen(track); + if(track->wait > 0) { + break; + } + } + end &= track->ending; + } + return end ^ 1; +} diff --git a/noise.h b/noise.h new file mode 100644 index 0000000..83d39de --- /dev/null +++ b/noise.h @@ -0,0 +1,225 @@ + +static const double noise_d3x[] = { 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D, -1.0D, 0.0D }; +static const double noise_d3y[] = { 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D }; +static const double noise_d3z[] = { 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D, -1.0D }; +static const double noise_d2x[] = { 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D, -1.0D, 0.0D }; +static const double noise_d2z[] = { 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D, -1.0D }; + +typedef struct { + int permutations[512]; + double xcoord; + double ycoord; + double zcoord; +} octave_t; + +typedef struct { + octave_t *octaves; + uint n_octaves; +} noisegen_t; + +void noise_oct_init(octave_t *oct, ulong *rand) { + memset(oct->permutations, 0, 512 * sizeof(int)); + oct->xcoord = rng_double(rand) * 256.0D; + oct->ycoord = rng_double(rand) * 256.0D; + oct->zcoord = rng_double(rand) * 256.0D; + + for(int n = 0; n < 256; oct->permutations[n] = n++) { + ; + } + + for(int n = 0; n < 256; ++n) { + int pos = rng_zrange(rand, 256 - n) + n; + int value = oct->permutations[n]; + oct->permutations[n] = oct->permutations[pos]; + oct->permutations[pos] = value; + oct->permutations[n + 256] = oct->permutations[n]; + } +} + +void noise_gen_init(noisegen_t *gen, ulong *rand, int octs) { + gen->n_octaves = octs; + gen->octaves = mem_alloc(octs * sizeof(octave_t), MEM_MISC); + + for(int n = 0; n < octs; ++n) { + noise_oct_init(&gen->octaves[n], rand); + } +} + +void noise_gen_free(noisegen_t *gen) { + if(gen->octaves) + mem_free(gen->octaves); + gen->octaves = NULL; + gen->n_octaves = 0; +} + +double noise_lerp(double d, double n, double m) { + return n + d * (m - n); +} + +double noise_grad2d(int n, double xm, double zm) { + int i = n & 15; + return noise_d2x[i] * xm + noise_d2z[i] * zm; +} + +double noise_grad(int n, double xm, double ym, double zm) { + int i = n & 15; + return noise_d3x[i] * xm + noise_d3y[i] * ym + noise_d3z[i] * zm; +} + +long noise_floor_double(double value) { + long i = (long)value; + return value < (double)i ? i - 1L : i; +} + +void noise_oct_gen(octave_t *oct, double *noise, double xoff, double yoff, double zoff, int xsize, int ysize, int zsize, + double xscale, double yscale, double zscale, double scale) { + int *permutations = oct->permutations; + double xcoord = oct->xcoord; + double ycoord = oct->ycoord; + double zcoord = oct->zcoord; + if(ysize == 1) { + int i5 = 0; + int j5 = 0; + int j = 0; + int k5 = 0; + double d14 = 0.0D; + double d15 = 0.0D; + int l5 = 0; + double d16 = 1.0D / scale; + + for(int j2 = 0; j2 < xsize; ++j2) { + double d17 = xoff + (double)j2 * xscale + xcoord; + int i6 = (int)d17; + + if(d17 < (double)i6) { + --i6; + } + + int k2 = i6 & 255; + d17 = d17 - (double)i6; + double d18 = d17 * d17 * d17 * (d17 * (d17 * 6.0D - 15.0D) + 10.0D); + + for(int j6 = 0; j6 < zsize; ++j6) { + double d19 = zoff + (double)j6 * zscale + zcoord; + int k6 = (int)d19; + + if(d19 < (double)k6) { + --k6; + } + + int l6 = k6 & 255; + d19 = d19 - (double)k6; + double d20 = d19 * d19 * d19 * (d19 * (d19 * 6.0D - 15.0D) + 10.0D); + i5 = permutations[k2] + 0; + j5 = permutations[i5] + l6; + j = permutations[k2 + 1] + 0; + k5 = permutations[j] + l6; + d14 = noise_lerp(d18, noise_grad2d(permutations[j5], d17, d19), noise_grad(permutations[k5], d17 - 1.0D, 0.0D, d19)); + d15 = noise_lerp(d18, noise_grad(permutations[j5 + 1], d17, 0.0D, d19 - 1.0D), + noise_grad(permutations[k5 + 1], d17 - 1.0D, 0.0D, d19 - 1.0D)); + double d21 = noise_lerp(d20, d14, d15); + int i7 = l5++; + noise[i7] += d21 * d16; + } + } + } + else { + int i = 0; + double d0 = 1.0D / scale; + int k = -1; + int l = 0; + int i1 = 0; + int j1 = 0; + int k1 = 0; + int l1 = 0; + int i2 = 0; + double d1 = 0.0D; + double d2 = 0.0D; + double d3 = 0.0D; + double d4 = 0.0D; + + for(int l2 = 0; l2 < xsize; ++l2) { + double d5 = xoff + (double)l2 * xscale + xcoord; + int i3 = (int)d5; + + if(d5 < (double)i3) { + --i3; + } + + int j3 = i3 & 255; + d5 = d5 - (double)i3; + double d6 = d5 * d5 * d5 * (d5 * (d5 * 6.0D - 15.0D) + 10.0D); + + for(int k3 = 0; k3 < zsize; ++k3) { + double d7 = zoff + (double)k3 * zscale + zcoord; + int l3 = (int)d7; + + if(d7 < (double)l3) { + --l3; + } + + int i4 = l3 & 255; + d7 = d7 - (double)l3; + double d8 = d7 * d7 * d7 * (d7 * (d7 * 6.0D - 15.0D) + 10.0D); + + for(int j4 = 0; j4 < ysize; ++j4) { + double d9 = yoff + (double)j4 * yscale + ycoord; + int k4 = (int)d9; + + if(d9 < (double)k4) { + --k4; + } + + int l4 = k4 & 255; + d9 = d9 - (double)k4; + double d10 = d9 * d9 * d9 * (d9 * (d9 * 6.0D - 15.0D) + 10.0D); + + if(j4 == 0 || l4 != k) { + k = l4; + l = permutations[j3] + l4; + i1 = permutations[l] + i4; + j1 = permutations[l + 1] + i4; + k1 = permutations[j3 + 1] + l4; + l1 = permutations[k1] + i4; + i2 = permutations[k1 + 1] + i4; + d1 = noise_lerp(d6, noise_grad(permutations[i1], d5, d9, d7), noise_grad(permutations[l1], d5 - 1.0D, d9, d7)); + d2 = noise_lerp(d6, noise_grad(permutations[j1], d5, d9 - 1.0D, d7), + noise_grad(permutations[i2], d5 - 1.0D, d9 - 1.0D, d7)); + d3 = noise_lerp(d6, noise_grad(permutations[i1 + 1], d5, d9, d7 - 1.0D), + noise_grad(permutations[l1 + 1], d5 - 1.0D, d9, d7 - 1.0D)); + d4 = noise_lerp(d6, noise_grad(permutations[j1 + 1], d5, d9 - 1.0D, d7 - 1.0D), + noise_grad(permutations[i2 + 1], d5 - 1.0D, d9 - 1.0D, d7 - 1.0D)); + } + + double d11 = noise_lerp(d10, d1, d2); + double d12 = noise_lerp(d10, d3, d4); + double d13 = noise_lerp(d8, d11, d12); + int j7 = i++; + noise[j7] += d13 * d0; + } + } + } + } +} + +void noise_gen(noisegen_t *gen, double *noise, double xoff, double yoff, double zoff, int xsize, int ysize, int zsize, + double xscale, double yscale, double zscale) { + memset(noise, 0, sizeof(double) * xsize * ysize * zsize); + uint octaves = gen->n_octaves; + double factor = 1.0D; + for(int n = 0; n < octaves; ++n) { + double xo = xoff * factor * xscale; + double yo = yoff * factor * yscale; + double zo = zoff * factor * zscale; + long xn = noise_floor_double(xo); + long zn = noise_floor_double(zo); + xo = xo - (double)xn; + zo = zo - (double)zn; + xn = xn % 16777216L; + zn = zn % 16777216L; + xo = xo + (double)xn; + zo = zo + (double)zn; + noise_oct_gen(&gen->octaves[n], noise, xo, yo, zo, xsize, ysize, zsize, xscale * factor, yscale * factor, zscale * factor, factor); + factor /= 2.0D; + } +} diff --git a/opl3.h b/opl3.h new file mode 100644 index 0000000..b11b2c8 --- /dev/null +++ b/opl3.h @@ -0,0 +1,932 @@ + +// Envelope generator + +short OPL3_EnvelopeCalcExp(uint level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +short OPL3_EnvelopeCalcSin0(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +short OPL3_EnvelopeCalcSin1(ushort phase, ushort envelope) +{ + ushort out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +short OPL3_EnvelopeCalcSin2(ushort phase, ushort envelope) +{ + ushort out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +short OPL3_EnvelopeCalcSin3(ushort phase, ushort envelope) +{ + ushort out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +short OPL3_EnvelopeCalcSin4(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +short OPL3_EnvelopeCalcSin5(ushort phase, ushort envelope) +{ + ushort out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +short OPL3_EnvelopeCalcSin6(ushort phase, ushort envelope) +{ + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +short OPL3_EnvelopeCalcSin7(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +short OPL3_EnvelopeCalcTri(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = (phase & 0xff) << 3; + } + else + { + out = ~(phase & 0xff) << 3; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +short OPL3_EnvelopeCalcSqr25(ushort phase, ushort envelope) +{ + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x300) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +short OPL3_EnvelopeCalcSqr12(ushort phase, ushort envelope) +{ + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x380) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +short OPL3_EnvelopeCalcSaw(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + out = ~(phase & 0x1ff) << 2; + } + else + { + out = (phase & 0x1ff) << 2; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +short OPL3_EnvelopeCalcNoise(ushort phase, ushort envelope) +{ + ushort out = 0; + ushort neg = 0; + phase &= 0x3ff; + if ((phase ^ rng_table[~phase & 0xff]) & 1) + { + neg = 0xffff; + } + out = rng_table[phase & 0xff] << 2; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[16] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7, + OPL3_EnvelopeCalcTri, + OPL3_EnvelopeCalcSqr25, + OPL3_EnvelopeCalcSqr12, + OPL3_EnvelopeCalcSaw, + OPL3_EnvelopeCalcNoise, + OPL3_EnvelopeCalcNoise, + OPL3_EnvelopeCalcNoise, + OPL3_EnvelopeCalcNoise, +}; + +void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + byte nonzero; + byte rate; + byte rate_hi; + byte rate_lo; + byte reg_rate = 0; + byte ks; + byte eg_shift, shift; + ushort eg_rout; + short eg_inc; + byte eg_off; + byte reset = 0; + if(slot->retrigger) { + slot->eg_rout = 0x1ff; + } + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!(slot->eg_rout)) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->eg_rout >> (4 - shift); + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!(slot->key)) + { + slot->eg_gen = envelope_gen_num_release; + } + slot->detrigger = 0; + slot->retrigger = 0; +} + +void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + short ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (byte)ksl; +} + +void OPL3_EnvelopeKeyOn(opl3_slot *slot) +{ + slot->key = 0x01; + if(slot->detrigger) { + slot->eg_gen = envelope_gen_num_release; + // slot->eg_rout = 0x1ff; + // slot->eg_out = slot->eg_rout = 0x1ff; + slot->detrigger = 0; + } + slot->retrigger = 1; +} + +void OPL3_EnvelopeKeyOff(opl3_slot *slot) +{ + slot->key = 0x00; + slot->detrigger = 1; + slot->retrigger = 0; +} + +void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + ushort f_num; + uint basefreq; + ushort phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + char range; + byte vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (ushort)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * op_mt[slot->reg_mult]) >> 1; + slot->pg_phase_out = phase; +} + +void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +void OPL3_ProcessSlot(opl3_slot *slot) +{ + OPL3_SlotCalcFB(slot); + OPL3_EnvelopeCalc(slot); + OPL3_PhaseGenerate(slot); + OPL3_SlotGenerate(slot); +} + +short OPL3_ClipSampleOld(int sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (short)sample; +} + +short OPL3_ClipSample(int sample) +{ + int sign = (sample < 0) ? -1 : 1; + sample = (sample < 0) ? -sample : sample; + sample *= 5; + sample /= 8; + sample = sample > 32767 ? 32767 : sample; + sample *= sign; + return (short)sample; +} + +void OPL3_Generate(opl3_chip *chip, short *buf) +{ + opl3_channel *channel; + short **out; + int mix[2]; + byte ii; + short accm; + byte shift = 0; + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < (chip->n_voices * 2); ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } + + mix[0] = mix[1] = 0; + for (ii = 0; ii < chip->n_voices; ii++) + { + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; + mix[0] += (short)((accm * channel->level[0]) >> 16); + mix[1] += (short)((accm * channel->level[1]) >> 16); + } + chip->mixbuff[0] = mix[0]; + chip->mixbuff[1] = mix[1]; + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == 0xfffffffff) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; +} + +void OPL3_GenerateResampled(opl3_chip *chip, short *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (short)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (short)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +// Operator + +void OPL3_SlotFlags(opl3_slot *slot, byte tremolo, byte vibrato, byte sustaining, byte ksr) { + if (tremolo) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (byte*)&slot->chip->zeromod; + } + slot->reg_vib = vibrato & 0x01; + slot->reg_type = sustaining & 0x01; + slot->reg_ksr = ksr & 0x01; +} + +void OPL3_SlotMult(opl3_slot *slot, byte mult) { + slot->reg_mult = mult & 0x0f; +} + +void OPL3_SlotKSL(opl3_slot *slot, byte ksl) { + slot->reg_ksl = ksl & 0x03; + OPL3_EnvelopeUpdateKSL(slot); +} + +void OPL3_SlotLevel(opl3_slot *slot, byte level) { + slot->reg_tl = level & 0x3f; +} + +void OPL3_SlotADSR(opl3_slot *slot, byte attack, byte decay, byte sustain, byte release) { + slot->reg_ar = attack & 0x0f; + slot->reg_dr = decay & 0x0f; + slot->reg_sl = sustain & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = release & 0x0f; + if (slot->reg_rr == 0x00) + { + slot->reg_rr = 0x01; + } +} + +void OPL3_SlotWaveform(opl3_slot *slot, byte waveform) { + slot->reg_wf = waveform & 0x07; +} + +void OPL3_SlotWaveformNew(opl3_slot *slot, byte waveform) { + slot->reg_wf = waveform & 0x0f; +} + +// Channel + +void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +void OPL3_ChannelUpdateAlg(opl3_channel *channel) +{ + channel->alg = channel->con; + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } +} + +void OPL3_ChannelFreq(opl3_channel *channel, byte block, ushort f_num) { + if (channel->chtype == ch_4op2) + { + return; + } + channel->f_num = f_num & 0x3ff; + channel->block = block & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +void OPL3_ChannelOutput(opl3_channel *channel, byte output, uint level) { + channel->level[output & 1] = level; +} + +void OPL3_Channel4Op(opl3_channel *channel, byte op4) { + if (op4) + { + channel->chtype = ch_4op; + channel->pair->chtype = ch_4op2; + OPL3_ChannelUpdateAlg(channel); + } + else + { + channel->chtype = ch_2op; + channel->pair->chtype = ch_2op; + OPL3_ChannelUpdateAlg(channel); + OPL3_ChannelUpdateAlg(channel->pair); + } +} + +void OPL3_ChannelFeedback(opl3_channel *channel, byte feedback) { + channel->fb = feedback & 0x07; +} + +void OPL3_ChannelAM(opl3_channel *channel, byte am) { + channel->con = am & 0x01; + OPL3_ChannelUpdateAlg(channel); +} + +void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slots[0]); + OPL3_EnvelopeKeyOn(channel->slots[1]); + OPL3_EnvelopeKeyOn(channel->pair->slots[0]); + OPL3_EnvelopeKeyOn(channel->pair->slots[1]); + } + else if (channel->chtype == ch_2op) + { + OPL3_EnvelopeKeyOn(channel->slots[0]); + OPL3_EnvelopeKeyOn(channel->slots[1]); + } +} + +void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slots[0]); + OPL3_EnvelopeKeyOff(channel->slots[1]); + OPL3_EnvelopeKeyOff(channel->pair->slots[0]); + OPL3_EnvelopeKeyOff(channel->pair->slots[1]); + } + else if (channel->chtype == ch_2op) + { + OPL3_EnvelopeKeyOff(channel->slots[0]); + OPL3_EnvelopeKeyOff(channel->slots[1]); + } +} + +void OPL3_Reset(opl3_chip *chip) +{ + byte slotnum; + byte channum; + + int rateratio = chip->rateratio; + byte n_voices = chip->n_voices; + opl3_slot *slot = chip->slot; + opl3_channel *channel = chip->channel; + + memset(chip, 0, sizeof(opl3_chip) + (n_voices * (sizeof(opl3_slot) * 2 + sizeof(opl3_channel)))); + + chip->rateratio = rateratio; + chip->n_voices = n_voices; + chip->slot = slot; + chip->channel = channel; + + for (slotnum = 0; slotnum < (chip->n_voices * 2); slotnum++) + { + slot = &chip->slot[slotnum]; + slot->chip = chip; + slot->mod = &chip->zeromod; + slot->eg_rout = 0x1ff; + slot->eg_out = 0x1ff; + slot->eg_gen = envelope_gen_num_release; + slot->trem = (byte*)&chip->zeromod; + slot->slot_num = slotnum; + } + for (channum = 0; channum < chip->n_voices; channum++) + { + channel = &chip->channel[channum]; + channel->slots[0] = &chip->slot[channum * 2]; + channel->slots[1] = &chip->slot[channum * 2 + 1]; + chip->slot[channum * 2].channel = channel; + chip->slot[channum * 2 + 1].channel = channel; + channel->pair = &chip->channel[(channum & 1) ? (channum - 1) : (channum + 1)]; + channel->chip = chip; + channel->out[0] = &chip->zeromod; + channel->out[1] = &chip->zeromod; + channel->out[2] = &chip->zeromod; + channel->out[3] = &chip->zeromod; + channel->chtype = ch_2op; + channel->level[0] = 0x10000; + channel->level[1] = 0x10000; + channel->ch_num = channum; + OPL3_ChannelSetupAlg(channel); + } + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +void OPL3_Rate(opl3_chip *chip, uint samplerate) { + chip->rateratio = (samplerate << RSM_FRAC) / 49716; +} + +void OPL3_ChipFlags(opl3_chip *chip, byte nts, byte vibrato, byte deeptremolo, byte deepvibrato) { + chip->nts = nts; + chip->tremoloshift = ((deeptremolo ^ 1) << 1) + 2; + chip->vibshift = deepvibrato ^ 1; +} + +opl3_chip *OPL3_Alloc(uint samplerate, byte voices) { + opl3_chip *chip = malloc(sizeof(opl3_chip) + (voices * (sizeof(opl3_slot) * 2 + sizeof(opl3_channel)))); + chip->channel = (opl3_channel*)(((byte*)chip) + sizeof(opl3_chip)); + chip->slot = (opl3_slot*)(((byte*)chip->channel) + (voices * sizeof(opl3_channel))); + chip->n_voices = voices; + OPL3_Rate(chip, samplerate); + OPL3_Reset(chip); + return chip; +} + +byte OPL3_Playing(opl3_chip *chip) { + opl3_slot *slot; + for(int z = 0; z < (chip->n_voices * 2); z++) { + slot = &chip->slot[z]; + if(slot->eg_out <= 0x100) { + return 1; + } + } + return 0; +} diff --git a/player.h b/player.h new file mode 100644 index 0000000..1700c7c --- /dev/null +++ b/player.h @@ -0,0 +1,343 @@ + +static const char **plr_descs[] = { &STR_IMID_TITLE, &STR_IMID_INFO, &STR_IMID_COPY, &STR_IMID_WARN, &STR_IMID_LANG, &STR_IMID_VER }; + +byte plr_play_file(const char *midname, byte nowait) { + mid_handle mid; + uint size; + if(!(size = mid_read(&mid, midname))) + return 0; + logi(LOG_CON, STR_MID_PLAY, midname); + snd_instruct(mid.track, size, SND_CMD_LOAD, 0, (uint)mid.tracks | ((uint)mid.tpqn << 16) | ((uint)nowait << 31)); + mem_free(mid.track); + return 1; +} + +void plr_cmlog(byte empty); + +byte plr_play(int pos, byte nowait) { + plr_cmlog(0); + if(pos != (snd.mid_queued - 1)) + snd.mid_queue[pos+1][-1] = 0; + byte status = plr_play_file(&(snd.mid_queue[pos])[6], nowait); + if(pos != (snd.mid_queued - 1)) + snd.mid_queue[pos+1][-1] = '\n'; + snd.mid_queue[pos][0] = COL_GREEN[0]; + if(snd.mid_prevpos >= 0) + snd.mid_queue[snd.mid_prevpos][0] = COL_NEON[0]; + snd.mid_prevpos = pos; + return status; +} + +byte plr_stopped() { + return snd_query(SND_STAT_STOPPED, 0); +} + +// voices -- 2 ~ 192 +byte plr_setup(byte voices, const char *dmxname, const char *drumname, byte keep, byte useunkn, char velofunc) { + // if(dmxname) { + bank_instr *instr = bnk_read_banks(dmxname, drumname, 0); + if(!instr) + return 0; + snd_instruct(instr, sizeof(bank_instr) * 256, SND_CMD_VOICES, 0, 0); + mem_free(instr); + // } + voices = (voices & 1) ? (voices + 1) : voices; + // snd_instruct(NULL, 0, SND_CMD_BANK, dmxname ? 31 : bank, 0); + snd_instruct(NULL, 0, SND_CMD_MKOPL, 0, (uint)voices | ((uint)((byte)(velofunc & 0xff)) << 8) | ((uint)keep << 16) | ((uint)useunkn << 17)); + return 1; +} + +void plr_debug(byte debug) { + snd_instruct(NULL, 0, SND_CMD_DEBUG, 0, debug); + snd.mid_debug = debug; +} + +void plr_volume(byte channel, ushort volume) { + snd_instruct(NULL, 0, SND_CMD_VOLUME, channel, volume); +} + +enum repeat_type { + REPEAT_OFF, + REPEAT_SINGLE, + REPEAT_ALL, + REPEAT_SHUFFLE +}; + +void plr_end() { + if(snd.mid_queue) { + mem_free(snd.mid_queue); + mem_free(snd.mid_list); + } + snd.mid_queue = NULL; + snd.mid_list = NULL; + snd.mid_queued = 0; + snd_instruct(NULL, 0, SND_CMD_LOAD, 0, 0); + snd_instruct(NULL, 0, SND_CMD_MKOPL, 0, 0); +} + +byte plr_queue(const char *dmxname, const char *drumname, const char **files, int count) { + plr_end(); + if(!plr_setup(snd.mid_voices, dmxname, drumname, snd.mid_keep, snd.mid_unknown, (char)snd.mid_velo)) + return 0; + snd.mid_queue = mem_alloc(sizeof(char *) * count, MEM_FILE); + int size = 0; + for(int z = 0; z < count; z++) { + size += strlen(files[z]) + 7; + } + char *list = snd.mid_list = mem_alloc(size, MEM_FILE); + for(int z = 0; z < count; z++) { + snd.mid_queue[z] = list; + list += snprintf(list, 1024, (z == (count - 1)) ? COL_NEON "#%03d %s" : COL_NEON "#%03d %s\n", z + 1, files[z]); + } + snd.mid_queued = count; + snd.mid_playpos = -1; + snd.mid_prevpos = -1; +} + +void plr_mode(byte repeat) { + snd.mid_repeat = repeat; +} + +void plr_pause() { + if(!snd.mid_queue || (snd.mid_playpos == INT_MIN)) + return; + if(snd.mid_playpos >= -1) + snd.mid_playpos = -(snd.mid_playpos + 3); + else + snd.mid_playpos = -(snd.mid_playpos) - 3; + if(snd.mid_playpos >= 0 || snd.mid_playpos < -2) + snd.mid_queue[(snd.mid_playpos < -1) ? (-(snd.mid_playpos) - 3) : snd.mid_playpos][0] = snd.mid_playpos < -1 ? COL_YELLOW[0] : COL_GREEN[0]; + snd_instruct(NULL, 0, SND_CMD_PAUSE, 0, snd.mid_playpos < -1); +} + +void plr_stop() { + if(!snd.mid_queue) + return; + plr_cmlog(1); + snd.mid_playpos = INT_MIN; + if(snd.mid_prevpos >= 0) + snd.mid_queue[snd.mid_prevpos][0] = COL_NEON[0]; + snd.mid_prevpos = -1; + snd_instruct(NULL, 0, SND_CMD_LOAD, 0, 0); +} + +void plr_resume() { + if(!snd.mid_queue) + return; + if(snd.mid_playpos == INT_MIN) + plr_play(snd.mid_playpos = 0, snd.mid_nowait); + else if(snd.mid_playpos < -1) + plr_pause(); +} + +void plr_update() { + if(snd.mid_queue && (sys.tmr_current - snd.mid_poll) >= 250000ULL) { + snd.mid_poll = sys.tmr_current; + if((snd.mid_playpos >= -1) && plr_stopped()) { + if(snd.mid_playpos >= 0) { + switch(snd.mid_repeat) { + case REPEAT_OFF: + snd.mid_playpos += 1; + break; + case REPEAT_SINGLE: + break; + case REPEAT_ALL: + if((snd.mid_playpos += 1) >= snd.mid_queued) + snd.mid_playpos = 0; + break; + case REPEAT_SHUFFLE: + snd.mid_playpos = rand() % snd.mid_queued; + break; + } + } + else { + snd.mid_playpos = 0; + } + if(snd.mid_playpos >= snd.mid_queued) + plr_stop(); + else + plr_play(snd.mid_playpos, snd.mid_nowait); + if(snd.player && snd.player->open) { + gui_update_text(gui_get(snd.player, 1)); + gui_plr_update(snd.player); + } + } + } +} + +void plr_jump(int pos) { + if(!snd.mid_queue) + return; + plr_resume(); + pos = CLAMP_VALUE(pos, 0, snd.mid_queued - 1); + plr_play(snd.mid_playpos = pos, snd.mid_nowait); +} + +void plr_next() { + if(!snd.mid_queue) + return; + plr_resume(); + if((snd.mid_playpos += 1) >= snd.mid_queued) + snd.mid_playpos = 0; + plr_play(snd.mid_playpos, snd.mid_nowait); +} + +void plr_prev() { + if(!snd.mid_queue) + return; + plr_resume(); + if((snd.mid_playpos -= 1) < 0) + snd.mid_playpos = snd.mid_queued - 1; + plr_play(snd.mid_playpos, snd.mid_nowait); +} + +void plr_rand() { + if(!snd.mid_queue) + return; + plr_resume(); + plr_play((snd.mid_playpos = rand() % snd.mid_queued), snd.mid_nowait); +} + +void plr_capture(const char *wavname) { + snd_instruct(wavname, wavname ? (strlen(wavname) + 1) : 0, SND_CMD_WAVECAP, 0, 0); +} + +void plr_clog() { + snd.mid_klog_pos = 0; + snd.mid_klog_offs = 0; + for(int z = 0; z < SND_KLOG; z++) { + memset(&snd.mid_klogger[z * SND_KAR], ' ', SND_KAR - 1); + snd.mid_klogger[z * SND_KAR + (SND_KAR - 1)] = (z == (SND_KLOG - 1)) ? 0 : '\n'; + } + if(snd.player && snd.player->open) + gui_update_text(gui_get(snd.player, 16)); +} + +void plr_cmlog(byte empty) { + snd.mid_karaoke = 0; + for(int z = 0; z < 6; z++) { + snd.mid_info_offs[z] = empty ? 0 : sprintf(&snd.mid_info[z * SND_INFO], "%-12s: ", *(plr_descs[z])); + if(!empty) { + memset(&snd.mid_info[z * SND_INFO + snd.mid_info_offs[z]], ' ', SND_INFO - 1); + snd.mid_info[z * SND_INFO + (SND_INFO - 1)] = (z == 5) ? 0 : '\n'; + } + } + if(empty) + snd.mid_info[0] = 0; + plr_clog(); + if(snd.player && snd.player->open) + gui_update_text(gui_get(snd.player, 15)); +} + +void plr_kar() { + if(snd.mid_karaoke ^ 1) { + snd.mid_karaoke = 1; + plr_clog(); + } +} + +void plr_info(int type, const char *text) { + char *info = &snd.mid_info[type * SND_INFO]; + int offs = snd.mid_info_offs[type]; + int len = strlen(text); + int max = (SND_INFO - 1) - 14; + max -= (offs > 14) ? 2 : 0; + len = (len > (max-offs)) ? (max-offs) : len; + if(len <= 0) { + return; + } + if(offs > 14) { + info[offs++] = ';'; + info[offs++] = ' '; + } + // else { + // max = sprintf(info, "%-12s: ", *(plr_descs[type])); + // memset(info+(max + len), ' ', SND_INFO - (1 + max + len)); + // offs += max; + // } + memcpy(info+offs, text, len); + offs += len; + // info[SND_INFO - 1] = (type == 5) ? 0 : '\n'; + snd.mid_info_offs[type] = offs; + if(snd.player && snd.player->open) + gui_update_text(gui_get(snd.player, 15)); +} + +void plr_tlog(int type, const char *text) { + int slen = strlen(text); + // for(int h = 0; h < slen; h++) { + // text[h] = ((text[h] < 32) && (text[h] >= 0)) ? ((h == (slen-1)) ? 0 : '?') : text[h]; + // } + if((type == 1) && (text[0] == '@')) { + // text += 1; + switch(text[1]) { + case 'T': + plr_info(0, text+2); + return; + case 'I': + plr_info(1, text+2); + return; + case 'K': + plr_info(2, text+2); + return; + case 'W': + plr_info(3, text+2); + return; + case 'L': + plr_info(4, text+2); + return; + case 'V': + plr_info(5, text+2); + return; + } + // text += (text[0] == 0) ? 0 : 1; + } + else if((type == 1) && (text[0] == '%') && (text[1] == '-')) { + plr_kar(); + return; + } + else if(type == 1 /* || type == 5 */) { + switch(text[0]) { + case '\\': + plr_kar(); + plr_clog(); + text += 1; + slen -= 1; + break; + case '/': + // mid_kar(); + if(snd.mid_karaoke) { + snd.mid_klog_pos += 1; + snd.mid_klog_offs = 0; + if(snd.mid_klog_pos == SND_KLOG) + plr_clog(); + text += 1; + slen -= 1; + } + break; + } + if(snd.mid_karaoke) { + int len; + while(slen > 0) { + len = (slen > ((SND_KAR - 1) - snd.mid_klog_offs)) ? ((SND_KAR - 1) - snd.mid_klog_offs) : slen; + if(len <= 0) + break; + memcpy(&snd.mid_klogger[snd.mid_klog_pos * SND_KAR + snd.mid_klog_offs], text, len); + snd.mid_klog_offs += len; + // snd.mid_klogger[snd.mid_klog_pos * SND_KAR + snd.mid_klog_offs] = 0; + slen -= len; + if(slen > 0) { + snd.mid_klog_pos += 1; + snd.mid_klog_offs = 0; + if(snd.mid_klog_pos == SND_KLOG) + plr_clog(); + } + } + if(snd.player && snd.player->open) + gui_update_text(gui_get(snd.player, 16)); + return; + } + } + if(snd.mid_debug || (type <= 2) || (type >= 5)) + logd(LOG_CON, STR_MID_TEXT, type, text); +} \ No newline at end of file diff --git a/program.c b/program.c new file mode 100644 index 0000000..ac008bf --- /dev/null +++ b/program.c @@ -0,0 +1,214 @@ + +/* + Een net ganz funktionierndes Programm ... + + Absolute Mindestanforderungen: + + Prozessor : Intel Core i3 530 oder AMD Phenom 2 X2 545 + Grafikkarte : NVIDIA GeForce GTS 450 oder AMD Radeon HD 7730 + Arbeitsspeicher : Etwa 512 MB ~ 1 GB, 2 - 4 GB empfohlen + Mainboard : Was gerade passt (auch mit Hammer) + Kühler : Ne Klimaanlage oder ordentlich Pusten + Festplatte : Richtet sich nach der Geduld des Nutzers + Netzteil : Atomkraftwerk oder stabiler Energiekern + Gehäuse : Pappkarton, Bierkasten oder auch gar keins + Tastatur : Hlb fktonrnd odr bsr + Maus : Aus Plastik oder mit extra Quietsch-Effekt + Monitor : Olle Röhre oder gebrochener Flachbild (Mindestens 1280x800) + Stuhl : Interessiert niemanden (sonst siehe Gehäuse) + + [[oder einfach einen Toaster mit nem LCD und Maus]] +*/ + +#if !defined(__x86_64) && !defined(__amd64) && !defined(_M_AMD64) +#error "Programm erfordert die Prozessor-Architektur x86_64 ('AMD64' / 'Intel 64')" +#endif + +#if !defined(__SSE__) +#error "Die x86_64-Erweiterung SSE ist nicht vorhanden" +#endif + +#if !defined(__SSE2__) +#error "Die x86_64-Erweiterung SSE2 ist nicht vorhanden" +#endif + +#if !defined(__linux__) && !defined(__gnu_linux__) && !defined(__linux) && !defined(linux) +#error "Programm unterstützt nur Linux als das System / ABI" +#endif + +#define SYS_PROGNAME "SKCBlitz" +#define SYS_INFO \ +COL_GREEN SYM_BUG SYM_BUG SYM_BUG " " COL_VIOLET FMT_FADE SYS_PROGRAM_FULL FMT_RESET COL_GREEN " " SYM_BUG SYM_BUG SYM_BUG "\n"\ +"\n"\ +COL_LGRAY "Ein kleine Anwendung zur Simulation, zum Testen, für Spiele, Musik und vieles" "\n"\ +"mehr. Optimiert für Geschwindigkeit, Stabilität und" STR_UNK STR_UNK " [Speicherzugriffsfehler]" "\n"\ +"\n"\ +COL_CYAN "Geschrieben von Sen dem \"kleinen\" Dämonen " COL_CRIMSON SYM_DEMON COL_BLACK SYM_BLKHEART "\n"\ +"\n"\ +COL_YELLOW "Verwendete Programmbibliotheken:" "\n"\ +COL_LGRAY " -> " COL_NEON "axe_ork (Modifiziert: GLFW 3.3.8)" "\n"\ +COL_LGRAY " -> " COL_NEON "tcglm (Modifiziert: cglm 0.8.9)" "\n"\ +COL_LGRAY " -> " COL_NEON "stb_timage (Modifiziert: stb_image 2.28 + stb_image_write 1.16)" "\n"\ +COL_LGRAY " -> " COL_NEON "gl_loader (Modifiziert: glad 0.1.34)" "\n"\ +COL_LGRAY " -> " COL_NEON "opl3 (Modifiziert: Nuked-OPL3 f2c9873)" "\n"\ +"\n"\ +COL_YELLOW "Verwendeter Compiler: " "\n"\ +COL_LGRAY " -> " COL_NEON BUILD_COMP "\n"\ +"\n"\ +COL_YELLOW "Kompiliert auf System: " "\n"\ +COL_LGRAY " -> " COL_NEON BUILD_SYS "\n"\ +"\n"\ +"\n"\ +COL_BLACK "#0 " COL_DGRAY "#1 " COL_GRAY "#2 " COL_LGRAY "#3 " COL_WHITE "#4 " COL_RED "#5 " COL_GREEN "#6 " COL_BLUE "#7 " \ +COL_YELLOW "#8 " COL_MAGENTA "#9 " COL_CYAN "#A " COL_VIOLET "#B " COL_ORANGE "#C " COL_CRIMSON "#D " COL_MIDNIGHT "#E " COL_NEON "#F " \ +COL_LGRAY FMT_ULINE "#G" FMT_RESET " " COL_YELLOW FMT_CLINE "#H" FMT_RESET " " COL_DGRAY FMT_BLINK "#I" FMT_RESET " " COL_BLACK FMT_FADE "#J" FMT_RESET + +#define ALSA_PCM_NEW_HW_PARAMS_API + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inttypes.h" +#include "x11_stub.h" +#include "glx_stub.h" +#include "system.h" +#include "gl_types.h" +#include "gl_constants.h" +#include "types.h" +#include "tcglm.h" +#include "stb_timage.h" +#include "argparse.h" +#include "str.h" +#include "list.h" +#include "table.h" +#include "map.h" +#include "smap.h" +#include "locale.h" +#include "locale.h" +#include "locale.h" +#include "locale.h" +#include "timing.h" +#include "log.h" +#include "gl_loader.h" +#include "gl_loader.h" +#include "mem.h" +#include "files.h" +#include "random.h" +#include "noise.h" +#include "texture.h" +#include "shader.h" +#include "buffer.h" +#include "framebuf.h" +#include "strdraw.h" +#include "strdraw.h" +#include "strdraw.h" +#include "strdraw.h" +#include "strdraw.h" +#include "camera.h" +#include "bbox.h" +#include "entity.h" +#include "lighting.h" +#include "cvar.h" +#include "console.h" +#include "render.h" +#include "drawlist.h" +#include "world.h" +#include "axe_ork.h" +#include "gui.h" +#include "window.h" +#include "opl3.h" +#include "dmx.h" +#include "bank.h" +#include "midi.h" +#include "alsasnd.h" +#include "wavfile.h" +#include "sound.h" +#include "player.h" +#include "program.h" + +int main(int argc, char **argv) { + SIZE_TEST(void*, 64); + SIZE_TEST(byte, 8); + SIZE_TEST(char, 8); + SIZE_TEST(ushort, 16); + SIZE_TEST(short, 16); + SIZE_TEST(uint, 32); + SIZE_TEST(int, 32); + SIZE_TEST(ulong, 64); + SIZE_TEST(long, 64); + SIZE_TEST(float, 32); + SIZE_TEST(double, 64); + + SIZE_TEST(light_t, 704); + + uint cpuid; + asm volatile("cpuid" : "=d" (cpuid) : "0" (1)); + sys_assert((cpuid & 0x6000000) == 0x6000000); + + int loglevel = 0xff; + char *cfgfile = "skc.cfg"; + char *langfile = NULL; + int gldebug = 0; + int nosound = 0; + int version = 0; + int n_cmds = 0; + char **cmds = malloc(sizeof(char*) * argc); + + argp_t *argspec = arg_alloc(7); + arg_add_vstr(argspec, ARGSTR_COMMANDS, 0, cmds, &n_cmds); + arg_add_bool(argspec, 'v', ARGSTR_VERSION, &version); + arg_add_int(argspec, 'l', ARGSTR_LOGLEVEL, LOG_SILENT, LOG_TRACE, &loglevel); + arg_add_bool(argspec, 'g', ARGSTR_GLDEBUG, &gldebug); + arg_add_bool(argspec, 's', ARGSTR_NOSOUND, &nosound); + arg_add_str(argspec, 'c', ARGSTR_CONFIG, &cfgfile); + arg_add_str(argspec, 't', ARGSTR_LANG, &langfile); + if(!arg_parse(argspec, argc, argv)) { + free(cmds); + return -1; + } + if(version) { + fprintf(stderr, SYS_PROGRAM_FULL "\n* " BUILD_COMP "\n* " BUILD_SYS "\n"); + free(cmds); + return 0; + } + if(!init(cfgfile, langfile, loglevel, gldebug, nosound)) { + free(cmds); + return 1; + } + start(cmds, n_cmds); + free(cmds); + run(); + finish(); + return 0; +} diff --git a/program.h b/program.h new file mode 100644 index 0000000..d56d571 --- /dev/null +++ b/program.h @@ -0,0 +1,743 @@ + +byte win_down(byte bind) { + return sys.binds[bind].pressed; +} + +byte win_pressed(byte bind) { + return sys.binds[bind].active; +} + +void handle_int(int sig) { + sys.interrupted = 1; + fprintf(stderr, "\n"); +} + +void input() { + bind_t *bind; + for(int z = 0; z < KEY_BINDS; z++) { + bind = &sys.binds[z]; + if(sys.input ^ 1) { + bind->pressed = 0; + } + else if(bind->key >= 1 && bind->key <= KEYSYM_LAST) { + bind->pressed = wcf.active && (wcf_getkey(wcf.active, bind->key) == KEY_PRESS); + } + else if(bind->key >= -MOUSE_BTN_C && bind->key <= -MOUSE_LEFT) { + bind->pressed = wcf.active && !(wcf.active->open) && ((wcf.active->mouse >> (-MOUSE_LEFT - bind->key)) & 1); + if(!sys.mouse_bind) + bind->last_state = bind->pressed; + } + else if(bind->key >= -SCROLL_RIGHT && bind->key <= -SCROLL_UP) { + bind->pressed = wcf.active && !(wcf.active->open) && ((wcf.active->scroll >> (-SCROLL_UP - bind->key)) & 1); + } + else { + bind->pressed = 0; + } + bind->active = bind->pressed && !(bind->last_state); + bind->last_state = bind->pressed; + } + sys.mouse_bind = 1; + if(wcf.active) + wcf.active->scroll = 0; + + if(win_pressed(KEY_QUIT)) { + sys.interrupted = 1; + } + if(win_pressed(KEY_SYNC)) { + cvar_setint("win_sync", sys.win_vsync ? sys.sync_limit : (sys.sync_limited ? -1 : 0)); + } + if(win_pressed(KEY_SCREENSHOT)) { + sys.screenshot = 1; + } + if(win_pressed(KEY_PLAYER)) { + plr_rand(); + if(snd.player && snd.player->open) + gui_update_text(gui_get(snd.player, 1)); + } + if(wcf.active && wcf.active->world) { + if(win_pressed(KEY_FULLSCREEN)) { + win_full(!sys.win_full); + } + if(win_pressed(KEY_MENU)) { + gui_open(wcf.active, wcf.active->open ^ 1); + } + if(win_pressed(KEY_SHOW)) { + if(sys.draw_debug) { + sys.draw_debug = sys.draw_fps = 0; + } + else { + sys.draw_debug = sys.draw_fps; + sys.draw_fps = 1; + } + } + } + if(wcf.active && wcf.active->world && !wcf.active->open) { + if(wcf.active->world->pointed && win_pressed(KEY_FLY)) { + world_remove(wcf.active->world, wcf.active->world->pointed); + } + if(wcf.active->world->noclip) { + if((!(wcf.active->world->camonly) && win_pressed(KEY_NOCLIP)) || (wcf.active->world->camonly && win_pressed(KEY_CAMERA))) { + if(wcf.active->world->camonly && wcf.active->world->entity) { + cam_angle_ent(&wcf.active->world->camera, wcf.active->world->entity); + cam_pos_ent(&wcf.active->world->camera, wcf.active->world->entity, 0.0); + } + log_hud(wcf.active, wcf.active->world->camonly ? STR_CAMERA_OFF : STR_NOCLIP_OFF); + wcf.active->world->noclip = wcf.active->world->camonly = 0; + } + } + else if(wcf.active->world->entity) { + if(win_pressed(KEY_NOCLIP)) { + log_hud(wcf.active, STR_NOCLIP_ON); + wcf.active->world->noclip = 1; + } + else if(win_pressed(KEY_CAMERA)) { + log_hud(wcf.active, STR_CAMERA_ON); + wcf.active->world->noclip = wcf.active->world->camonly = 1; + } + } + if(win_down(KEY_ZOOM_IN)) { + cam_zoom(&wcf.active->world->camera, 0.05f); + } + if(win_down(KEY_ZOOM_OUT)) { + cam_zoom(&wcf.active->world->camera, -0.05f); + } + if((wcf.active->world->noclip && wcf.active->world->camonly) || !(wcf.active->world->entity)) { + float speed = sys.speed * (win_down(KEY_FAST) ? 20.0f : 5.0f); + if(win_down(KEY_DOWN)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_DOWN); + } + if(win_down(KEY_UP)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_UP); + } + if(win_down(KEY_FORWARD)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_FORWARD); + } + if(win_down(KEY_BACKWARD)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_BACKWARD); + } + if(win_down(KEY_LEFT)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_LEFT); + } + if(win_down(KEY_RIGHT)) { + cam_move_noclip(&wcf.active->world->camera, sys.fdelta, speed, DIR_RIGHT); + } + } + else { + wcf.active->world->entity->speed = /* sys.speed_fixed ? 1.0f : */ sys.speed; + wcf.active->world->entity->sprint = win_down(KEY_FAST); + wcf.active->world->entity->sneak = win_down(KEY_DOWN); + wcf.active->world->entity->jump = win_down(KEY_UP); + wcf.active->world->entity->forward = (win_down(KEY_FORWARD) ? 1.0f : 0.0f) + (win_down(KEY_BACKWARD) ? -1.0f : 0.0f); + wcf.active->world->entity->strafe = (win_down(KEY_LEFT) ? 1.0f : 0.0f) + (win_down(KEY_RIGHT) ? -1.0f : 0.0f); + } + // if(wld.entity && !(wld.camonly)) + // ent_angle_cam(wld.entity, &wld.camera); + } + if(!(sys.keywait)) + sys.input = 1; +} + +void screenshot() { + time_t t = time(0); + struct tm *date = localtime(&t); + struct stat statbuf; + int stride = (((wcf.current->frame_x * 3) & 3) ? (4 + ((wcf.current->frame_x * 3) & ~3)) : (wcf.current->frame_x * 3)); + int n = 0; + byte *data = mem_alloc(stride * wcf.current->frame_y, MEM_IMAGE); + mkdir(DIR_SCREENSHOTS, 0777); + do { + sprintf(sys.work_buf, DIR_SCREENSHOTS"/screen_%02d-%02d-%d_%02d-%02d-%02d%s%.0d.png", + date->tm_mday, date->tm_mon + 1, date->tm_year + 1900, date->tm_hour, date->tm_min, date->tm_sec, n ? "_" : "", n); + n++; + } + while(!stat(sys.work_buf, &statbuf)); + glReadPixels(wcf.current->offset_x, (wcf.current->fb_y - wcf.current->frame_y) - wcf.current->offset_y, + wcf.current->frame_x, wcf.current->frame_y, GL_RGB, GL_UNSIGNED_BYTE, data); + if(img_save(sys.work_buf, wcf.current->frame_x, wcf.current->frame_y, 0, 1, stride, data)) + logu(LOG_SYS, STR_MSG_SCREENSHOT, sys.work_buf); + mem_free(data); +} + +const char *framecode() { + return (sys.tmr_framerate >= 59.0f) ? COL_GREEN : ((sys.tmr_framerate >= 29.0f) ? COL_YELLOW : ((sys.tmr_framerate >= 14.0f) ? COL_ORANGE : COL_RED)); +} + +const char *tpscode() { + return (sys.tick_tickrate >= (((float)sys.tick_target / 1000.0f) - 1.0f)) ? COL_GREEN : ((sys.tick_tickrate >= (((float)sys.tick_target / 1000.0f) / 2.0f - 1.0f)) ? COL_YELLOW : + ((sys.tick_tickrate >= (((float)sys.tick_target / 1000.0f) / 4.0f - 1.0f)) ? COL_ORANGE : COL_RED)); +} + +ulong perf_get(byte section) { + return sys.tmr_profile[(sys.perf_swap ? 0 : PERF_SECTIONS) + section]; +} + +byte gl_isdebug() { + int flags; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + return (flags & GL_CONTEXT_FLAG_DEBUG_BIT) == GL_CONTEXT_FLAG_DEBUG_BIT; +} + +const char *gl_getsuffix() { + int mask; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + if(mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + return " compat"; + else if(mask & GL_CONTEXT_CORE_PROFILE_BIT) + return " core"; + else + return ""; +} + +void print_debug(world_t *world) { + int pos; + pos = sprintf(sys.work_buf, + "%s%.2f"COL_RESET" %s, @ "COL_RED"%.1f "COL_GREEN"%.1f "COL_CYAN"%.1f"COL_RESET", Y "COL_MAGENTA"%.1f"COL_RESET", P "COL_YELLOW"%.1f"COL_RESET + ", H %.1f, F %.1f, Z %.1f, M "COL_RED"%.1f "COL_GREEN"%.1f "COL_CYAN"%.1f"COL_RESET + , + framecode(), (sys.tmr_framerate < 1.0f) ? (1.0f / sys.tmr_framerate) : sys.tmr_framerate, sys.tmr_framerate < 1.0f ? "SPF" : "FPS", + world->camera.pos_x, world->camera.pos_y, world->camera.pos_z, world->camera.yaw, world->camera.pitch, (world->entity && !world->camonly) ? world->entity->eye : 0.0, + gdr.fov, world->camera.zoom * (gdr.fov - 1.0f), + world->entity ? world->entity->motion_x : 0.0, world->entity ? world->entity->motion_y : 0.0, world->entity ? world->entity->motion_z : 0.0 + ); + if(world->pointed) + pos = sprintf(&sys.work_buf[pos], + ", -> "COL_YELLOW"#%llu "COL_RESET"("COL_RED"%.1f "COL_GREEN"%.1f "COL_CYAN"%.1f"COL_RESET")" + , + world->pointed, world->look_pos X, world->look_pos Y, world->look_pos Z + ); +} + +void tick_start(world_t *world) { + world->last_cx = (int)NEG_OFFSET(world->camera.pos_x, CHUNK_SIZE); + world->last_cy = (int)NEG_OFFSET(world->camera.pos_y, CHUNK_SIZE); + world->last_cz = (int)NEG_OFFSET(world->camera.pos_z, CHUNK_SIZE); + if(world->entity && !(world->camonly)) + ent_angle_cam(world->entity, &world->camera); +} + +void tick_main(world_t *world) { + ent_tick(&world->entity_base); +} + +void tick_end(world_t *world) { + if(world->entity && !(world->camonly)) + cam_pos_ent(&world->camera, world->entity, sys.tick_fraction); + if(world->last_cx != (int)NEG_OFFSET(world->camera.pos_x, CHUNK_SIZE) || world->last_cy != (int)NEG_OFFSET(world->camera.pos_y, CHUNK_SIZE) || + world->last_cz != (int)NEG_OFFSET(world->camera.pos_z, CHUNK_SIZE)) + world->light_dirty = 1; +} + +void tick() { + window_t *win; + sys.tick_stime = tmr_rtime(); + if((sys.tick_stime - sys.tick_update) >= 1000000ULL) { + sys.tick_tickrate = ((float)sys.tick_done) * 1000000.0f / ((float)(ulong)(sys.tick_stime - sys.tick_update)); + sys.tick_done = 0; + sys.tick_update = sys.tick_stime; + } + if(sys.ticked ^ 1) { + sys.tick_time = 0ULL; + return; + } + sys.tick_torun += (((ulong)sys.tick_target) * sys.tmr_delta) / 1000ULL; + sys.tick_frame = 0; + for(win = wcf.window_list; win; win = win->next) { + if(win->world) + tick_start(win->world); + } + while(sys.tick_torun >= 1000000ULL) { + for(win = wcf.window_list; win; win = win->next) { + if(win->world) + tick_main(win->world); + } + sys.tick_done += 1ULL; + sys.tick_frame += 1; + sys.tick_total += 1ULL; + sys.tick_torun -= 1000000ULL; + if((sys.tick_ftime = (tmr_rtime() - sys.tick_stime)) >= (((ulong)sys.tick_timeout) * 1000ULL)) { + logw(LOG_TIC, STR_TICK_TIMEOUT, sys.tick_ftime / 1000ULL, sys.tick_timeout, sys.tick_torun / 1000000ULL); + sys.tick_torun = 0ULL; + break; + } + } + sys.tick_fraction = ((double)sys.tick_torun) / 1000000.0; + for(win = wcf.window_list; win; win = win->next) { + if(win->world) + tick_end(win->world); + } + sys.tick_ttime += sys.tick_ftime; + sys.tick_time = sys.tick_frame ? (sys.tick_ftime / (ulong)sys.tick_frame) : 0ULL; +} + +void tick_target(float tps) { + sys.tick_target = (int)(tps * 1000.0f); + sys.tick_torun = 0ULL; + sys.tick_done = 0ULL; + sys.tick_update = tmr_rtime(); + sys.ticked = tps > 0.0f; +} + +float rfloat(float min, float max) { + return ((float)(int)(rand() % 1000000000)) / 1000000000.0f * (max - min) + min; +} + +void floor_test(world_t *world, material_t *mat, int n) { + for(int z = 0; z < n; z++) { + world_add_floor(world, rfloat(-1800.0f, 1800.0f), rfloat(-230.0f, 230.0f), rfloat(-1800.0f, 1800.0f), rfloat(8.0f, 40.0f), rfloat(8.0f, 40.0f), mat); + } +} + +void snd_apply() { + plr_debug(cvar_byte("mid_debug_events")); + for(int z = 0; z < SND_VOLUMES; z++) { + plr_volume(z, (ushort)(cvar_float(snd_cvar_volume[z]) * 32767.0f)); + } +} + +void save_vars() { + cvar_setint("win_pos_x", sys.console->xpos); + cvar_setint("win_pos_y", sys.console->ypos); + cvar_setint("win_width", sys.console->xsize); + cvar_setint("win_height", sys.console->ysize); +} + +static const float lm_test[] = {20.0f, 20.0f, 40.0f, 20.0f, 20.0f, 40.0f}; + +float wg_test(void *p, float x, float z) { + noisegen_t *gen = (noisegen_t*)p; + // float n = (x * x + z * z) * + // (sinf(7.57f * M_PI*2.0f*x) + 1.0f) * (sinf(16.7585f * M_PI*2.0f*z) + 1.0f) * (sinf(8.657f * M_PI*2.0f*x) + 1.0f) * (sinf(6.75485f * M_PI*2.0f*z) + 1.0f); + // srand((int)(x * 16384.0f) ^ (int)(z * 16384.0f)); + // n += rfloat(0.0f, n / 8.0f); + double n; + noise_gen(gen, &n, (double)x, 0, (double)z, 1, 1, 1, 512.0, 512.0, 512.0); + return (float)(n / 512.0); +} + +uint tex_test, tex_floor; +material_t *mat_test; +material_t *mat_floor; +material_t *mat_grid; +material_t *mat_vis; + +void start(char **cmds, int n_cmds) { + tex_load(&tex_test, "textures/test.png", TEX_FLAG_FILTER | TEX_FLAG_MIPMAP); + tex_load(&tex_floor, "textures/floor.png", TEX_FLAG_FILTER | TEX_FLAG_MIPMAP); + mat_test = gfx_add_material(tex_test, 512.0f, 0xffffff, 4.0f, NULL); + mat_floor = gfx_add_material(tex_floor, 512.0f, 0xffffff, 1.0f, NULL); + mat_grid = gfx_add_material(0, 512.0f, 0xffffff, 1.0f, gdr.shd_grid); + mat_vis = gfx_add_material(0, 512.0f, 0xffffff, 1.0f, gdr.shd_vis); + // plr_play("/home/sen/Musik/midi50k/Video_Games/ff/ff2cecil.mid", 0); + for(int z = 0; z < n_cmds; z++) { + if(strlen(cmds[z]) >= CON_LINE) + loge(LOG_SYS, STR_CON_TOOLONG, z + 1); + else + con_exec(cmds[z]); + } +} + +void test(window_t *win) { + world_init(win, 0.0, 32.0, 0.0, 90.0f, 0.0f); + noisegen_t gen; + ulong rng = rng_rseed(); + noise_gen_init(&gen, &rng, 16); + switch(rng_zrange(&rng, 3)) { + case 0: + for(int z = 0; z < 10; z++) { + world_add_box(win->world, 10.0f + (float)z * 2.0f, 64.0f, 20.0f, 4.0f, (float)(z + 1) * 0.5f, 6.0f, mat_test); + } + world_add_graph(win->world, wg_test, &gen, -1536.0f, 0.0f, -1536.0f, 3072.0f, 0.125f, 3072.0f, mat_floor, + 128, 128, 0.0f, 0.0f, 512.0f, 512.0f, 1); + light_b lamp; + for(int z = 0; z < 256; z++) { + light_init_def(&lamp, ((rand() & 0xff) | 0x40) << ((rand() % 3) << 3), 1.0f); + world_add_light(win->world, &lamp, rfloat(-1250.0f, 1250.0f), rfloat(4.0f, 8.0f), rfloat(-1250.0f, 1250.0f)); + } + break; + + case 1: + world_add_graph(win->world, wg_test, &gen, -1536.0f, 0.0f, -1536.0f, 3072.0f, 2.0f, 3072.0f, mat_grid, + 128, 128, 0.0f, 0.0f, 512.0f, 512.0f, 1); + break; + + case 2: + world_add_graph(win->world, wg_test, &gen, -1536.0f, 0.0f, -1536.0f, 3072.0f, 1.0f, 3072.0f, mat_vis, + 256, 256, 0.0f, 0.0f, 512.0f, 512.0f, 1); + break; + } + noise_gen_free(&gen); +} + +void run() { + window_t *win; + PERF_FIRST() + while(sys.interrupted ^ 1) { + PERF_START(PERF_TIMING) + sys.tmr_current = tmr_rtime(); + sys.tmr_delta = sys.tmr_current - sys.tmr_last; + sys.tmr_last = sys.tmr_current; + sys.fdelta = ((float)sys.tmr_delta) / 1000000.0f; + if((sys.tmr_current - sys.tmr_update) >= 1000000ULL) { + sys.tmr_framerate = ((float)sys.tmr_frames) * 1000000.0f / ((float)(ulong)(sys.tmr_current - sys.tmr_update)); + sys.tmr_frames = 0; + sys.tmr_update = sys.tmr_current; + // logi(LOG_SYS, "%.3f FPS", sys.tmr_framerate); + } + PERF_SEC(PERF_INPUT) + for(win = wcf.window_list; win; win = win->next) { + gui_check(win); + } + input(); + if(wcf.active) + gui_await(wcf.active); + PERF_SEC(PERF_TICK) + tick(); + PERF_SEC(PERF_UPDATE) + plr_update(); + for(win = wcf.window_list; win; win = win->next) { + if(win->world) { + // wcf_selcontext(wcf.active); + // glViewport(0, 0, wcf.active->fb_x, wcf.active->fb_y); + world_update(win->world); + win->world->pointed = box_get_pointed(win->world, &win->world->camera, win->world->look_pos); + } + } + PERF_SEC(PERF_RENDER) + gdr.lists_drawn = 0; + gdr.tris_drawn = 0; + for(win = wcf.window_list; win; win = win->next) { + if(win->world) { + wcf_selcontext(win); + gdr.shader = 0; + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, gdr.post ? win->world->fb_post : 0); + glClearColor(gdr.clear_r, gdr.clear_g, gdr.clear_b, gdr.clear_a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if(gdr.wireframe) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glViewport(win->offset_x, (win->fb_y - win->frame_y) - win->offset_y, + win->frame_x, win->frame_y); + // gfx_pass_world(); + // glBindTexture(GL_TEXTURE_2D, gdr.tex_test); + // glDrawArrays(GL_TRIANGLES, 0, 36); + // draw_floor(gdr.world); + draw_world(win->world); + // ... + // glViewport(0, 0, wld.window->fb_x, wld.window->fb_y); + if(gdr.wireframe) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + } + PERF_SEC(PERF_GUI) + for(win = wcf.window_list; win; win = win->next) { + wcf_selcontext(win); + gdr.shader = 0; + glViewport(0, 0, win->fb_x, win->fb_y); + glDisable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, win->fb_gui); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + if(win->redraw) { + win->redraw = 0; + glClear(GL_COLOR_BUFFER_BIT); + } + if(win->fb_x && win->fb_y) + gfx_draw_gui(win); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + if(gdr.post && win->world) + gfx_blit(win->world->fbtex_post); + gfx_draw_rect(0, win->offset_y - (win->fb_x - win->frame_x) / 2, win->fb_x, win->frame_y + win->fb_x - win->frame_x, (win->fb_x - win->frame_x) / 2, + 0, 0, 0, sys.style.wbrdr_top, sys.style.wbrdr_btm); + if(!(win->world)) + gfx_draw_rect(win->offset_x, win->offset_y, win->frame_x, win->frame_y, 0, 0, sys.style.bg_top, sys.style.bg_btm, 0, 0); + else if(!win->open) + gfx_draw_rect(win->offset_x + (win->frame_x / 2) - 16, win->offset_y + (win->frame_y / 2) - 16, 32, 32, 0, gdr.tex_crosshair, + win->world->pointed ? 0xffffffff : 0xffcfcfcf, win->world->pointed ? 0xffffffff : 0xffcfcfcf, 0, 0); + gfx_blit(win->fbtex_gui); + if(win == wcf.active) + gfx_draw_gui_overlay(win); + if(win->drag_x || win->drag_y) { + sprintf(sys.work_buf, "%d x %d", win->frame_x, win->frame_y); + txt_draw(0, 0, 0, sys.font.yglyph, sys.font.yglyph, + 0, 0, win->fb_x, win->fb_y, + 0xffff0000, 0xff000000, &sys.font, sys.work_buf); + } + if(sys.hud_overlay && win->world) + gfx_draw_con_overlay(win); + if(sys.draw_fps && win->world) { + // gfx_pass_text(); + if(sys.draw_debug) { + print_debug(win->world); + txt_draw(win->offset_x, win->offset_y + (sys.hud_bottom ? 0 : (win->frame_y - sys.font.yglyph)), + 0, sys.font.yglyph, sys.font.yglyph, + win->offset_x, win->offset_y + (sys.hud_bottom ? 0 : (win->frame_y - sys.font.yglyph)), + win->offset_x + win->frame_x, win->offset_y + win->frame_y, + 0xffffffff, 0x40000000, &sys.font, sys.work_buf); + // print_profile(); + // txt_draw(win->offset_x + win->frame_x - ((sys.mono.xglyph - 1) * 28), win->offset_y, 0, sys.mono.yglyph, sys.mono.yglyph, + // win->offset_x + win->frame_x - ((sys.mono.xglyph - 1) * 28), win->offset_y, win->offset_x + win->frame_x, win->offset_y + win->frame_y, + // 0xffffffff, 0x40000000, &sys.mono, sys.work_buf); + } + else { + sprintf(sys.work_buf, "%s%.2f", framecode(), sys.tmr_framerate); + txt_draw(win->offset_x, win->offset_y + (sys.hud_bottom ? 0 : (win->frame_y - sys.font.yglyph)), + 0, sys.font.yglyph, sys.font.yglyph, + win->offset_x, win->offset_y + (sys.hud_bottom ? 0 : (win->frame_y - sys.font.yglyph)), + win->offset_x + win->frame_x, win->offset_y + win->frame_y, + 0xffffffff, 0x40000000, &sys.font, sys.work_buf); + } + } + } + PERF_SEC(PERF_REST) + if(sys.screenshot) { + sys.screenshot = 0; + if(wcf.active) { + wcf_selcontext(wcf.active); + glViewport(0, 0, wcf.active->fb_x, wcf.active->fb_y); + screenshot(); + } + } + if(sys.cfg_dirty) { + save_vars(); + cvar_save(); + sys.cfg_dirty = 0; + } + PERF_SEC(PERF_SWAP) + for(win = wcf.window_list; win; win = win->next) { + wcf_selcontext(win); + glViewport(0, 0, win->fb_x, win->fb_y); + if(sys.gl_flush) + glFlush(); // glFinish(); + wcf_swap(win); + } + PERF_SEC(PERF_EVENTS) + snd_log_flush(); + wcf_poll(); + PERF_SEC(PERF_WAIT) + while(sys.sync_limited && (tmr_rtime() - sys.tmr_current) < (1000000ULL / sys.sync_limit)) { + ; + } + sys.tmr_frames += 1; + PERF_END() + } + PERF_LAST() +} + +void end() { + wcf_end(); +// #ifdef MEM_TRACING + // logt(LOG_MEM, STR_MEM_FREE, SYS_BASE, (ulong)(&sys), sys.mem_alloc -= SYS_BASE, "base"); +// #else + sys.mem_alloc -= SYS_BASE; +// #endif + logp(LOG_MEM, STR_MEM_PEAK, sys.mem_peak); + if(sys.mem_alloc) { + logw(LOG_MEM, STR_MEM_LEAK, sys.mem_alloc); + } + logi(LOG_SYS, STR_EXIT); + sys_remove_handlers(); +} + +void finish() { + logi(LOG_SYS, STR_SHUTDOWN); + // world_delete(wld.world); + // wld.world = NULL; + plr_end(); + if(snd_end()) + logi(LOG_SYS, STR_SND_STOP); + save_vars(); + win_end(); + con_end(); + cvar_end(); + gfx_end(); + end(); +} + +void snd_start(byte disabled) { + const char *devname = snd_edevice[cvar_int("snd_device_type")]; + if(!strcmp(devname, "hw") || !strcmp(devname, "plughw")) { + int dev = cvar_int("snd_device_index"); + int sub = cvar_int("snd_device_sub"); + sprintf(sys.work_buf, dev < 0 ? "default" : (sub < 0 ? "%s:%d" : "%s:%d,%d"), devname, dev, sub); + devname = sys.work_buf; + } + uint samplerate = snd_init(devname, cvar_int("snd_sample_rate"), cvar_byte("snd_sample_format") ? 32 : 16, cvar_int("snd_frame_size"), cvar_int("snd_buffer_size"), disabled); + snd_log_flush(); + if(samplerate) + logi(LOG_SYS, STR_SND_START, devname, samplerate); + else if(!disabled && strcmp(devname, "none")) + loge(LOG_SYS, STR_SND_STARTERR, devname); +} + +void snd_restart() { + byte disabled = sgt.disabled; + plr_end(); + if(snd_end()) + logi(LOG_SYS, STR_SND_STOP); + memset(&sgt, 0, sizeof(isnd_t)); + memset(&snd, 0, sizeof(snd_t)); + snd_start(disabled); + snd_apply(); +} + +const char *gl_debug_source(uint src) { + switch(src) { + case GL_DEBUG_SOURCE_API: + return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + return "WIN"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + return "SHD"; + case GL_DEBUG_SOURCE_THIRD_PARTY: + return "3RD"; + case GL_DEBUG_SOURCE_APPLICATION: + return "PRG"; + case GL_DEBUG_SOURCE_OTHER: + default: + return "XXX"; + } +} + +const char *gl_debug_type(uint type) { + switch(type) { + case GL_DEBUG_TYPE_ERROR: + return "ERR!"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + return "DEPR"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + return "NDEF"; + case GL_DEBUG_TYPE_PORTABILITY: + return "PORT"; + case GL_DEBUG_TYPE_PERFORMANCE: + return "PERF"; + case GL_DEBUG_TYPE_MARKER: + return "MARK"; + case GL_DEBUG_TYPE_PUSH_GROUP: + return "GPSH"; + case GL_DEBUG_TYPE_POP_GROUP: + return "GPOP"; + case GL_DEBUG_TYPE_OTHER: + default: + return "XXXX"; + } +} + +byte gl_debug_severity(uint level) { + switch(level) { + case GL_DEBUG_SEVERITY_HIGH: + return LOG_ERROR; + case GL_DEBUG_SEVERITY_MEDIUM: + return LOG_WARN; + case GL_DEBUG_SEVERITY_LOW: + return LOG_INFO; + case GL_DEBUG_SEVERITY_NOTIFICATION: + default: + return LOG_PERF; + } +} + +void gl_debug_callback(uint src, uint type, uint id, uint level, int len, const char *msg, const void *user) { + byte lvl = gl_debug_severity(level); + const char *color = log_colors[lvl]; + log_str(LOG_GFX, lvl, COL_DGRAY"[%sGL:%d"COL_DGRAY"][%s%s"COL_DGRAY"][%s%s"COL_DGRAY"]%s %s", + color, id, color, gl_debug_source(src), color, gl_debug_type(type), color, msg); +} + +byte init(const char *cfgfile, const char *langfile, byte loglevel, byte gldebug, byte nosound) { + clockid_t timer; + sys_setup_handlers(); + sys_test_handlers(getenv("ERROR_HANDLER_TEST")); + timer = tmr_gettimer(); + memset(&sys, 0, sizeof(sys_t)); + memset(&gdr, 0, sizeof(gfx_t)); + memset(&sgt, 0, sizeof(isnd_t)); + memset(&snd, 0, sizeof(snd_t)); + signal(SIGINT, handle_int); + sys.tmr_timer = (ulong)timer; + sys.tmr_start = tmr_time(); + sys.log_level = loglevel == 0xff ? LOG_INFO : loglevel; + sys.log_set = loglevel; + sys.cfg_file = cfgfile; + sys.rng_uniq = 8682522807148012L; + + con_reset(); + + loc_init(); + loc_def(); + if(langfile) + loc_load(langfile); + + logi(LOG_SYS, SYS_PROGRAM_FULL); + logi(LOG_SYS, STR_BUILD_COMPILER, BUILD_COMP); + logi(LOG_SYS, STR_BUILD_SYSTEM, BUILD_SYS); +// #ifdef MEM_TRACING + // logt(LOG_MEM, STR_MEM_ALLOC, SYS_BASE, (ulong)(&sys), sys.mem_alloc = sys.mem_peak = SYS_BASE, "base"); +// #else + sys.mem_alloc = sys.mem_peak = SYS_BASE; +// #endif + + if(!wcf_init(gldebug)) + return 0; + if(!(sys.console = wcf_create(SYS_PROGRAM, 1))) { + loge(LOG_GFX, STR_WIN_CREATE_ERR); + wcf_end(); + return 0; + } + if(gl_isdebug()) { + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, 1); + glDebugMessageCallback(gl_debug_callback, NULL); + } + logi(LOG_GFX, "OpenGL %s%s%s", glGetString(GL_VERSION), gl_getsuffix(), gl_isdebug() ? " debug" : ""); + logi(LOG_GFX, "GLSL %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + logi(LOG_GFX, "GL_VENDOR: %s", glGetString(GL_VENDOR)); + logi(LOG_GFX, "GL_RENDERER: %s", glGetString(GL_RENDERER)); + logi(LOG_SYS, STR_STARTUP); + if(!gfx_init()) { + gfx_end(); + end(); + return 0; + } + cvar_init(); + con_init(); + win_init(); + win_sync(cvar_int("win_sync")); + snd_start(nosound); + snd_apply(); + light_setup(cvar_int("gl_dynlight_max")); + cvar_setdef("tic_target"); + tex_load(&gdr.tex_logo, "textures/logo.png", 0); + tex_load(&gdr.tex_crosshair, "textures/crosshair.png", 0); + return 1; +} + +/* +txt_draw(80, 80, 0, 18, 18, 80, 80, sys.fb_x - 80, sys.fb_y - 80, 0xff1f1f1f, 0x40000000, &sys.font, + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_BLACK" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_DGRAY" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_GRAY" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_LGRAY" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_WHITE" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_RED" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_GREEN" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_BLUE" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_YELLOW" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_MAGENTA" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_CYAN" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_VIOLET" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_ORANGE" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_CRIMSON" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_MIDNIGHT" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_NEON" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX1" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX2" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX3" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX4" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX5" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX6" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX7" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_AUX8" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~"STR_UNK"§°²³´µÄÖÜßäöü€"STR_WSPC"\n" + COL_RESET"END\n" +); +*/ diff --git a/random.h b/random.h new file mode 100644 index 0000000..ce3845b --- /dev/null +++ b/random.h @@ -0,0 +1,153 @@ + +#define RNG_MULT 0x5DEECE66DL +#define RNG_ADDEND 0xBL +#define RNG_MASK (1L << 48) - 1 +#define RNG_UNIT 0x1.0p-53 // 1.0 / (1L << 53) + +ulong rng_rseed() { + return (((sys.rng_uniq *= 181783497276652981L) ^ tmr_ntime()) ^ RNG_MULT) & RNG_MASK; +} + +ulong rng_sseed(ulong seed) { + return (seed ^ RNG_MULT) & RNG_MASK; +} + +uint rng_gen(ulong *rand, int bits) { + *rand = ((*rand) * RNG_MULT + RNG_ADDEND) & RNG_MASK; + return (uint)((*rand) >> (48 - bits)); +} + +uint rng_uint(ulong *rand) { + return rng_gen(rand, 32); +} + +int rng_zrange(ulong *rand, int bound) { + sys_assert(bound > 0); + int r = rng_gen(rand, 31); + int m = bound - 1; + if((bound & m) == 0) // ^2 + r = (int)((bound * (long)r) >> 31); + else + for(int u = r; u - (r = u % bound) + m < 0; u = rng_gen(rand, 31)); + return r; +} + +uint rng_urange(ulong *rand, uint bound) { + sys_assert(bound); + uint r = rng_gen(rand, 32); + uint m = bound - 1; + if((bound & m) == 0) // ^2 + r = (uint)((bound * (ulong)r) >> 31); + else + for(uint u = r; u - (r = u % bound) + m < 0; u = rng_gen(rand, 32)); + return r; +} + +ulong rng_ulong(ulong *rand) { + return ((ulong)(rng_gen(rand, 32)) << 32) | (ulong)rng_gen(rand, 32); +} + +byte rng_bool(ulong *rand) { + return (byte)rng_gen(rand, 1); +} + +float rng_float(ulong *rand) { + return rng_gen(rand, 24) / ((float)(1 << 24)); +} + +double rng_double(ulong *rand) { + return (((long)(rng_gen(rand, 26)) << 27) + rng_gen(rand, 27)) * RNG_UNIT; +} + +void rng_gauss(ulong *rand, double *n1, double *n2) { + double v1, v2, s; + do { + v1 = 2.0 * rng_double(rand) - 1.0; // between -1 and 1 + v2 = 2.0 * rng_double(rand) - 1.0; // between -1 and 1 + s = v1 * v1 + v2 * v2; + } + while (s >= 1.0 || s == 0.0); + double mul = sqrt(-2.0 * log(s)/s); + *n1 = v1 * mul; + *n2 = v2 * mul; +} + +int rng_excl(ulong *rand, int min, int max) { + return min + rng_zrange(rand, max - min); +} + +int rng_ch_offset(ulong *rand) { + return 8 + rng_gen(rand, 4); +} + +int rng_roll(ulong *rand, int range) { + return 1 + rng_zrange(rand, range); +} + +int rng_roll_bonus(ulong *rand, int range, int bonus) { + return 1 + rng_zrange(rand, range) + rng_zrange(rand, bonus + 1); +} + +int rng_range(ulong *rand, int min, int max) { + return min + rng_zrange(rand, max + 1 - min); +} + +int rng_range_bonus(ulong *rand, int min, int max, int bonus) { + return (min + rng_zrange(rand, max + 1 - min)) + rng_zrange(rand, bonus + 1); +} + +int rng_range_checked(ulong *rand, int min, int max) { + return min >= max ? min : (rng_zrange(rand, max + 1 - min) + min); +} + +float rng_frange(ulong *rand, float min, float max) { + return min >= max ? min : (rng_float(rand) * (max - min) + min); +} + +double rng_drange(ulong *rand, double min, double max) { + return min >= max ? min : (rng_double(rand) * (max - min) + min); +} + +byte rng_chance(ulong *rand, int chance) { + return !rng_zrange(rand, chance); +} + +byte rng_rarity(ulong *rand, int chance) { + return rng_zrange(rand, chance) != 0; +} + +void *rng_select(ulong *rand, void *a, void *b, int chance) { + return rng_zrange(rand, chance) ? a : b; +} + +int rng_iselect(ulong *rand, int a, int b, int chance) { + return rng_zrange(rand, chance) ? a : b; +} + +byte rng_percent(ulong *rand, float chance) { + return rng_float(rand) < chance / 100.0f; +} + +void *rng_pselect(ulong *rand, void *a, void *b, float chance) { + return rng_float(rand) < chance / 100.0f ? b : a; +} + +void *rng_rselect(ulong *rand, void *a, void *b) { + return rng_gen(rand, 1) != 0 ? b : a; +} + +void *rng_pick(ulong *rand, void **obj, uint size) { + return obj[rng_zrange(rand, size)]; +} + +int rng_ipick(ulong *rand, int *obj, uint size) { + return obj[rng_zrange(rand, size)]; +} + +void *rng_weight(ulong *rand, void *a, void *b, int ca, int cb) { + return rng_zrange(rand, ca + cb) >= ca ? b : a; +} + +void *rng_fselect(ulong *rand, void *a, void *b, float chance) { + return rng_float(rand) < chance ? b : a; +} diff --git a/render.h b/render.h new file mode 100644 index 0000000..00db169 --- /dev/null +++ b/render.h @@ -0,0 +1,552 @@ + +/* +void gfx_calc_mono(fchar_t *glyphs, int width, int height) { + fchar_t *ch; + for(int z = 0; z < 256; z++) { + ch = &glyphs[z]; + if(ch->u) { + ch->s = ch->t = 2; + ch->u = width - 2; + ch->v = height - 2; + } + } +} +*/ + +byte gfx_font_special(int width, uint ch) { + return ((ch == CHR_UNK) || (ch == CHR_WSPC)) ? width : ((ch == CHR_SPC) ? (width / 6) : 255); +} + +void gfx_calc_font(byte *data, fchar_t *glyphs, int width, int height, int page) { + int off; + fchar_t *ch; + for(int z = 0; z < 256; z++) { + ch = &glyphs[z]; + if((ch->u = gfx_font_special(width, (page << 8) + z)) != 255) { + ch->s = ch->t = 0; + ch->v = ((int)ch->u) * height / width; + if(((page << 8) + z) != CHR_UNK) { + for(int x = 0; x < width; x++) { + for(int y = 0; y < height; y++) { + off = FONT_STRIDE(x, y, z); + data[off+3] = 0; + } + } + } + continue; + } + ch->s = ch->t = 255; + ch->u = ch->v = 0; + for(int x = 0; x < width; x++) { + for(int y = 0; y < height; y++) { + off = FONT_STRIDE(x, y, z); + if(data[off+3]) { + ch->s = x < ch->s ? x : ch->s; + ch->t = y < ch->t ? y : ch->t; + ch->u = x > ch->u ? x : ch->u; + ch->v = y > ch->v ? y : ch->v; + } + } + } + if(ch->s == 255 && ch->t == 255 && ch->u == 0 && ch->v == 0) { + ch->s = ch->t = 0; + } + else { + ch->u += 1; + ch->v += 1; + } + } +} + +int gfx_load_font(font_t *font, const char *path) { + byte *data; + int width, height; + int pages = 0; + font->xglyph = 0; + // if(mono) + // mono->xglyph = 0; + for(int page = 0; page < UNI_MAX_PAGES; page++) { + sprintf(sys.work_buf, "fonts/%s_%04x.png", path, page); + if(!(data = img_load(sys.work_buf, &width, &height, 0, 1))) { + if(!page) { + + return 0; + } + font->textures[page] = 0; + font->sizes[page] = NULL; + // if(mono) { + // mono->textures[page] = 0; + // mono->sizes[page] = NULL; + // } + continue; + } + if(page == 0) { + if(width > 3968 || height > 3968) { + + mem_free(data); + return 0; + } + else if((width % 16) || (height % 16)) { + + mem_free(data); + return 0; + } + font->xsize = width; + font->ysize = height; + font->xglyph = width >> 4; + font->yglyph = height >> 4; + } + else if(font->xsize != width || font->ysize != height) { + + font->textures[page] = 0; + font->sizes[page] = NULL; + // if(mono) { + // mono->textures[page] = 0; + // mono->sizes[page] = NULL; + // } + mem_free(data); + continue; + } + pages += 1; + font->sizes[page] = mem_alloc(256 * sizeof(fchar_t), MEM_FONT); + gfx_calc_font(data, font->sizes[page], font->xglyph, font->yglyph, page); + tex_push(&font->textures[page], data, width, height); + // if(mono) { + // mono->sizes[page] = mem_alloc(256 * sizeof(fchar_t), MEM_FONT); + // memcpy(mono->sizes[page], font->sizes[page], 256 * sizeof(fchar_t)); + // gfx_calc_mono(mono->sizes[page], font->xglyph, font->yglyph); + // mono->textures[page] = font->textures[page]; + // } + logd(LOG_GFX, STR_TEX_ILOADED, sys.work_buf, font->textures[page]); + mem_free(data); + } + // if(mono) { + // mono->xsize = font->xsize; + // mono->ysize = font->ysize; + // mono->xglyph = font->xglyph; + // mono->yglyph = font->yglyph; + // } + return pages; +} + +/* +int gfx_load_font_int(font_t *font, const byte **fdata, const byte **fsizes) { + int pages = 0; + byte *data = mem_alloc(12 * 16 * 18 * 16 * 4, MEM_IMAGE); + font->xglyph = 12; + font->yglyph = 18; + font->xsize = 12 * 16; + font->ysize = 18 * 16; + for(int page = 0; page < UNI_MAX_PAGES; page++) { + sprintf(sys.work_buf, "font_%04x", page); + if(!fdata[page]) { + font->textures[page] = 0; + font->sizes[page] = NULL; + continue; + } + pages += 1; + font->sizes[page] = (fchar_t *)fsizes[page]; + tex_unpack_1bit(data, fdata[page], 12 * 16 * 18 * 16); + tex_push(&font->textures[page], data, 12 * 16, 18 * 16); + // mono->sizes[page] = mem_alloc(256 * sizeof(fchar_t), MEM_FONT); + // memcpy(mono->sizes[page], fsizes[page], 256 * sizeof(fchar_t)); + // gfx_calc_mono(mono->sizes[page], font->xglyph, font->yglyph); + // mono->textures[page] = font->textures[page]; + logd(LOG_GFX, STR_TEX_ILOADED, sys.work_buf, font->textures[page]); + } + mem_free(data); + return pages; +} +*/ + +void gfx_free_font(font_t *font) { + for(int page = 0; (page < UNI_MAX_PAGES) && font->xglyph; page++) { + if(font->sizes[page]) { + mem_free(font->sizes[page]); + tex_delete(&font->textures[page]); + } + // if(mono && mono->sizes[page]) + // mem_free(mono->sizes[page]); + } +} + +/* +void gfx_free_font_int(font_t *font) { + for(int page = 0; page < UNI_MAX_PAGES; page++) { + if(font->sizes[page]) + tex_delete(&font->textures[page]); + // if(mono->sizes[page]) + // mem_free(mono->sizes[page]); + } +} +*/ + +void gfx_aux_colors(const uint *colors) { + memcpy(gdr.aux_colors, colors, sizeof(uint) * 8); +} + +void gfx_back_color(uint color) { + gdr.clear_r = (float)((uint)((color >> 16) & 0xff)) / 255.0f; + gdr.clear_g = (float)((uint)((color >> 8) & 0xff)) / 255.0f; + gdr.clear_b = (float)((uint)(color & 0xff)) / 255.0f; + gdr.clear_a = (float)((uint)((color >> 24) & 0xff)) / 255.0f; +} + +void shd_fnc_tex() { + shd_int("tex", 0); +} + +void shd_fnc_world() { + shd_int("tex", 0); + glUniformBlockBinding(gdr.shader, glGetUniformBlockIndex(gdr.shader, "light_block"), 0); +} + +void gfx_view(camera_t *cam, mat4 mat, float x, float y, float z) { + vec3 pos; + vec3 center; + VEC3_SET(pos, x, y, z); + glm_vec3_add(pos, cam->front, center); + glm_mat4_identity(mat); + glm_lookat(pos, center, cam->up, mat); +} + +void gfx_perspective(camera_t *cam, mat4 mat) { + glm_mat4_identity(mat); + glm_perspective(glm_rad(gdr.fov - (cam->zoom * (gdr.fov - 1.0f))), ((float)wcf.current->frame_x) / ((float)wcf.current->frame_y), gdr.clip_near, gdr.clip_far, mat); +} + +void shd_pass_rect(void *u) { + // shd_sel(gdr.shd_rect); + shd_vec2v("screen", (float)wcf.current->fb_x, (float)wcf.current->fb_y); + glBindVertexArray(gdr.vao_quad); + glActiveTexture(GL_TEXTURE0); +} + +void shd_pass_text(void *u) { + // shd_sel(gdr.shd_text); + shd_vec2v("screen", (float)wcf.current->fb_x, (float)wcf.current->fb_y); + shd_float("time", tmr_ftime()); + glBindVertexArray(gdr.vao_quad); + glActiveTexture(GL_TEXTURE0); +} + +void shd_pass_blit(void *u) { + // shd_sel(gdr.shd_blit); + glBindVertexArray(gdr.vao_quad); + glActiveTexture(GL_TEXTURE0); +} + +void shd_pass_world(world_t *world) { + mat4 mat; + // shd_sel(gdr.shd_world); + shd_float("clip_near", gdr.clip_near); + shd_float("clip_far", gdr.clip_far); + shd_vec2v("screen", (float)wcf.current->frame_x, (float)wcf.current->frame_y); + // shd_vec3v("world_size", (float)(TEMP_LM_SIZE * CHUNK_SIZE), (float)(TEMP_LM_SIZE * CHUNK_SIZE), (float)(TEMP_LM_SIZE * CHUNK_SIZE)); + shd_vec3v("cam_pos", world->camera.pos_x, world->camera.pos_y, world->camera.pos_z); + gfx_view(&world->camera, mat, world->camera.pos_x, world->camera.pos_y, world->camera.pos_z); + shd_mat4("view", mat); + gfx_perspective(&world->camera, mat); + shd_mat4("projection", mat); + /* + glm_mat4_identity(mat); + vec3 v; + VEC3_SET(v, 100.0f, 0.5f, 100.0f); + glm_scale(mat, v); + VEC3_SET(v, 0.0f, -1.0f, 0.0f); + glm_translate(mat, v); + shd_mat4("model", mat); + shd_scolor("specular", 0xffffff); + shd_float("shine", 4.0f); + */ + shd_vec3v("dir_direction", gdr.ambient_x, gdr.ambient_y, gdr.ambient_z); + // shd_vec3v("dir_ambient", 0.1f, 0.1f, 0.2f); + shd_vec3v("dir_ambient", gdr.clear_r, gdr.clear_g, gdr.clear_b); + shd_vec3v("dir_diffuse", gdr.clear_r * 1.25f, gdr.clear_g * 1.25f, gdr.clear_b * 1.25f); + shd_vec3v("dir_specular", gdr.clear_r * 1.5f, gdr.clear_g * 1.5f, gdr.clear_b * 1.5f); + shd_float("light_factor", gdr.light_blend); + shd_float("max_vert_dist", gdr.light_dist_vert); + shd_float("max_cam_dist", gdr.light_dist_cam); + // shd_float("time", tmr_ftime()); + // glBindVertexArray(gdr.vao_box); + // glActiveTexture(GL_TEXTURE1); + // glBindTexture(GL_TEXTURE_2D, gdr.fbtex_lightmap); + shd_int("n_lights", world->s_lights); + glActiveTexture(GL_TEXTURE0); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, world->light_buf); +} + +void shd_pass_vis(world_t *world) { + mat4 mat; + gfx_view(&world->camera, mat, world->camera.pos_x, world->camera.pos_y, world->camera.pos_z); + shd_mat4("view", mat); + gfx_perspective(&world->camera, mat); + shd_mat4("projection", mat); +} + +void shd_pass_grid(world_t *world) { + mat4 mat; + // shd_sel(gdr.shd_vis); + shd_float("clip_near", gdr.clip_near); + shd_float("clip_far", gdr.clip_far); + shd_vec2v("screen", (float)wcf.current->frame_x, (float)wcf.current->frame_y); + gfx_view(&world->camera, mat, world->camera.pos_x, world->camera.pos_y, world->camera.pos_z); + shd_mat4("view", mat); + gfx_perspective(&world->camera, mat); + shd_mat4("projection", mat); + shd_float("time", tmr_ftime()); + // glActiveTexture(GL_TEXTURE0); +} + +byte gfx_init() { + gdr.aniso_max = 1.0f; + // if(gdr.aniso_avail = (GL_API_4_6 || gl_has_ext("GL_EXT_texture_filter_anisotropic") || gl_has_ext("GL_ARB_texture_filter_anisotropic"))) + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &gdr.aniso_max); + tbl_init(&gdr.textures, 256, 64, sizeof(tex_t), MEM_DRAW); + tbl_init(&gdr.buffers, 1024, 16, sizeof(buf_t), MEM_DRAW); + tbl_init(&gdr.shaders, 1, 16, sizeof(shd_t), MEM_DRAW); + tbl_init(&gdr.framebufs, 1, 16, sizeof(fbo_t), MEM_DRAW); + tbl_init(&gdr.drawlists, 1, 16384, sizeof(drawlist_t), MEM_DRAW); + tbl_init(&gdr.materials, 16, 32, sizeof(material_t), MEM_DRAW); + shd_setvars(); + LOAD_SHADER(text, shd_fnc_tex, shd_pass_text); + LOAD_SHADER(rect, shd_fnc_tex, shd_pass_rect); + LOAD_SHADER(blit, shd_fnc_tex, shd_pass_blit); + LOAD_SHADER(world, shd_fnc_world, shd_pass_world); + // LOAD_SHADER(lightmap, NULL, NULL); + LOAD_SHADER(vis, NULL, shd_pass_vis); + LOAD_SHADER(grid, NULL, shd_pass_grid); + sys.font_pages = gfx_load_font(&sys.font, "font"); + buf_init_sys(&gdr.vbo_quad, &gdr.vao_quad, vert_quad, sizeof(vert_quad), 2, 2); + if(!fb_init_sys(&sys.console->fb_gui, &sys.console->fbtex_gui)) + return 0; + tex_gen_fallback(&gdr.tex_fallback, 8); + gfx_aux_colors(aux_colors); + // gfx_back_color(0x000000); + // gfx_back_color(0xff20208f); + gfx_back_color(0xff000000); + // gui_style_default(&sys.style); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // gdr.buf_lightmap = buf_init(1, &gdr.vbo_lightmap, &gdr.vao_lightmap, NULL, 0, 2, 2); + // gdr.fbt_lightmap = fb_init(&gdr.fb_lightmap, &gdr.fbtex_lightmap, NULL, 0, 1); + // gfx_init_world(); + return 1; +} + +void gfx_end() { + tex_clear(); + buf_clear(); + shd_clear(); + fb_clear(); + gfx_free_font(&sys.font); + tex_delete(&gdr.tex_fallback); + buf_delete(&gdr.vbo_quad, &gdr.vao_quad); + // shd_delete(&gdr.shd_rect); + // shd_delete(&gdr.shd_text); + // shd_delete(&gdr.shd_blit); + // shd_clearall(); + // fb_delete(&sys.console->fb_gui, &sys.console->fbtex_gui); + tbl_clear(&gdr.textures); + tbl_clear(&gdr.buffers); + tbl_clear(&gdr.shaders); + tbl_clear(&gdr.framebufs); + tbl_clear(&gdr.drawlists); + tbl_clear(&gdr.materials); +} + +void gfx_blit(uint tex) { + shd_sel(gdr.shd_blit, NULL); + glBindTexture(GL_TEXTURE_2D, tex); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +void gfx_draw_rect(int x, int y, int w, int h, int border, uint tex, uint top, uint bottom, uint b_top, uint b_bottom) { + shd_sel(gdr.shd_rect, NULL); + shd_vec2v("offset", (float)x, (float)y); + shd_vec2v("size", (float)w, (float)h); + shd_bool("use_tex", tex ? 1 : 0); + shd_float("border", (float)border); + shd_color("fill_t", top); + shd_color("fill_b", bottom); + shd_color("border_t", b_top); + shd_color("border_b", b_bottom); + if(tex) { + glBindTexture(GL_TEXTURE_2D, tex); + } + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +void gfx_draw_gui(window_t *win) { + int x1, y1, x2, y2; + gui_t *elem = win->selected; + if(elem && elem->r_dirty && elem->type == GUI_DROPDOWN_HANDLE && !(elem->visible)) { + glScissor(elem->pos_x, (win->fb_y - (elem->pos_y + elem->size_y)) < 0 ? 0 : (win->fb_y - (elem->pos_y + elem->size_y)), elem->size_x, elem->size_y); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + elem->r_dirty = elem->t_dirty = 0; + win->selected = NULL; + for(int z = 0; z < win->n_elems; z++) { + if((win->elems[z].pos_y + win->elems[z].size_y) > elem->pos_y && win->elems[z].pos_y < (elem->pos_y + elem->size_y) && + (win->elems[z].pos_x + win->elems[z].size_x) > elem->pos_x && win->elems[z].pos_x < (elem->pos_x + elem->size_x) && win->elems[z].visible) + win->elems[z].r_dirty = 1; + } + } + for(int z = 0; z < win->n_elems; z++) { + elem = &win->elems[z]; + if(elem->r_dirty || elem->t_dirty) { + if(win->selected == elem && elem->type == GUI_DROPDOWN_HANDLE && elem->visible) + continue; + glScissor(elem->pos_x, (win->fb_y - (elem->pos_y + elem->size_y)) < 0 ? 0 : (win->fb_y - (elem->pos_y + elem->size_y)), elem->size_x, elem->size_y); + // logd("DBG", "%d @ %d %d -> %d %d", elem->id, elem->pos_x, elem->pos_y, elem->pos_x + elem->size_x, elem->pos_y + elem->size_y); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + if(elem->type == GUI_CUSTOM) + elem->func(elem, 0); + glDisable(GL_SCISSOR_TEST); + if(elem->visible && (elem->type != GUI_CUSTOM)) + gfx_draw_rect(elem->pos_x, elem->pos_y, elem->size_x, elem->size_y, elem->border, elem->texture, elem->color_fill_t, elem->color_fill_b, elem->color_brdr_t, elem->color_brdr_b); + if(elem->visible && (elem->type == GUI_SLIDER)) + gfx_draw_rect(elem->pos_x + elem->sel_start - (elem->sel_end / 2), elem->pos_y, elem->sel_end, elem->size_y, elem->sel_drag, elem->texture, elem->margin_x1, elem->margin_y1, elem->margin_x2, elem->margin_y2); + elem->r_dirty = 0; + elem->t_dirty = elem->visible; + // logd("DBG", "@ r"); + } + } + // gfx_pass_text(); + for(int z = 0; z < win->n_elems; z++) { + elem = &win->elems[z]; + if(elem->t_dirty && elem->visible) { + if(win->selected == elem && elem->type == GUI_DROPDOWN_HANDLE) + continue; + x1 = elem->pos_x + ((elem->type != GUI_SLIDER) ? elem->margin_x1 : 0); + y1 = elem->pos_y + ((elem->type != GUI_SLIDER) ? elem->margin_y1 : 0); + x2 = elem->size_x - ((elem->type != GUI_SLIDER) ? (elem->margin_x1 + elem->margin_x2) : 0); + y2 = elem->size_y - ((elem->type != GUI_SLIDER) ? (elem->margin_y1 + elem->margin_y2) : 0); + // if(elem->type == GUI_FIELD) { + glScissor(x1 < 0 ? 0 : x1, (win->fb_y - (y1 + y2)) < 0 ? 0 : (win->fb_y - (y1 + y2)), x2 < 0 ? 0 : x2, y2 < 0 ? 0 : y2); + glEnable(GL_SCISSOR_TEST); + // } + if(elem->type == GUI_CUSTOM) + elem->func(elem, 1); + else + elem->t_dirty = txt_draw(x1 + (elem->xbreak ? 0 : elem->text_x), y1 + elem->text_y, + 0, elem->font_size * elem->font->yglyph, + elem->font_size * (elem->font->yglyph + elem->line_space), x1 + elem->text_x, y1 + elem->text_y, + elem->xbreak ? (elem->pos_x + x2) : 0x7fffffff, 0x7fffffff, elem->color_text, 0x00000000, elem->font, elem->text); + if(elem->type == GUI_FIELD && elem->sel_start >= 0 && elem->sel_end != elem->sel_start) + txt_draw_range(elem->sel_start, elem->sel_end, x1 + (elem->xbreak ? 0 : elem->text_x), y1 + elem->text_y, 0, elem->font_size * elem->font->yglyph, + elem->font_size * elem->font->yglyph, x1 + elem->text_x, y1 + elem->text_y, + elem->xbreak ? (elem->pos_x + x2) : 0x7fffffff, 0x7fffffff, 0x00000000, sys.style.select, elem->font, elem->text); + // logd("DBG", "%d @ %d %d -> %d %d", elem->id, x1, y1, elem->pos_x + x2, elem->pos_y + y2); + // if(elem->type == GUI_FIELD) { + glDisable(GL_SCISSOR_TEST); + // glScissor(0, 0, sys.fb_x, sys.fb_y); + // } + } + } + elem = win->selected; + if(elem && (elem->r_dirty || elem->t_dirty) && elem->type == GUI_DROPDOWN_HANDLE && elem->visible) { + // gfx_pass_rect(); + glScissor(elem->pos_x, (win->fb_y - (elem->pos_y + elem->size_y)) < 0 ? 0 : (win->fb_y - (elem->pos_y + elem->size_y)), elem->size_x, elem->size_y); + glEnable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + gfx_draw_rect(elem->pos_x, elem->pos_y, elem->size_x, elem->size_y, elem->border, elem->texture, elem->color_fill_t, elem->color_fill_b, elem->color_brdr_t, elem->color_brdr_b); + elem->r_dirty = 0; + // gfx_pass_text(); + x1 = elem->pos_x + elem->margin_x1; + y1 = elem->pos_y + elem->margin_y1; + x2 = elem->size_x - (elem->margin_x1 + elem->margin_x2); + y2 = elem->size_y - (elem->margin_y1 + elem->margin_y2); + glScissor(x1 < 0 ? 0 : x1, (win->fb_y - (y1 + y2)) < 0 ? 0 : (win->fb_y - (y1 + y2)), x2 < 0 ? 0 : x2, y2 < 0 ? 0 : y2); + glEnable(GL_SCISSOR_TEST); + elem->t_dirty = txt_draw(x1 + elem->text_x, y1 + elem->text_y, + 0, elem->font_size * elem->font->yglyph, + elem->font_size * (elem->font->yglyph + elem->line_space), x1 + elem->text_x, y1 + elem->text_y, + 0x7fffffff, 0x7fffffff, elem->color_text, 0x00000000, elem->font, elem->text); + glDisable(GL_SCISSOR_TEST); + } +} + +byte gui_inside(gui_t *elem, int x, int y); +gui_t *gui_clicked(window_t *win, int x, int y); + +void gfx_draw_gui_overlay(window_t *win) { + gui_t *elem; + if(win->open) { + elem = win->selected; + if(win->mouse && elem && elem->enabled && elem->visible && (elem->type != GUI_LABEL) && (elem->type != GUI_FIELD) && (elem->type != GUI_DROPDOWN_HANDLE) && (elem->type != GUI_CUSTOM)) { + gfx_draw_rect(elem->pos_x, elem->pos_y, elem->size_x, elem->size_y, 0, 0, sys.style.press_top, sys.style.press_btm, 0x00000000, 0x00000000); + return; + } + else if(elem && elem->enabled && elem->visible && (elem->type == GUI_FIELD)) { + if(elem->value && elem->sel_start >= 0 && elem->sel_end == elem->sel_start && fmodf(tmr_ftime(), 1.0f) < 0.5f) { + int x1 = elem->pos_x + elem->margin_x1; + int y1 = elem->pos_y + elem->margin_y1; + int x2 = elem->size_x - (elem->margin_x1 + elem->margin_x2); + int y2 = elem->size_y - (elem->margin_y1 + elem->margin_y2); + glScissor(x1 < 0 ? 0 : x1, (win->fb_y - (y1 + y2)) < 0 ? 0 : (win->fb_y - (y1 + y2)), x2 < 0 ? 0 : x2, y2 < 0 ? 0 : y2); + glEnable(GL_SCISSOR_TEST); + gfx_draw_rect(elem->min, elem->max, elem->font_size, elem->font->yglyph * elem->font_size, 0, 0, sys.style.cursor, sys.style.cursor, 0x00000000, 0x00000000); + glDisable(GL_SCISSOR_TEST); + } + } + elem = gui_clicked(win, win->mouse_x, win->mouse_y); + if(elem && elem->enabled && elem->visible && (elem->type != GUI_LABEL) && /* ( */ (elem->type != GUI_FIELD) && (elem->type != GUI_CUSTOM) /* || (elem != gui.selected)) */ ) { + if(elem->type == GUI_DROPDOWN_HANDLE) { + int m = ((win->mouse_y - (elem->pos_y + elem->margin_y1)) * elem->max / (elem->size_y - (elem->margin_y1 + elem->margin_y2))); + // if((sys.mouse_y - elem->pos_y) < (elem->size_y - elem->margin_y2)) + gfx_draw_rect(elem->pos_x + elem->margin_x1, elem->pos_y + elem->margin_y1 + CLAMP_VALUE(m, 0, elem->max - 1) * ((elem->size_y - (elem->margin_y1 + elem->margin_y2)) / elem->max), + elem->size_x - (elem->margin_x1 + elem->margin_x2), + (elem->size_y - (elem->margin_y1 + elem->margin_y2)) / elem->max, 0, 0, sys.style.hover_top, sys.style.hover_btm, 0x00000000, 0x00000000); + } + else { + gfx_draw_rect(elem->pos_x, elem->pos_y, elem->size_x, elem->size_y, 0, 0, sys.style.hover_top, sys.style.hover_btm, 0x00000000, 0x00000000); + } + } + } +} + +void gfx_draw_con_overlay(window_t *win) { + if(sys.hud_size) { + conmsg_t *con; + ulong fade = 1000ULL * (ulong)(sys.hud_fadeout); + font_t *font = &sys.font; + int y = sys.hud_bottom ? (win->frame_y - font->yglyph) : 0; + int pos = sys.hud_pos; + for(int z = 0; z < sys.hud_size; z++) { + if(--pos < 0) + pos = sys.hud_size - 1; + con = &sys.hud_msgs[pos]; + if(con->message && (con->window == win)) { + if((sys.tmr_current - con->time) <= fade) { + txt_draw_range(0, con->length, win->offset_x, win->offset_y + y, 0, font->yglyph, font->yglyph, + win->offset_x, win->offset_y + y, + win->offset_x + win->frame_x, win->offset_y + win->frame_y, + 0xffffffff, (uint)sys.hud_opacity << 24, font, con->message); + y += sys.hud_bottom ? -(font->yglyph) : font->yglyph; + } + else { + con->message = NULL; + } + } + } + } +} + +material_t *gfx_add_material(uint texture, float density, uint spec_color, float shine, shd_t *shader) { + material_t *mat = tbl_push(&gdr.materials); + mat->texture = texture; + mat->density = density; + mat->spec_color = spec_color; + mat->shine = shine; + mat->shader = shader; + return mat; +} + +void gfx_clear_materials() { + tbl_clear(&gdr.materials); +} diff --git a/screenshots/screen_01-10-2023_14-24-33.png b/screenshots/screen_01-10-2023_14-24-33.png new file mode 100644 index 0000000..fa5f1dd Binary files /dev/null and b/screenshots/screen_01-10-2023_14-24-33.png differ diff --git a/screenshots/screen_01-10-2023_14-58-06.png b/screenshots/screen_01-10-2023_14-58-06.png new file mode 100644 index 0000000..2674ac4 Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-06.png differ diff --git a/screenshots/screen_01-10-2023_14-58-08.png b/screenshots/screen_01-10-2023_14-58-08.png new file mode 100644 index 0000000..46e0f4d Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-08.png differ diff --git a/screenshots/screen_01-10-2023_14-58-09.png b/screenshots/screen_01-10-2023_14-58-09.png new file mode 100644 index 0000000..289d583 Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-09.png differ diff --git a/screenshots/screen_01-10-2023_14-58-14.png b/screenshots/screen_01-10-2023_14-58-14.png new file mode 100644 index 0000000..2b27b20 Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-14.png differ diff --git a/screenshots/screen_01-10-2023_14-58-15.png b/screenshots/screen_01-10-2023_14-58-15.png new file mode 100644 index 0000000..a61643e Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-15.png differ diff --git a/screenshots/screen_01-10-2023_14-58-17.png b/screenshots/screen_01-10-2023_14-58-17.png new file mode 100644 index 0000000..e92249d Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-17.png differ diff --git a/screenshots/screen_01-10-2023_14-58-18.png b/screenshots/screen_01-10-2023_14-58-18.png new file mode 100644 index 0000000..65e2eaf Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-18.png differ diff --git a/screenshots/screen_01-10-2023_14-58-21.png b/screenshots/screen_01-10-2023_14-58-21.png new file mode 100644 index 0000000..4a36de7 Binary files /dev/null and b/screenshots/screen_01-10-2023_14-58-21.png differ diff --git a/screenshots/screen_02-09-2024_08-36-24.png b/screenshots/screen_02-09-2024_08-36-24.png new file mode 100644 index 0000000..5333cbb Binary files /dev/null and b/screenshots/screen_02-09-2024_08-36-24.png differ diff --git a/screenshots/screen_02-09-2024_08-36-36.png b/screenshots/screen_02-09-2024_08-36-36.png new file mode 100644 index 0000000..bf652f9 Binary files /dev/null and b/screenshots/screen_02-09-2024_08-36-36.png differ diff --git a/screenshots/screen_02-09-2024_14-34-49.png b/screenshots/screen_02-09-2024_14-34-49.png new file mode 100644 index 0000000..f251c61 Binary files /dev/null and b/screenshots/screen_02-09-2024_14-34-49.png differ diff --git a/screenshots/screen_02-09-2024_15-08-05.png b/screenshots/screen_02-09-2024_15-08-05.png new file mode 100644 index 0000000..b27f4cd Binary files /dev/null and b/screenshots/screen_02-09-2024_15-08-05.png differ diff --git a/screenshots/screen_02-09-2024_15-08-22.png b/screenshots/screen_02-09-2024_15-08-22.png new file mode 100644 index 0000000..7fba791 Binary files /dev/null and b/screenshots/screen_02-09-2024_15-08-22.png differ diff --git a/screenshots/screen_02-09-2024_15-08-56.png b/screenshots/screen_02-09-2024_15-08-56.png new file mode 100644 index 0000000..c4294b0 Binary files /dev/null and b/screenshots/screen_02-09-2024_15-08-56.png differ diff --git a/screenshots/screen_02-09-2024_15-12-27.png b/screenshots/screen_02-09-2024_15-12-27.png new file mode 100644 index 0000000..9deedd8 Binary files /dev/null and b/screenshots/screen_02-09-2024_15-12-27.png differ diff --git a/screenshots/screen_02-09-2024_15-12-59.png b/screenshots/screen_02-09-2024_15-12-59.png new file mode 100644 index 0000000..ebcfb05 Binary files /dev/null and b/screenshots/screen_02-09-2024_15-12-59.png differ diff --git a/screenshots/screen_02-09-2024_15-44-28.png b/screenshots/screen_02-09-2024_15-44-28.png new file mode 100644 index 0000000..866bd1e Binary files /dev/null and b/screenshots/screen_02-09-2024_15-44-28.png differ diff --git a/screenshots/screen_02-09-2024_15-48-31.png b/screenshots/screen_02-09-2024_15-48-31.png new file mode 100644 index 0000000..fa6ca6b Binary files /dev/null and b/screenshots/screen_02-09-2024_15-48-31.png differ diff --git a/screenshots/screen_02-09-2024_16-21-11.png b/screenshots/screen_02-09-2024_16-21-11.png new file mode 100644 index 0000000..069062c Binary files /dev/null and b/screenshots/screen_02-09-2024_16-21-11.png differ diff --git a/screenshots/screen_02-10-2023_00-22-21.png b/screenshots/screen_02-10-2023_00-22-21.png new file mode 100644 index 0000000..2d4e4e2 Binary files /dev/null and b/screenshots/screen_02-10-2023_00-22-21.png differ diff --git a/screenshots/screen_02-10-2023_00-22-23.png b/screenshots/screen_02-10-2023_00-22-23.png new file mode 100644 index 0000000..6d10cd2 Binary files /dev/null and b/screenshots/screen_02-10-2023_00-22-23.png differ diff --git a/screenshots/screen_03-09-2024_13-16-16.png b/screenshots/screen_03-09-2024_13-16-16.png new file mode 100644 index 0000000..ef65a32 Binary files /dev/null and b/screenshots/screen_03-09-2024_13-16-16.png differ diff --git a/screenshots/screen_03-09-2024_13-16-30.png b/screenshots/screen_03-09-2024_13-16-30.png new file mode 100644 index 0000000..bf3ea8b Binary files /dev/null and b/screenshots/screen_03-09-2024_13-16-30.png differ diff --git a/screenshots/screen_03-12-2023_00-26-26.png b/screenshots/screen_03-12-2023_00-26-26.png new file mode 100644 index 0000000..d35c4b8 Binary files /dev/null and b/screenshots/screen_03-12-2023_00-26-26.png differ diff --git a/screenshots/screen_04-10-2023_13-04-37.png b/screenshots/screen_04-10-2023_13-04-37.png new file mode 100644 index 0000000..38f0283 Binary files /dev/null and b/screenshots/screen_04-10-2023_13-04-37.png differ diff --git a/screenshots/screen_04-10-2023_13-04-39.png b/screenshots/screen_04-10-2023_13-04-39.png new file mode 100644 index 0000000..4f785a1 Binary files /dev/null and b/screenshots/screen_04-10-2023_13-04-39.png differ diff --git a/screenshots/screen_04-10-2023_13-04-40.png b/screenshots/screen_04-10-2023_13-04-40.png new file mode 100644 index 0000000..7902f58 Binary files /dev/null and b/screenshots/screen_04-10-2023_13-04-40.png differ diff --git a/screenshots/screen_05-09-2024_13-13-37.png b/screenshots/screen_05-09-2024_13-13-37.png new file mode 100644 index 0000000..c6f5820 Binary files /dev/null and b/screenshots/screen_05-09-2024_13-13-37.png differ diff --git a/screenshots/screen_05-09-2024_23-04-38.png b/screenshots/screen_05-09-2024_23-04-38.png new file mode 100644 index 0000000..6b3cc58 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-04-38.png differ diff --git a/screenshots/screen_05-09-2024_23-04-42.png b/screenshots/screen_05-09-2024_23-04-42.png new file mode 100644 index 0000000..0871e14 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-04-42.png differ diff --git a/screenshots/screen_05-09-2024_23-04-43.png b/screenshots/screen_05-09-2024_23-04-43.png new file mode 100644 index 0000000..fadc9f2 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-04-43.png differ diff --git a/screenshots/screen_05-09-2024_23-47-56.png b/screenshots/screen_05-09-2024_23-47-56.png new file mode 100644 index 0000000..f9ab147 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-47-56.png differ diff --git a/screenshots/screen_05-09-2024_23-47-57.png b/screenshots/screen_05-09-2024_23-47-57.png new file mode 100644 index 0000000..3a170dd Binary files /dev/null and b/screenshots/screen_05-09-2024_23-47-57.png differ diff --git a/screenshots/screen_05-09-2024_23-50-24.png b/screenshots/screen_05-09-2024_23-50-24.png new file mode 100644 index 0000000..00d7ccb Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-24.png differ diff --git a/screenshots/screen_05-09-2024_23-50-25.png b/screenshots/screen_05-09-2024_23-50-25.png new file mode 100644 index 0000000..aeff9ea Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-25.png differ diff --git a/screenshots/screen_05-09-2024_23-50-27.png b/screenshots/screen_05-09-2024_23-50-27.png new file mode 100644 index 0000000..a6903f4 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-27.png differ diff --git a/screenshots/screen_05-09-2024_23-50-30.png b/screenshots/screen_05-09-2024_23-50-30.png new file mode 100644 index 0000000..485bff8 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-30.png differ diff --git a/screenshots/screen_05-09-2024_23-50-34.png b/screenshots/screen_05-09-2024_23-50-34.png new file mode 100644 index 0000000..f713cc2 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-34.png differ diff --git a/screenshots/screen_05-09-2024_23-50-37.png b/screenshots/screen_05-09-2024_23-50-37.png new file mode 100644 index 0000000..d33a72a Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-37.png differ diff --git a/screenshots/screen_05-09-2024_23-50-39.png b/screenshots/screen_05-09-2024_23-50-39.png new file mode 100644 index 0000000..cb9cbe7 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-39.png differ diff --git a/screenshots/screen_05-09-2024_23-50-43.png b/screenshots/screen_05-09-2024_23-50-43.png new file mode 100644 index 0000000..5297450 Binary files /dev/null and b/screenshots/screen_05-09-2024_23-50-43.png differ diff --git a/screenshots/screen_06-10-2023_01-20-26.png b/screenshots/screen_06-10-2023_01-20-26.png new file mode 100644 index 0000000..3a40e80 Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-26.png differ diff --git a/screenshots/screen_06-10-2023_01-20-28.png b/screenshots/screen_06-10-2023_01-20-28.png new file mode 100644 index 0000000..fb29f4e Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-28.png differ diff --git a/screenshots/screen_06-10-2023_01-20-33.png b/screenshots/screen_06-10-2023_01-20-33.png new file mode 100644 index 0000000..7cde592 Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-33.png differ diff --git a/screenshots/screen_06-10-2023_01-20-34.png b/screenshots/screen_06-10-2023_01-20-34.png new file mode 100644 index 0000000..38c8cf1 Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-34.png differ diff --git a/screenshots/screen_06-10-2023_01-20-34_1.png b/screenshots/screen_06-10-2023_01-20-34_1.png new file mode 100644 index 0000000..785a02c Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-34_1.png differ diff --git a/screenshots/screen_06-10-2023_01-20-34_2.png b/screenshots/screen_06-10-2023_01-20-34_2.png new file mode 100644 index 0000000..e0a30c8 Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-34_2.png differ diff --git a/screenshots/screen_06-10-2023_01-20-35.png b/screenshots/screen_06-10-2023_01-20-35.png new file mode 100644 index 0000000..91806f7 Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-35.png differ diff --git a/screenshots/screen_06-10-2023_01-20-35_1.png b/screenshots/screen_06-10-2023_01-20-35_1.png new file mode 100644 index 0000000..b3b293d Binary files /dev/null and b/screenshots/screen_06-10-2023_01-20-35_1.png differ diff --git a/screenshots/screen_06-10-2023_03-25-06.png b/screenshots/screen_06-10-2023_03-25-06.png new file mode 100644 index 0000000..7fe2392 Binary files /dev/null and b/screenshots/screen_06-10-2023_03-25-06.png differ diff --git a/screenshots/screen_06-10-2023_03-25-13.png b/screenshots/screen_06-10-2023_03-25-13.png new file mode 100644 index 0000000..96710ec Binary files /dev/null and b/screenshots/screen_06-10-2023_03-25-13.png differ diff --git a/screenshots/screen_06-10-2023_11-24-50.png b/screenshots/screen_06-10-2023_11-24-50.png new file mode 100644 index 0000000..864350c Binary files /dev/null and b/screenshots/screen_06-10-2023_11-24-50.png differ diff --git a/screenshots/screen_06-10-2023_11-24-51.png b/screenshots/screen_06-10-2023_11-24-51.png new file mode 100644 index 0000000..067bd68 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-24-51.png differ diff --git a/screenshots/screen_06-10-2023_11-24-51_1.png b/screenshots/screen_06-10-2023_11-24-51_1.png new file mode 100644 index 0000000..f58124a Binary files /dev/null and b/screenshots/screen_06-10-2023_11-24-51_1.png differ diff --git a/screenshots/screen_06-10-2023_11-41-19.png b/screenshots/screen_06-10-2023_11-41-19.png new file mode 100644 index 0000000..07ff054 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-41-19.png differ diff --git a/screenshots/screen_06-10-2023_11-41-20.png b/screenshots/screen_06-10-2023_11-41-20.png new file mode 100644 index 0000000..5a05cd6 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-41-20.png differ diff --git a/screenshots/screen_06-10-2023_11-54-57.png b/screenshots/screen_06-10-2023_11-54-57.png new file mode 100644 index 0000000..6accd65 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-54-57.png differ diff --git a/screenshots/screen_06-10-2023_11-55-00.png b/screenshots/screen_06-10-2023_11-55-00.png new file mode 100644 index 0000000..3b568c8 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-55-00.png differ diff --git a/screenshots/screen_06-10-2023_11-55-02.png b/screenshots/screen_06-10-2023_11-55-02.png new file mode 100644 index 0000000..3f29f98 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-55-02.png differ diff --git a/screenshots/screen_06-10-2023_11-55-03.png b/screenshots/screen_06-10-2023_11-55-03.png new file mode 100644 index 0000000..e8419e4 Binary files /dev/null and b/screenshots/screen_06-10-2023_11-55-03.png differ diff --git a/screenshots/screen_06-10-2023_11-55-04.png b/screenshots/screen_06-10-2023_11-55-04.png new file mode 100644 index 0000000..d9ac23e Binary files /dev/null and b/screenshots/screen_06-10-2023_11-55-04.png differ diff --git a/screenshots/screen_06-10-2023_11-55-05.png b/screenshots/screen_06-10-2023_11-55-05.png new file mode 100644 index 0000000..f3c6e4d Binary files /dev/null and b/screenshots/screen_06-10-2023_11-55-05.png differ diff --git a/screenshots/screen_06-10-2023_15-02-50.png b/screenshots/screen_06-10-2023_15-02-50.png new file mode 100644 index 0000000..bdd716d Binary files /dev/null and b/screenshots/screen_06-10-2023_15-02-50.png differ diff --git a/screenshots/screen_06-10-2023_15-02-55.png b/screenshots/screen_06-10-2023_15-02-55.png new file mode 100644 index 0000000..84b006d Binary files /dev/null and b/screenshots/screen_06-10-2023_15-02-55.png differ diff --git a/screenshots/screen_06-10-2023_15-02-57.png b/screenshots/screen_06-10-2023_15-02-57.png new file mode 100644 index 0000000..b797a25 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-02-57.png differ diff --git a/screenshots/screen_06-10-2023_15-02-59.png b/screenshots/screen_06-10-2023_15-02-59.png new file mode 100644 index 0000000..2041602 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-02-59.png differ diff --git a/screenshots/screen_06-10-2023_15-03-00.png b/screenshots/screen_06-10-2023_15-03-00.png new file mode 100644 index 0000000..3c1b31f Binary files /dev/null and b/screenshots/screen_06-10-2023_15-03-00.png differ diff --git a/screenshots/screen_06-10-2023_15-03-03.png b/screenshots/screen_06-10-2023_15-03-03.png new file mode 100644 index 0000000..50261fb Binary files /dev/null and b/screenshots/screen_06-10-2023_15-03-03.png differ diff --git a/screenshots/screen_06-10-2023_15-05-10.png b/screenshots/screen_06-10-2023_15-05-10.png new file mode 100644 index 0000000..eeb5693 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-05-10.png differ diff --git a/screenshots/screen_06-10-2023_15-05-15.png b/screenshots/screen_06-10-2023_15-05-15.png new file mode 100644 index 0000000..1020914 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-05-15.png differ diff --git a/screenshots/screen_06-10-2023_15-05-17.png b/screenshots/screen_06-10-2023_15-05-17.png new file mode 100644 index 0000000..8da5a31 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-05-17.png differ diff --git a/screenshots/screen_06-10-2023_15-05-52.png b/screenshots/screen_06-10-2023_15-05-52.png new file mode 100644 index 0000000..a37c251 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-05-52.png differ diff --git a/screenshots/screen_06-10-2023_15-08-23.png b/screenshots/screen_06-10-2023_15-08-23.png new file mode 100644 index 0000000..7d30dcb Binary files /dev/null and b/screenshots/screen_06-10-2023_15-08-23.png differ diff --git a/screenshots/screen_06-10-2023_15-08-24.png b/screenshots/screen_06-10-2023_15-08-24.png new file mode 100644 index 0000000..4897381 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-08-24.png differ diff --git a/screenshots/screen_06-10-2023_15-10-06.png b/screenshots/screen_06-10-2023_15-10-06.png new file mode 100644 index 0000000..827f2fd Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-06.png differ diff --git a/screenshots/screen_06-10-2023_15-10-07.png b/screenshots/screen_06-10-2023_15-10-07.png new file mode 100644 index 0000000..11a72fb Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-07.png differ diff --git a/screenshots/screen_06-10-2023_15-10-07_1.png b/screenshots/screen_06-10-2023_15-10-07_1.png new file mode 100644 index 0000000..a61bcdd Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-07_1.png differ diff --git a/screenshots/screen_06-10-2023_15-10-08.png b/screenshots/screen_06-10-2023_15-10-08.png new file mode 100644 index 0000000..062331c Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-08.png differ diff --git a/screenshots/screen_06-10-2023_15-10-12.png b/screenshots/screen_06-10-2023_15-10-12.png new file mode 100644 index 0000000..10ae499 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-12.png differ diff --git a/screenshots/screen_06-10-2023_15-10-13.png b/screenshots/screen_06-10-2023_15-10-13.png new file mode 100644 index 0000000..5e9c3e4 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-13.png differ diff --git a/screenshots/screen_06-10-2023_15-10-13_1.png b/screenshots/screen_06-10-2023_15-10-13_1.png new file mode 100644 index 0000000..970bf26 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-13_1.png differ diff --git a/screenshots/screen_06-10-2023_15-10-14.png b/screenshots/screen_06-10-2023_15-10-14.png new file mode 100644 index 0000000..1a0b429 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-14.png differ diff --git a/screenshots/screen_06-10-2023_15-10-23.png b/screenshots/screen_06-10-2023_15-10-23.png new file mode 100644 index 0000000..adf6946 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-23.png differ diff --git a/screenshots/screen_06-10-2023_15-10-24.png b/screenshots/screen_06-10-2023_15-10-24.png new file mode 100644 index 0000000..6b30370 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-24.png differ diff --git a/screenshots/screen_06-10-2023_15-10-25.png b/screenshots/screen_06-10-2023_15-10-25.png new file mode 100644 index 0000000..cc9b5b9 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-25.png differ diff --git a/screenshots/screen_06-10-2023_15-10-26.png b/screenshots/screen_06-10-2023_15-10-26.png new file mode 100644 index 0000000..21c6ad7 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-26.png differ diff --git a/screenshots/screen_06-10-2023_15-10-26_1.png b/screenshots/screen_06-10-2023_15-10-26_1.png new file mode 100644 index 0000000..bb8a3be Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-26_1.png differ diff --git a/screenshots/screen_06-10-2023_15-10-49.png b/screenshots/screen_06-10-2023_15-10-49.png new file mode 100644 index 0000000..ecf2df5 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-49.png differ diff --git a/screenshots/screen_06-10-2023_15-10-50.png b/screenshots/screen_06-10-2023_15-10-50.png new file mode 100644 index 0000000..60daf8f Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-50.png differ diff --git a/screenshots/screen_06-10-2023_15-10-51.png b/screenshots/screen_06-10-2023_15-10-51.png new file mode 100644 index 0000000..5a918ec Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-51.png differ diff --git a/screenshots/screen_06-10-2023_15-10-51_1.png b/screenshots/screen_06-10-2023_15-10-51_1.png new file mode 100644 index 0000000..545d172 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-51_1.png differ diff --git a/screenshots/screen_06-10-2023_15-10-52.png b/screenshots/screen_06-10-2023_15-10-52.png new file mode 100644 index 0000000..b343cfb Binary files /dev/null and b/screenshots/screen_06-10-2023_15-10-52.png differ diff --git a/screenshots/screen_06-10-2023_15-11-02.png b/screenshots/screen_06-10-2023_15-11-02.png new file mode 100644 index 0000000..7e67501 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-11-02.png differ diff --git a/screenshots/screen_06-10-2023_15-16-44.png b/screenshots/screen_06-10-2023_15-16-44.png new file mode 100644 index 0000000..4693c89 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-16-44.png differ diff --git a/screenshots/screen_06-10-2023_15-17-56.png b/screenshots/screen_06-10-2023_15-17-56.png new file mode 100644 index 0000000..5a78c5e Binary files /dev/null and b/screenshots/screen_06-10-2023_15-17-56.png differ diff --git a/screenshots/screen_06-10-2023_15-18-06.png b/screenshots/screen_06-10-2023_15-18-06.png new file mode 100644 index 0000000..da08778 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-18-06.png differ diff --git a/screenshots/screen_06-10-2023_15-18-09.png b/screenshots/screen_06-10-2023_15-18-09.png new file mode 100644 index 0000000..e2fc8bd Binary files /dev/null and b/screenshots/screen_06-10-2023_15-18-09.png differ diff --git a/screenshots/screen_06-10-2023_15-18-15.png b/screenshots/screen_06-10-2023_15-18-15.png new file mode 100644 index 0000000..2f4d49a Binary files /dev/null and b/screenshots/screen_06-10-2023_15-18-15.png differ diff --git a/screenshots/screen_06-10-2023_15-18-20.png b/screenshots/screen_06-10-2023_15-18-20.png new file mode 100644 index 0000000..1ee60c6 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-18-20.png differ diff --git a/screenshots/screen_06-10-2023_15-22-57.png b/screenshots/screen_06-10-2023_15-22-57.png new file mode 100644 index 0000000..840756d Binary files /dev/null and b/screenshots/screen_06-10-2023_15-22-57.png differ diff --git a/screenshots/screen_06-10-2023_15-22-57_1.png b/screenshots/screen_06-10-2023_15-22-57_1.png new file mode 100644 index 0000000..238120f Binary files /dev/null and b/screenshots/screen_06-10-2023_15-22-57_1.png differ diff --git a/screenshots/screen_06-10-2023_15-22-58.png b/screenshots/screen_06-10-2023_15-22-58.png new file mode 100644 index 0000000..a84d6bf Binary files /dev/null and b/screenshots/screen_06-10-2023_15-22-58.png differ diff --git a/screenshots/screen_06-10-2023_15-22-58_1.png b/screenshots/screen_06-10-2023_15-22-58_1.png new file mode 100644 index 0000000..52c89b0 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-22-58_1.png differ diff --git a/screenshots/screen_06-10-2023_15-24-18.png b/screenshots/screen_06-10-2023_15-24-18.png new file mode 100644 index 0000000..51dc422 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-18.png differ diff --git a/screenshots/screen_06-10-2023_15-24-19.png b/screenshots/screen_06-10-2023_15-24-19.png new file mode 100644 index 0000000..87f9a77 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-19.png differ diff --git a/screenshots/screen_06-10-2023_15-24-19_1.png b/screenshots/screen_06-10-2023_15-24-19_1.png new file mode 100644 index 0000000..97eb82e Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-19_1.png differ diff --git a/screenshots/screen_06-10-2023_15-24-20.png b/screenshots/screen_06-10-2023_15-24-20.png new file mode 100644 index 0000000..b72332b Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-20.png differ diff --git a/screenshots/screen_06-10-2023_15-24-23.png b/screenshots/screen_06-10-2023_15-24-23.png new file mode 100644 index 0000000..d7fddf8 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-23.png differ diff --git a/screenshots/screen_06-10-2023_15-24-30.png b/screenshots/screen_06-10-2023_15-24-30.png new file mode 100644 index 0000000..5bf129c Binary files /dev/null and b/screenshots/screen_06-10-2023_15-24-30.png differ diff --git a/screenshots/screen_06-10-2023_15-30-18.png b/screenshots/screen_06-10-2023_15-30-18.png new file mode 100644 index 0000000..6851bf8 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-18.png differ diff --git a/screenshots/screen_06-10-2023_15-30-19.png b/screenshots/screen_06-10-2023_15-30-19.png new file mode 100644 index 0000000..4a265f7 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-19.png differ diff --git a/screenshots/screen_06-10-2023_15-30-20.png b/screenshots/screen_06-10-2023_15-30-20.png new file mode 100644 index 0000000..af5c14d Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-20.png differ diff --git a/screenshots/screen_06-10-2023_15-30-21.png b/screenshots/screen_06-10-2023_15-30-21.png new file mode 100644 index 0000000..bc28ac7 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-21.png differ diff --git a/screenshots/screen_06-10-2023_15-30-22.png b/screenshots/screen_06-10-2023_15-30-22.png new file mode 100644 index 0000000..0527f43 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-22.png differ diff --git a/screenshots/screen_06-10-2023_15-30-22_1.png b/screenshots/screen_06-10-2023_15-30-22_1.png new file mode 100644 index 0000000..2726ee6 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-22_1.png differ diff --git a/screenshots/screen_06-10-2023_15-30-23.png b/screenshots/screen_06-10-2023_15-30-23.png new file mode 100644 index 0000000..7ff44ca Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-23.png differ diff --git a/screenshots/screen_06-10-2023_15-30-24.png b/screenshots/screen_06-10-2023_15-30-24.png new file mode 100644 index 0000000..7f2c570 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-30-24.png differ diff --git a/screenshots/screen_06-10-2023_15-31-08.png b/screenshots/screen_06-10-2023_15-31-08.png new file mode 100644 index 0000000..219c159 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-31-08.png differ diff --git a/screenshots/screen_06-10-2023_15-31-48.png b/screenshots/screen_06-10-2023_15-31-48.png new file mode 100644 index 0000000..096b669 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-31-48.png differ diff --git a/screenshots/screen_06-10-2023_15-31-51.png b/screenshots/screen_06-10-2023_15-31-51.png new file mode 100644 index 0000000..0ddf0c0 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-31-51.png differ diff --git a/screenshots/screen_06-10-2023_15-31-52.png b/screenshots/screen_06-10-2023_15-31-52.png new file mode 100644 index 0000000..3cd2d94 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-31-52.png differ diff --git a/screenshots/screen_06-10-2023_15-32-01.png b/screenshots/screen_06-10-2023_15-32-01.png new file mode 100644 index 0000000..33e0992 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-32-01.png differ diff --git a/screenshots/screen_06-10-2023_15-38-17.png b/screenshots/screen_06-10-2023_15-38-17.png new file mode 100644 index 0000000..baf6618 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-38-17.png differ diff --git a/screenshots/screen_06-10-2023_15-40-05.png b/screenshots/screen_06-10-2023_15-40-05.png new file mode 100644 index 0000000..4c91090 Binary files /dev/null and b/screenshots/screen_06-10-2023_15-40-05.png differ diff --git a/screenshots/screen_07-10-2023_11-45-34.png b/screenshots/screen_07-10-2023_11-45-34.png new file mode 100644 index 0000000..519c7c4 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-34.png differ diff --git a/screenshots/screen_07-10-2023_11-45-36.png b/screenshots/screen_07-10-2023_11-45-36.png new file mode 100644 index 0000000..3a4ff00 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-36.png differ diff --git a/screenshots/screen_07-10-2023_11-45-41.png b/screenshots/screen_07-10-2023_11-45-41.png new file mode 100644 index 0000000..20cac56 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-41.png differ diff --git a/screenshots/screen_07-10-2023_11-45-48.png b/screenshots/screen_07-10-2023_11-45-48.png new file mode 100644 index 0000000..9ae819c Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-48.png differ diff --git a/screenshots/screen_07-10-2023_11-45-49.png b/screenshots/screen_07-10-2023_11-45-49.png new file mode 100644 index 0000000..4334445 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-49.png differ diff --git a/screenshots/screen_07-10-2023_11-45-51.png b/screenshots/screen_07-10-2023_11-45-51.png new file mode 100644 index 0000000..342cb81 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-45-51.png differ diff --git a/screenshots/screen_07-10-2023_11-46-08.png b/screenshots/screen_07-10-2023_11-46-08.png new file mode 100644 index 0000000..7ede082 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-46-08.png differ diff --git a/screenshots/screen_07-10-2023_11-46-10.png b/screenshots/screen_07-10-2023_11-46-10.png new file mode 100644 index 0000000..6cfc746 Binary files /dev/null and b/screenshots/screen_07-10-2023_11-46-10.png differ diff --git a/screenshots/screen_07-10-2023_11-51-18.png b/screenshots/screen_07-10-2023_11-51-18.png new file mode 100644 index 0000000..df011ae Binary files /dev/null and b/screenshots/screen_07-10-2023_11-51-18.png differ diff --git a/screenshots/screen_08-02-2024_19-31-02.png b/screenshots/screen_08-02-2024_19-31-02.png new file mode 100644 index 0000000..cd08a04 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-02.png differ diff --git a/screenshots/screen_08-02-2024_19-31-04.png b/screenshots/screen_08-02-2024_19-31-04.png new file mode 100644 index 0000000..2a6758d Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-04.png differ diff --git a/screenshots/screen_08-02-2024_19-31-12.png b/screenshots/screen_08-02-2024_19-31-12.png new file mode 100644 index 0000000..fdefb7f Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-12.png differ diff --git a/screenshots/screen_08-02-2024_19-31-12_1.png b/screenshots/screen_08-02-2024_19-31-12_1.png new file mode 100644 index 0000000..726ce4c Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-12_1.png differ diff --git a/screenshots/screen_08-02-2024_19-31-14.png b/screenshots/screen_08-02-2024_19-31-14.png new file mode 100644 index 0000000..bb1b355 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-14.png differ diff --git a/screenshots/screen_08-02-2024_19-31-28.png b/screenshots/screen_08-02-2024_19-31-28.png new file mode 100644 index 0000000..2eb498e Binary files /dev/null and b/screenshots/screen_08-02-2024_19-31-28.png differ diff --git a/screenshots/screen_08-02-2024_19-34-26.png b/screenshots/screen_08-02-2024_19-34-26.png new file mode 100644 index 0000000..bef635e Binary files /dev/null and b/screenshots/screen_08-02-2024_19-34-26.png differ diff --git a/screenshots/screen_08-02-2024_19-38-33.png b/screenshots/screen_08-02-2024_19-38-33.png new file mode 100644 index 0000000..4409067 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-38-33.png differ diff --git a/screenshots/screen_08-02-2024_19-38-35.png b/screenshots/screen_08-02-2024_19-38-35.png new file mode 100644 index 0000000..601805a Binary files /dev/null and b/screenshots/screen_08-02-2024_19-38-35.png differ diff --git a/screenshots/screen_08-02-2024_19-38-36.png b/screenshots/screen_08-02-2024_19-38-36.png new file mode 100644 index 0000000..606d41d Binary files /dev/null and b/screenshots/screen_08-02-2024_19-38-36.png differ diff --git a/screenshots/screen_08-02-2024_19-38-51.png b/screenshots/screen_08-02-2024_19-38-51.png new file mode 100644 index 0000000..67c4471 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-38-51.png differ diff --git a/screenshots/screen_08-02-2024_19-39-36.png b/screenshots/screen_08-02-2024_19-39-36.png new file mode 100644 index 0000000..7f47620 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-39-36.png differ diff --git a/screenshots/screen_08-02-2024_19-42-19.png b/screenshots/screen_08-02-2024_19-42-19.png new file mode 100644 index 0000000..aa1ac40 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-42-19.png differ diff --git a/screenshots/screen_08-02-2024_19-43-41.png b/screenshots/screen_08-02-2024_19-43-41.png new file mode 100644 index 0000000..9fd59c1 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-43-41.png differ diff --git a/screenshots/screen_08-02-2024_19-53-02.png b/screenshots/screen_08-02-2024_19-53-02.png new file mode 100644 index 0000000..c383502 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-53-02.png differ diff --git a/screenshots/screen_08-02-2024_19-53-05.png b/screenshots/screen_08-02-2024_19-53-05.png new file mode 100644 index 0000000..192d2e2 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-53-05.png differ diff --git a/screenshots/screen_08-02-2024_19-53-42.png b/screenshots/screen_08-02-2024_19-53-42.png new file mode 100644 index 0000000..abe1325 Binary files /dev/null and b/screenshots/screen_08-02-2024_19-53-42.png differ diff --git a/screenshots/screen_08-02-2024_20-01-31.png b/screenshots/screen_08-02-2024_20-01-31.png new file mode 100644 index 0000000..ff53e77 Binary files /dev/null and b/screenshots/screen_08-02-2024_20-01-31.png differ diff --git a/screenshots/screen_08-02-2024_20-01-32.png b/screenshots/screen_08-02-2024_20-01-32.png new file mode 100644 index 0000000..b359722 Binary files /dev/null and b/screenshots/screen_08-02-2024_20-01-32.png differ diff --git a/screenshots/screen_08-02-2024_20-01-33.png b/screenshots/screen_08-02-2024_20-01-33.png new file mode 100644 index 0000000..557c65d Binary files /dev/null and b/screenshots/screen_08-02-2024_20-01-33.png differ diff --git a/screenshots/screen_08-02-2024_20-04-48.png b/screenshots/screen_08-02-2024_20-04-48.png new file mode 100644 index 0000000..465d1f4 Binary files /dev/null and b/screenshots/screen_08-02-2024_20-04-48.png differ diff --git a/screenshots/screen_08-02-2024_20-06-21.png b/screenshots/screen_08-02-2024_20-06-21.png new file mode 100644 index 0000000..b482278 Binary files /dev/null and b/screenshots/screen_08-02-2024_20-06-21.png differ diff --git a/screenshots/screen_08-02-2024_20-06-32.png b/screenshots/screen_08-02-2024_20-06-32.png new file mode 100644 index 0000000..913205c Binary files /dev/null and b/screenshots/screen_08-02-2024_20-06-32.png differ diff --git a/screenshots/screen_08-02-2024_20-07-01.png b/screenshots/screen_08-02-2024_20-07-01.png new file mode 100644 index 0000000..4e5c4fe Binary files /dev/null and b/screenshots/screen_08-02-2024_20-07-01.png differ diff --git a/screenshots/screen_08-02-2024_20-07-03.png b/screenshots/screen_08-02-2024_20-07-03.png new file mode 100644 index 0000000..88058ee Binary files /dev/null and b/screenshots/screen_08-02-2024_20-07-03.png differ diff --git a/screenshots/screen_08-02-2024_20-08-16.png b/screenshots/screen_08-02-2024_20-08-16.png new file mode 100644 index 0000000..fc540ae Binary files /dev/null and b/screenshots/screen_08-02-2024_20-08-16.png differ diff --git a/screenshots/screen_08-02-2024_20-08-19.png b/screenshots/screen_08-02-2024_20-08-19.png new file mode 100644 index 0000000..d40b4c8 Binary files /dev/null and b/screenshots/screen_08-02-2024_20-08-19.png differ diff --git a/screenshots/screen_08-05-2024_19-46-34.png b/screenshots/screen_08-05-2024_19-46-34.png new file mode 100644 index 0000000..272e457 Binary files /dev/null and b/screenshots/screen_08-05-2024_19-46-34.png differ diff --git a/screenshots/screen_08-05-2024_19-46-43.png b/screenshots/screen_08-05-2024_19-46-43.png new file mode 100644 index 0000000..53d1107 Binary files /dev/null and b/screenshots/screen_08-05-2024_19-46-43.png differ diff --git a/screenshots/screen_08-05-2024_19-46-48.png b/screenshots/screen_08-05-2024_19-46-48.png new file mode 100644 index 0000000..051e8a1 Binary files /dev/null and b/screenshots/screen_08-05-2024_19-46-48.png differ diff --git a/screenshots/screen_08-05-2024_19-46-51.png b/screenshots/screen_08-05-2024_19-46-51.png new file mode 100644 index 0000000..028c168 Binary files /dev/null and b/screenshots/screen_08-05-2024_19-46-51.png differ diff --git a/screenshots/screen_08-10-2023_15-01-45.png b/screenshots/screen_08-10-2023_15-01-45.png new file mode 100644 index 0000000..d3a6e74 Binary files /dev/null and b/screenshots/screen_08-10-2023_15-01-45.png differ diff --git a/screenshots/screen_08-10-2023_15-23-56.png b/screenshots/screen_08-10-2023_15-23-56.png new file mode 100644 index 0000000..3d2bc8c Binary files /dev/null and b/screenshots/screen_08-10-2023_15-23-56.png differ diff --git a/screenshots/screen_09-10-2023_17-07-12.png b/screenshots/screen_09-10-2023_17-07-12.png new file mode 100644 index 0000000..c19d629 Binary files /dev/null and b/screenshots/screen_09-10-2023_17-07-12.png differ diff --git a/screenshots/screen_09-10-2023_18-17-57.png b/screenshots/screen_09-10-2023_18-17-57.png new file mode 100644 index 0000000..f9fa7eb Binary files /dev/null and b/screenshots/screen_09-10-2023_18-17-57.png differ diff --git a/screenshots/screen_10-10-2023_18-13-29.png b/screenshots/screen_10-10-2023_18-13-29.png new file mode 100644 index 0000000..fe01212 Binary files /dev/null and b/screenshots/screen_10-10-2023_18-13-29.png differ diff --git a/screenshots/screen_10-10-2023_18-16-40.png b/screenshots/screen_10-10-2023_18-16-40.png new file mode 100644 index 0000000..ab62637 Binary files /dev/null and b/screenshots/screen_10-10-2023_18-16-40.png differ diff --git a/screenshots/screen_10-10-2023_18-16-42.png b/screenshots/screen_10-10-2023_18-16-42.png new file mode 100644 index 0000000..990a1c0 Binary files /dev/null and b/screenshots/screen_10-10-2023_18-16-42.png differ diff --git a/screenshots/screen_10-10-2023_18-16-49.png b/screenshots/screen_10-10-2023_18-16-49.png new file mode 100644 index 0000000..567d5da Binary files /dev/null and b/screenshots/screen_10-10-2023_18-16-49.png differ diff --git a/screenshots/screen_10-10-2023_18-16-56.png b/screenshots/screen_10-10-2023_18-16-56.png new file mode 100644 index 0000000..114eddd Binary files /dev/null and b/screenshots/screen_10-10-2023_18-16-56.png differ diff --git a/screenshots/screen_13-06-2024_10-34-16.png b/screenshots/screen_13-06-2024_10-34-16.png new file mode 100644 index 0000000..4b713f0 Binary files /dev/null and b/screenshots/screen_13-06-2024_10-34-16.png differ diff --git a/screenshots/screen_13-06-2024_10-34-22.png b/screenshots/screen_13-06-2024_10-34-22.png new file mode 100644 index 0000000..40088f8 Binary files /dev/null and b/screenshots/screen_13-06-2024_10-34-22.png differ diff --git a/screenshots/screen_14-06-2024_00-12-00.png b/screenshots/screen_14-06-2024_00-12-00.png new file mode 100644 index 0000000..629e316 Binary files /dev/null and b/screenshots/screen_14-06-2024_00-12-00.png differ diff --git a/screenshots/screen_15-10-2023_02-12-09.png b/screenshots/screen_15-10-2023_02-12-09.png new file mode 100644 index 0000000..8127ffe Binary files /dev/null and b/screenshots/screen_15-10-2023_02-12-09.png differ diff --git a/screenshots/screen_15-10-2023_02-12-57.png b/screenshots/screen_15-10-2023_02-12-57.png new file mode 100644 index 0000000..9aaa505 Binary files /dev/null and b/screenshots/screen_15-10-2023_02-12-57.png differ diff --git a/screenshots/screen_15-10-2023_02-13-01.png b/screenshots/screen_15-10-2023_02-13-01.png new file mode 100644 index 0000000..5f52d8c Binary files /dev/null and b/screenshots/screen_15-10-2023_02-13-01.png differ diff --git a/screenshots/screen_15-10-2023_02-13-04.png b/screenshots/screen_15-10-2023_02-13-04.png new file mode 100644 index 0000000..8b5b378 Binary files /dev/null and b/screenshots/screen_15-10-2023_02-13-04.png differ diff --git a/screenshots/screen_15-10-2023_02-14-00.png b/screenshots/screen_15-10-2023_02-14-00.png new file mode 100644 index 0000000..195c455 Binary files /dev/null and b/screenshots/screen_15-10-2023_02-14-00.png differ diff --git a/screenshots/screen_15-10-2023_02-14-02.png b/screenshots/screen_15-10-2023_02-14-02.png new file mode 100644 index 0000000..f344b62 Binary files /dev/null and b/screenshots/screen_15-10-2023_02-14-02.png differ diff --git a/screenshots/screen_15-10-2023_02-14-05.png b/screenshots/screen_15-10-2023_02-14-05.png new file mode 100644 index 0000000..4ee7c3d Binary files /dev/null and b/screenshots/screen_15-10-2023_02-14-05.png differ diff --git a/screenshots/screen_15-10-2023_02-40-20.png b/screenshots/screen_15-10-2023_02-40-20.png new file mode 100644 index 0000000..5b65b5e Binary files /dev/null and b/screenshots/screen_15-10-2023_02-40-20.png differ diff --git a/screenshots/screen_16-10-2023_23-31-41.png b/screenshots/screen_16-10-2023_23-31-41.png new file mode 100644 index 0000000..c8dfb0e Binary files /dev/null and b/screenshots/screen_16-10-2023_23-31-41.png differ diff --git a/screenshots/screen_18-03-2024_19-51-32.png b/screenshots/screen_18-03-2024_19-51-32.png new file mode 100644 index 0000000..8e38a88 Binary files /dev/null and b/screenshots/screen_18-03-2024_19-51-32.png differ diff --git a/screenshots/screen_18-03-2024_21-02-33.png b/screenshots/screen_18-03-2024_21-02-33.png new file mode 100644 index 0000000..6e4a9f0 Binary files /dev/null and b/screenshots/screen_18-03-2024_21-02-33.png differ diff --git a/screenshots/screen_19-03-2024_11-02-03.png b/screenshots/screen_19-03-2024_11-02-03.png new file mode 100644 index 0000000..b0ddc8c Binary files /dev/null and b/screenshots/screen_19-03-2024_11-02-03.png differ diff --git a/screenshots/screen_19-03-2024_11-02-06.png b/screenshots/screen_19-03-2024_11-02-06.png new file mode 100644 index 0000000..1100e44 Binary files /dev/null and b/screenshots/screen_19-03-2024_11-02-06.png differ diff --git a/screenshots/screen_19-03-2024_11-02-07.png b/screenshots/screen_19-03-2024_11-02-07.png new file mode 100644 index 0000000..41ebc7f Binary files /dev/null and b/screenshots/screen_19-03-2024_11-02-07.png differ diff --git a/screenshots/screen_19-03-2024_11-29-09.png b/screenshots/screen_19-03-2024_11-29-09.png new file mode 100644 index 0000000..e48b9ad Binary files /dev/null and b/screenshots/screen_19-03-2024_11-29-09.png differ diff --git a/screenshots/screen_20-01-2024_00-58-00.png b/screenshots/screen_20-01-2024_00-58-00.png new file mode 100644 index 0000000..d167456 Binary files /dev/null and b/screenshots/screen_20-01-2024_00-58-00.png differ diff --git a/screenshots/screen_20-01-2024_01-20-23.png b/screenshots/screen_20-01-2024_01-20-23.png new file mode 100644 index 0000000..253ca32 Binary files /dev/null and b/screenshots/screen_20-01-2024_01-20-23.png differ diff --git a/screenshots/screen_20-01-2024_01-32-06.png b/screenshots/screen_20-01-2024_01-32-06.png new file mode 100644 index 0000000..41e58a4 Binary files /dev/null and b/screenshots/screen_20-01-2024_01-32-06.png differ diff --git a/screenshots/screen_20-01-2024_01-32-10.png b/screenshots/screen_20-01-2024_01-32-10.png new file mode 100644 index 0000000..7f2c3f9 Binary files /dev/null and b/screenshots/screen_20-01-2024_01-32-10.png differ diff --git a/screenshots/screen_20-01-2024_01-32-23.png b/screenshots/screen_20-01-2024_01-32-23.png new file mode 100644 index 0000000..9c40819 Binary files /dev/null and b/screenshots/screen_20-01-2024_01-32-23.png differ diff --git a/screenshots/screen_20-01-2024_12-12-55.png b/screenshots/screen_20-01-2024_12-12-55.png new file mode 100644 index 0000000..0502097 Binary files /dev/null and b/screenshots/screen_20-01-2024_12-12-55.png differ diff --git a/screenshots/screen_20-01-2024_14-13-53.png b/screenshots/screen_20-01-2024_14-13-53.png new file mode 100644 index 0000000..9095755 Binary files /dev/null and b/screenshots/screen_20-01-2024_14-13-53.png differ diff --git a/screenshots/screen_23-08-2024_21-29-11.png b/screenshots/screen_23-08-2024_21-29-11.png new file mode 100644 index 0000000..ef44475 Binary files /dev/null and b/screenshots/screen_23-08-2024_21-29-11.png differ diff --git a/screenshots/screen_23-08-2024_22-29-26.png b/screenshots/screen_23-08-2024_22-29-26.png new file mode 100644 index 0000000..226647a Binary files /dev/null and b/screenshots/screen_23-08-2024_22-29-26.png differ diff --git a/screenshots/screen_23-08-2024_22-29-30.png b/screenshots/screen_23-08-2024_22-29-30.png new file mode 100644 index 0000000..89b980c Binary files /dev/null and b/screenshots/screen_23-08-2024_22-29-30.png differ diff --git a/screenshots/screen_23-08-2024_22-29-30_1.png b/screenshots/screen_23-08-2024_22-29-30_1.png new file mode 100644 index 0000000..c52f1fa Binary files /dev/null and b/screenshots/screen_23-08-2024_22-29-30_1.png differ diff --git a/screenshots/screen_24-04-2024_18-07-31.png b/screenshots/screen_24-04-2024_18-07-31.png new file mode 100644 index 0000000..21ea177 Binary files /dev/null and b/screenshots/screen_24-04-2024_18-07-31.png differ diff --git a/screenshots/screen_24-04-2024_18-12-04.png b/screenshots/screen_24-04-2024_18-12-04.png new file mode 100644 index 0000000..3f761fb Binary files /dev/null and b/screenshots/screen_24-04-2024_18-12-04.png differ diff --git a/screenshots/screen_24-08-2024_00-15-15.png b/screenshots/screen_24-08-2024_00-15-15.png new file mode 100644 index 0000000..30d8534 Binary files /dev/null and b/screenshots/screen_24-08-2024_00-15-15.png differ diff --git a/screenshots/screen_24-08-2024_12-45-25.png b/screenshots/screen_24-08-2024_12-45-25.png new file mode 100644 index 0000000..ce6d80c Binary files /dev/null and b/screenshots/screen_24-08-2024_12-45-25.png differ diff --git a/screenshots/screen_24-08-2024_12-45-28.png b/screenshots/screen_24-08-2024_12-45-28.png new file mode 100644 index 0000000..09aa243 Binary files /dev/null and b/screenshots/screen_24-08-2024_12-45-28.png differ diff --git a/screenshots/screen_24-08-2024_15-51-50.png b/screenshots/screen_24-08-2024_15-51-50.png new file mode 100644 index 0000000..1de913f Binary files /dev/null and b/screenshots/screen_24-08-2024_15-51-50.png differ diff --git a/screenshots/screen_24-08-2024_22-45-53.png b/screenshots/screen_24-08-2024_22-45-53.png new file mode 100644 index 0000000..4c43718 Binary files /dev/null and b/screenshots/screen_24-08-2024_22-45-53.png differ diff --git a/screenshots/screen_24-08-2024_22-47-03.png b/screenshots/screen_24-08-2024_22-47-03.png new file mode 100644 index 0000000..2cec0a5 Binary files /dev/null and b/screenshots/screen_24-08-2024_22-47-03.png differ diff --git a/screenshots/screen_24-08-2024_22-55-23.png b/screenshots/screen_24-08-2024_22-55-23.png new file mode 100644 index 0000000..ca09964 Binary files /dev/null and b/screenshots/screen_24-08-2024_22-55-23.png differ diff --git a/screenshots/screen_24-08-2024_23-24-33.png b/screenshots/screen_24-08-2024_23-24-33.png new file mode 100644 index 0000000..10cd029 Binary files /dev/null and b/screenshots/screen_24-08-2024_23-24-33.png differ diff --git a/screenshots/screen_25-08-2024_17-50-40.png b/screenshots/screen_25-08-2024_17-50-40.png new file mode 100644 index 0000000..aa8e780 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-40.png differ diff --git a/screenshots/screen_25-08-2024_17-50-41.png b/screenshots/screen_25-08-2024_17-50-41.png new file mode 100644 index 0000000..a327c9c Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-41.png differ diff --git a/screenshots/screen_25-08-2024_17-50-42.png b/screenshots/screen_25-08-2024_17-50-42.png new file mode 100644 index 0000000..4e011dd Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-42.png differ diff --git a/screenshots/screen_25-08-2024_17-50-42_1.png b/screenshots/screen_25-08-2024_17-50-42_1.png new file mode 100644 index 0000000..1f3b615 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-42_1.png differ diff --git a/screenshots/screen_25-08-2024_17-50-43.png b/screenshots/screen_25-08-2024_17-50-43.png new file mode 100644 index 0000000..a560161 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-43.png differ diff --git a/screenshots/screen_25-08-2024_17-50-48.png b/screenshots/screen_25-08-2024_17-50-48.png new file mode 100644 index 0000000..d37a465 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-48.png differ diff --git a/screenshots/screen_25-08-2024_17-50-49.png b/screenshots/screen_25-08-2024_17-50-49.png new file mode 100644 index 0000000..98850b4 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-49.png differ diff --git a/screenshots/screen_25-08-2024_17-50-50.png b/screenshots/screen_25-08-2024_17-50-50.png new file mode 100644 index 0000000..2f98196 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-50.png differ diff --git a/screenshots/screen_25-08-2024_17-50-58.png b/screenshots/screen_25-08-2024_17-50-58.png new file mode 100644 index 0000000..8824ef1 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-58.png differ diff --git a/screenshots/screen_25-08-2024_17-50-59.png b/screenshots/screen_25-08-2024_17-50-59.png new file mode 100644 index 0000000..9524729 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-59.png differ diff --git a/screenshots/screen_25-08-2024_17-50-59_1.png b/screenshots/screen_25-08-2024_17-50-59_1.png new file mode 100644 index 0000000..46804e1 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-50-59_1.png differ diff --git a/screenshots/screen_25-08-2024_17-51-00.png b/screenshots/screen_25-08-2024_17-51-00.png new file mode 100644 index 0000000..a0fc853 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-00.png differ diff --git a/screenshots/screen_25-08-2024_17-51-01.png b/screenshots/screen_25-08-2024_17-51-01.png new file mode 100644 index 0000000..c59e20d Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-01.png differ diff --git a/screenshots/screen_25-08-2024_17-51-05.png b/screenshots/screen_25-08-2024_17-51-05.png new file mode 100644 index 0000000..c628557 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-05.png differ diff --git a/screenshots/screen_25-08-2024_17-51-05_1.png b/screenshots/screen_25-08-2024_17-51-05_1.png new file mode 100644 index 0000000..6d9d019 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-05_1.png differ diff --git a/screenshots/screen_25-08-2024_17-51-06.png b/screenshots/screen_25-08-2024_17-51-06.png new file mode 100644 index 0000000..e5d1883 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-06.png differ diff --git a/screenshots/screen_25-08-2024_17-51-07.png b/screenshots/screen_25-08-2024_17-51-07.png new file mode 100644 index 0000000..87af802 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-07.png differ diff --git a/screenshots/screen_25-08-2024_17-51-08.png b/screenshots/screen_25-08-2024_17-51-08.png new file mode 100644 index 0000000..be99f82 Binary files /dev/null and b/screenshots/screen_25-08-2024_17-51-08.png differ diff --git a/screenshots/screen_25-08-2024_19-19-21.png b/screenshots/screen_25-08-2024_19-19-21.png new file mode 100644 index 0000000..c807050 Binary files /dev/null and b/screenshots/screen_25-08-2024_19-19-21.png differ diff --git a/screenshots/screen_25-08-2024_19-19-24.png b/screenshots/screen_25-08-2024_19-19-24.png new file mode 100644 index 0000000..65ddf38 Binary files /dev/null and b/screenshots/screen_25-08-2024_19-19-24.png differ diff --git a/screenshots/screen_25-08-2024_19-21-00.png b/screenshots/screen_25-08-2024_19-21-00.png new file mode 100644 index 0000000..0dd03a7 Binary files /dev/null and b/screenshots/screen_25-08-2024_19-21-00.png differ diff --git a/screenshots/screen_25-08-2024_19-21-01.png b/screenshots/screen_25-08-2024_19-21-01.png new file mode 100644 index 0000000..3e932b2 Binary files /dev/null and b/screenshots/screen_25-08-2024_19-21-01.png differ diff --git a/screenshots/screen_25-08-2024_19-21-05.png b/screenshots/screen_25-08-2024_19-21-05.png new file mode 100644 index 0000000..4234a18 Binary files /dev/null and b/screenshots/screen_25-08-2024_19-21-05.png differ diff --git a/screenshots/screen_25-08-2024_19-21-06.png b/screenshots/screen_25-08-2024_19-21-06.png new file mode 100644 index 0000000..98f6e2a Binary files /dev/null and b/screenshots/screen_25-08-2024_19-21-06.png differ diff --git a/screenshots/screen_25-08-2024_21-24-23.png b/screenshots/screen_25-08-2024_21-24-23.png new file mode 100644 index 0000000..4015768 Binary files /dev/null and b/screenshots/screen_25-08-2024_21-24-23.png differ diff --git a/screenshots/screen_26-04-2024_19-12-00.png b/screenshots/screen_26-04-2024_19-12-00.png new file mode 100644 index 0000000..88929cd Binary files /dev/null and b/screenshots/screen_26-04-2024_19-12-00.png differ diff --git a/screenshots/screen_26-04-2024_19-57-24.png b/screenshots/screen_26-04-2024_19-57-24.png new file mode 100644 index 0000000..e030f58 Binary files /dev/null and b/screenshots/screen_26-04-2024_19-57-24.png differ diff --git a/screenshots/screen_26-08-2024_20-03-57.png b/screenshots/screen_26-08-2024_20-03-57.png new file mode 100644 index 0000000..e04dc31 Binary files /dev/null and b/screenshots/screen_26-08-2024_20-03-57.png differ diff --git a/screenshots/screen_26-08-2024_20-04-00.png b/screenshots/screen_26-08-2024_20-04-00.png new file mode 100644 index 0000000..dc19d3f Binary files /dev/null and b/screenshots/screen_26-08-2024_20-04-00.png differ diff --git a/screenshots/screen_26-08-2024_20-04-01.png b/screenshots/screen_26-08-2024_20-04-01.png new file mode 100644 index 0000000..176d90f Binary files /dev/null and b/screenshots/screen_26-08-2024_20-04-01.png differ diff --git a/screenshots/screen_26-08-2024_23-01-01.png b/screenshots/screen_26-08-2024_23-01-01.png new file mode 100644 index 0000000..f06a104 Binary files /dev/null and b/screenshots/screen_26-08-2024_23-01-01.png differ diff --git a/screenshots/screen_28-01-2024_22-40-26.png b/screenshots/screen_28-01-2024_22-40-26.png new file mode 100644 index 0000000..3573cf9 Binary files /dev/null and b/screenshots/screen_28-01-2024_22-40-26.png differ diff --git a/screenshots/screen_28-01-2024_22-40-56.png b/screenshots/screen_28-01-2024_22-40-56.png new file mode 100644 index 0000000..fd79d78 Binary files /dev/null and b/screenshots/screen_28-01-2024_22-40-56.png differ diff --git a/screenshots/screen_29-08-2024_16-07-26.png b/screenshots/screen_29-08-2024_16-07-26.png new file mode 100644 index 0000000..7bf16d2 Binary files /dev/null and b/screenshots/screen_29-08-2024_16-07-26.png differ diff --git a/screenshots/screen_29-08-2024_16-07-30.png b/screenshots/screen_29-08-2024_16-07-30.png new file mode 100644 index 0000000..19bccfb Binary files /dev/null and b/screenshots/screen_29-08-2024_16-07-30.png differ diff --git a/screenshots/screen_29-08-2024_16-07-41.png b/screenshots/screen_29-08-2024_16-07-41.png new file mode 100644 index 0000000..c75489c Binary files /dev/null and b/screenshots/screen_29-08-2024_16-07-41.png differ diff --git a/screenshots/screen_31-08-2024_11-46-52.png b/screenshots/screen_31-08-2024_11-46-52.png new file mode 100644 index 0000000..71f66aa Binary files /dev/null and b/screenshots/screen_31-08-2024_11-46-52.png differ diff --git a/screenshots/screen_31-08-2024_11-47-13.png b/screenshots/screen_31-08-2024_11-47-13.png new file mode 100644 index 0000000..d434b28 Binary files /dev/null and b/screenshots/screen_31-08-2024_11-47-13.png differ diff --git a/shader.h b/shader.h new file mode 100644 index 0000000..29f7030 --- /dev/null +++ b/shader.h @@ -0,0 +1,327 @@ + +uint shd_compile(const char* vcode, const char* fcode, uint vlen, uint flen, const char* vertex, const char* fragment, const char* inc) { + int ok; + uint vs, fs, pr; + char *logstr = sys.work_buf; + const char *code[3]; + uint len[3]; + code[0] = "#version " SHD_VERSION "\n"; + len[0] = strlen(code[0]); + vs = glCreateShader(GL_VERTEX_SHADER); + code[1] = inc; + code[2] = vcode; + len[1] = strlen(inc); + len[2] = vlen; + glShaderSource(vs, 3, code, len); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + if(!ok) { + glGetShaderInfoLog(vs, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EVERTEX, vertex); + glDeleteShader(vs); + return 0; + } + fs = glCreateShader(GL_FRAGMENT_SHADER); + code[1] = inc; + code[2] = fcode; + len[1] = strlen(inc); + len[2] = flen; + glShaderSource(fs, 3, code, len); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + if(!ok) { + glGetShaderInfoLog(fs, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EFRAGMENT, fragment); + glDeleteShader(vs); + glDeleteShader(fs); + return 0; + } + pr = glCreateProgram(); + glAttachShader(pr, vs); + glAttachShader(pr, fs); + glLinkProgram(pr); + glGetProgramiv(pr, GL_LINK_STATUS, &ok); + if(!ok) { + glGetProgramInfoLog(pr, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EPROGRAM, vertex, fragment); + glDeleteShader(vs); + glDeleteShader(fs); + glDeleteProgram(pr); + return 0; + } + glDeleteShader(vs); + glDeleteShader(fs); + return pr; +} + +/* +void shd_delete(uint *id) { + if(*id) { + glDeleteProgram(*id); + logd(LOG_GFX, STR_SHD_IUNLOADED, *id); + *id = 0; + } +} +*/ + +uint shd_load_files(const char* vertex, const char* fragment, const char* inc) { + byte *vcode; + byte *fcode; + int ok; + uint prog; + uint vsize, fsize; + vsize = file_read(&vcode, vertex); + if(!vsize) { + // loge(LOG_GFX, STR_SHD_ELOADVERT, vertex); + return 0; + } + fsize = file_read(&fcode, fragment); + if(!fsize) { + // loge(LOG_GFX, STR_SHD_ELOADFRAG, fragment); + mem_free(vcode); + return 0; + } + prog = shd_compile(vcode, fcode, vsize, fsize, vertex, fragment, inc); + mem_free(vcode); + mem_free(fcode); + return prog; +} + +byte shd_unload(shd_t *shd) { + if(!(shd->id)) { + return 0; + } + glDeleteProgram(shd->id); + // if(shd->builtin) + // logd(LOG_GFX, STR_SHD_IUNLOADED, shd->id); + // else + logd(LOG_GFX, STR_SHD_UNLOADED, shd->vpath, shd->fpath, shd->id); + shd->id = 0; + return 1; +} + +void shd_remove(shd_t *shd) { + if(shd_unload(shd)) { // && (shd->builtin ^ 1)) { + // tbl_pop(&gdr.shaders, shd); + gdr.shd_loaded -= 1; + } +} + +// byte shd_remove_sel(shd_t *shd) { + // if(shd->builtin ^ 1) { + // shd_unload(shd); + // gdr.shd_loaded -= 1; + // return 1; + // } + // return 0; +// } + +int shd_clear() { + return tbl_clear_func(&gdr.shaders, (clear_func*)shd_remove); +} + +// int shd_clear() { + // return tbl_clear_func_sel(&gdr.shaders, (clear_sfunc*)shd_remove_sel, gdr.shd_loaded); +// } + +void shd_sel(shd_t *shader, void *draw) { + if(gdr.shader != shader->id) { + glUseProgram(gdr.shader = shader->id); + if(shader->draw) + shader->draw(draw); + } +} + +/* +byte shd_load(uint *shader, const char *vpath, const char *fpath, shd_func *init, shd_func *draw) { + shd_t *bind; + // if(!(( + *shader = shd_load_files(vpath, fpath, gdr.shd_include); + // )) { + // *shader = 0; + // return 0; + // } + sys_assert(bind = (shd_t*)tbl_push(&gdr.shaders)); + bind->vpath = vpath; + bind->fpath = fpath; + bind->id = shader; + bind->init = init; + bind->draw = draw; + bind->builtin = 0; + shd_sel(*shader); + if(bind->init) + bind->init(); + gdr.shd_loaded += 1; + logd(LOG_GFX, STR_SHD_LOADED, vpath, fpath, *shader); + return 1; +} +*/ + +byte shd_load(shd_t **shader, const char* vpath, const char* fpath, shd_func *init, shd_dfunc *draw) { + uint id; + shd_t *bind; + // id = shd_compile(vcode, fcode, strlen(vcode), strlen(fcode), name, name, gdr.shd_include); + id = shd_load_files(vpath, fpath, gdr.shd_include); + if(!id) { + *shader = NULL; + return 0; + } + sys_assert(bind = (shd_t*)tbl_push(&gdr.shaders)); + bind->vpath = vpath; + bind->fpath = fpath; + bind->id = id; + bind->init = init; + bind->draw = draw; + // bind->builtin = 1; + glUseProgram(gdr.shader = id); + if(bind->init) + bind->init(); + logd(LOG_GFX, STR_SHD_LOADED, vpath, fpath, id); + *shader = bind; + gdr.shader = 0; + gdr.shd_loaded += 1; + return 1; +} + +byte shd_reload(shd_t *shader) { + uint sh; + if(sh = // (shader->builtin ? shd_compile(shader->vpath, shader->fpath, strlen(shader->vpath), strlen(shader->fpath), "[builtin]", "[builtin]", gdr.shd_include): + shd_load_files(shader->vpath, shader->fpath, gdr.shd_include)) { + glUseProgram(gdr.shader = sh); + if(shader->init) + shader->init(); + // if(shader->builtin) + // logd(LOG_GFX, STR_SHD_IREASSIGN, shader->id, sh); + // else + logd(LOG_GFX, STR_SHD_REASSIGN, shader->vpath, shader->fpath, shader->id, sh); + shd_unload(shader); + shader->id = sh; + return 1; + } + return 0; +} + +#define SHD_SETFNC(fn, sf, vt) void fn(const char *name, vt value) { sf(glGetUniformLocation(gdr.shader, name), value); } +#define SHD_SETFNCV(fn, sf, vt) void fn(const char *name, vt vec) { sf(glGetUniformLocation(gdr.shader, name), 1, &vec[0]); } +#define SHD_SETFNCM(fn, sf, vt) void fn(const char *name, vt mat) { sf(glGetUniformLocation(gdr.shader, name), 1, GL_FALSE, &mat[0][0]); } +#define SHD_SETFNC2(fn, sf, vt) void fn(const char *name, vt x, vt y) { sf(glGetUniformLocation(gdr.shader, name), x, y); } +#define SHD_SETFNC3(fn, sf, vt) void fn(const char *name, vt x, vt y, vt z) { sf(glGetUniformLocation(gdr.shader, name), x, y, z); } +#define SHD_SETFNC4(fn, sf, vt) void fn(const char *name, vt x, vt y, vt z, vt w) { sf(glGetUniformLocation(gdr.shader, name), x, y, z, w); } + +SHD_SETFNC(shd_bool, glUniform1i, byte); +SHD_SETFNC(shd_int, glUniform1i, int); +SHD_SETFNC(shd_float, glUniform1f, float); +SHD_SETFNCV(shd_vec2, glUniform2fv, vec2); +SHD_SETFNCV(shd_vec3, glUniform3fv, vec3); +SHD_SETFNCV(shd_vec4, glUniform4fv, vec4); +SHD_SETFNC2(shd_vec2v, glUniform2f, float); +SHD_SETFNC3(shd_vec3v, glUniform3f, float); +SHD_SETFNC4(shd_vec4v, glUniform4f, float); +SHD_SETFNCM(shd_mat2, glUniformMatrix2fv, mat2); +SHD_SETFNCM(shd_mat3, glUniformMatrix3fv, mat3); +SHD_SETFNCM(shd_mat4, glUniformMatrix4fv, mat4); + +void shd_color(const char *name, uint color) { + glUniform4f(glGetUniformLocation(gdr.shader, name), + (float)((uint)((color >> 16) & 0xff)) / 255.0f, (float)((uint)((color >> 8) & 0xff)) / 255.0f, + (float)((uint)(color & 0xff)) / 255.0f, (float)((uint)((color >> 24) & 0xff)) / 255.0f + ); +} + +void shd_scolor(const char *name, uint color) { + glUniform3f(glGetUniformLocation(gdr.shader, name), + (float)((uint)((color >> 16) & 0xff)) / 255.0f, (float)((uint)((color >> 8) & 0xff)) / 255.0f, + (float)((uint)(color & 0xff)) / 255.0f + ); +} + +void shd_setint(int *pos, const char *vardef, int value) { + *pos += sprintf(&gdr.shd_include[*pos], "#define %s %d\n", vardef, value); +} + +void shd_setvars() { + int pos = 0; + shd_setint(&pos, "MAX_LIGHTS", gdr.light_max < 1 ? 1 : gdr.light_max); +} + +int shd_refresh() { + int done = 0; + ulong iter = 0; + shd_t *shd; + shd_setvars(); + while(shd = tbl_iter(&gdr.shaders, &iter)) { + done += (int)shd_reload(shd); + } + // shd_setup(); + logd(LOG_GFX, STR_SHD_REFRESH, done); + return done; +} + +/* +byte shd_compile_int(uint *shader, const char* name, const char* vcode, const char* fcode) { + uint pr = shd_compile(vcode, fcode, strlen(vcode), strlen(fcode), name, name, gdr.shd_include); + if(!pr) + return 0; + /* + int ok; + uint vs, fs, pr; + char *logstr = sys.work_buf; + const char *code[2]; + uint len[2]; + *shader = 0; + code[0] = "#version " SHD_VERSION "\n"; + len[0] = strlen(code[0]); + vs = glCreateShader(GL_VERTEX_SHADER); + code[1] = vcode; + len[1] = strlen(vcode); + glShaderSource(vs, 2, code, len); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + if(!ok) { + glGetShaderInfoLog(vs, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EIVERTEX, name); + glDeleteShader(vs); + return 0; + } + fs = glCreateShader(GL_FRAGMENT_SHADER); + code[1] = fcode; + len[1] = strlen(fcode); + glShaderSource(fs, 2, code, len); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + if(!ok) { + glGetShaderInfoLog(fs, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EIFRAGMENT, name); + glDeleteShader(vs); + glDeleteShader(fs); + return 0; + } + pr = glCreateProgram(); + glAttachShader(pr, vs); + glAttachShader(pr, fs); + glLinkProgram(pr); + glGetProgramiv(pr, GL_LINK_STATUS, &ok); + if(!ok) { + glGetProgramInfoLog(pr, SHD_TRACE_SIZE, NULL, logstr); + loge(LOG_GFX, "%s", logstr); + loge(LOG_GFX, STR_SHD_EIPROGRAM, name); + glDeleteShader(vs); + glDeleteShader(fs); + glDeleteProgram(pr); + return 0; + } + glDeleteShader(vs); + glDeleteShader(fs); + **** //// + *shader = pr; + shd_sel(pr); + shd_int("tex", 0); + logd(LOG_GFX, STR_SHD_ILOADED, name, pr); + return 1; +} +*/ diff --git a/shaders/blit.fsh b/shaders/blit.fsh new file mode 100644 index 0000000..c52248e --- /dev/null +++ b/shaders/blit.fsh @@ -0,0 +1,9 @@ +out vec4 FragColor; + +in vec2 tex_coord; + +uniform sampler2D tex; + +void main() { + FragColor = texture(tex, tex_coord); +} diff --git a/shaders/blit.vsh b/shaders/blit.vsh new file mode 100644 index 0000000..6884929 --- /dev/null +++ b/shaders/blit.vsh @@ -0,0 +1,8 @@ +layout (location = 0) in vec2 coords; + +out vec2 tex_coord; + +void main() { + tex_coord = coords; + gl_Position = vec4(coords * 2.0 - 1.0, 0.0, 1.0); +} diff --git a/shaders/grid.fsh b/shaders/grid.fsh new file mode 100644 index 0000000..c86d557 --- /dev/null +++ b/shaders/grid.fsh @@ -0,0 +1,38 @@ +out vec4 FragColor; + +in vec3 vertex; +in vec2 tex_coord; + +uniform float time; + +#define GLM_PI 3.14159265358979323846264338327950288 +#define GLM_SQRT2 1.41421356237309504880168872420969808 +#define vis_div 24.0 + +float v2rand(vec2 uv) { + return fract(sin(dot(uv, vec2(12.9898,78.233))) * 43758.5453123); +} + +float avg(vec4 v) { + return ((v.r + v.g + v.b) / 3.0) * v.a; +} + +float gauss(float v, float b, float c) { + return (1.0 / (c * sqrt(2.0 * GLM_PI))) * exp(-(pow(v - b, 2.0) / (2.0 * pow(c, 2.0)))); +} + +float dgauss(float v, float b, float c) { + return b + v * c; +} + +float inside(vec2 vec, vec2 offs, float a, float b, float scale, float x1, float x2, float y1, float y2) { + vec2 cscaled = (mod(vec + offs + 1.0, 1.0)) * scale; + return (x2 < x1 ? (cscaled.x >= x1 || cscaled.x <= x2) : (cscaled.x >= x1 && cscaled.x <= x2)) || (y2 < y1 ? (cscaled.y >= y1 || cscaled.y <= y2) : (cscaled.y >= y1 && cscaled.y <= y2)) || (abs(cscaled.y - (cscaled.x - (x2 < x1 ? (x1 - x2) : (x2 - x1)) * GLM_SQRT2 * (x2 < x1 ? 0.0 : (x1 < scale / 2.0 ? x1 : (x2 - scale))))) < GLM_SQRT2) ? b : a; +} + +void main() { + vec2 shift = vec2(v2rand(tex_coord + fract(time)) * 2.0 - 1.0, v2rand(tex_coord + 0.5 + fract(time)) * 2.0 - 1.0); + shift = vec2(dgauss(shift.x, 0.0, 1.0), dgauss(shift.y, 0.0, 1.0)) * 0.015; + vec3 cl = vec3(inside(tex_coord / vis_div, shift, 0.0, 1.0, 64.0, 1.0, 3.0, 1.0, 3.0), inside(tex_coord / vis_div, shift, 0.0, 1.0, 64.0, 63.0, 1.0, 63.0, 1.0), inside(tex_coord / vis_div, shift, 0.0, 1.0, 64.0, 61.0, 63.0, 61.0, 63.0)); + FragColor = vec4(mix(vec3(0.0, 0.0, clamp(0.1 + 0.2 * (vertex.y + 64.0) / 128.0, 0.0, 1.0)), cl, clamp(cl.x + cl.y + cl.z, 0.0, 1.0)), 1.0); +} diff --git a/shaders/grid.vsh b/shaders/grid.vsh new file mode 100644 index 0000000..78259dc --- /dev/null +++ b/shaders/grid.vsh @@ -0,0 +1,16 @@ +layout (location = 0) in vec3 pos; +layout (location = 1) in vec3 norm; +layout (location = 2) in vec2 coord; + +out vec3 vertex; +out vec2 tex_coord; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() { + vertex = vec3(model * vec4(pos, 1.0)); + tex_coord = coord; + gl_Position = projection * view * vec4(vertex, 1.0); +} diff --git a/shaders/lightmap.fsh b/shaders/lightmap.fsh new file mode 100644 index 0000000..37202e3 --- /dev/null +++ b/shaders/lightmap.fsh @@ -0,0 +1,8 @@ +out vec4 FragColor; + +uniform float fog; +uniform vec3 color; + +void main() { + FragColor = vec4(color, fog); +} diff --git a/shaders/lightmap.vsh b/shaders/lightmap.vsh new file mode 100644 index 0000000..0e04610 --- /dev/null +++ b/shaders/lightmap.vsh @@ -0,0 +1,7 @@ +layout (location = 0) in vec2 coords; + +uniform vec2 size; + +void main() { + gl_Position = vec4(coords * 2.0 / size, 0.0, 1.0); +} diff --git a/shaders/rect.fsh b/shaders/rect.fsh new file mode 100644 index 0000000..bda6915 --- /dev/null +++ b/shaders/rect.fsh @@ -0,0 +1,20 @@ +out vec4 FragColor; + +in vec2 tex_coord; + +uniform vec2 size; +uniform sampler2D tex; +uniform bool use_tex; +uniform float border; +uniform vec4 fill_t; +uniform vec4 fill_b; +uniform vec4 border_t; +uniform vec4 border_b; + +void main() { + vec2 tex_pos = tex_coord * size; + if(tex_pos.x < border || tex_pos.x > (size.x - border) || tex_pos.y < border || tex_pos.y > (size.y - border)) + FragColor = mix(border_t, border_b, tex_coord.y); + else + FragColor = (use_tex ? texture(tex, (tex_coord - (border / size)) * (size / (size - (border * 2.0)))) : vec4(1.0)) * mix(fill_t, fill_b, tex_coord.y); +} diff --git a/shaders/rect.vsh b/shaders/rect.vsh new file mode 100644 index 0000000..064b5f1 --- /dev/null +++ b/shaders/rect.vsh @@ -0,0 +1,12 @@ +layout (location = 0) in vec2 coords; + +out vec2 tex_coord; + +uniform vec2 screen; +uniform vec2 offset; +uniform vec2 size; + +void main() { + tex_coord = coords; + gl_Position = vec4((vec2(offset.x, screen.y - offset.y) + (coords * vec2(size.x, -size.y))) / screen * 2.0 - 1.0, 0.0, 1.0); +} diff --git a/shaders/text.fsh b/shaders/text.fsh new file mode 100644 index 0000000..5c115c0 --- /dev/null +++ b/shaders/text.fsh @@ -0,0 +1,35 @@ +out vec4 FragColor; + +in vec2 tex_coord; + +uniform vec2 font_size; +uniform vec4 pad; +uniform vec4 glyph; +uniform vec4 color; +uniform vec4 back; +uniform sampler2D tex; +uniform int flags; +uniform int ch_index; +uniform float time; + +#define FLAG_UL 0x01 +#define FLAG_CL 0x02 +#define FLAG_BL 0x04 +#define FLAG_FD 0x08 + +void main() { + vec2 tex_pos = glyph.st - pad.st + tex_coord * ((glyph.pq + pad.pq) - (glyph.st - pad.st)); + if((flags & FLAG_CL) != 0 && tex_pos.y >= floor(font_size.y / 2.0) && tex_pos.y < (floor(font_size.y / 2.0) + 1.0)) + FragColor = vec4(0.25, 0.25, 0.25, color.a); + else if((flags & FLAG_UL) != 0 && tex_pos.y >= (font_size.y - 2.0) && tex_pos.y < (font_size.y - 1.0)) + FragColor = vec4(0.95, 0.1, 0.1, color.a); + else if(tex_pos.x < glyph.s || tex_pos.x > glyph.p || tex_pos.y < glyph.t || tex_pos.y > glyph.q) + FragColor = vec4(0.0); + else + FragColor = texture(tex, (vec2(float(ch_index & 15), float((ch_index >> 4) & 15)) + (tex_pos / font_size)) / 16.0) * color; + if((flags & FLAG_BL) != 0 && fract(time) >= 0.5) + FragColor = vec4(0.0, 1.0, 0.0, FragColor.a); + if((flags & FLAG_FD) != 0) + FragColor = mix(FragColor, vec4(1.0, 1.0, 1.0, FragColor.a), sin(radians(fract(time + tex_coord.y) * 360.0)) * 0.5 + 0.5); + FragColor = mix(back, FragColor, FragColor.a); +} diff --git a/shaders/text.vsh b/shaders/text.vsh new file mode 100644 index 0000000..064b5f1 --- /dev/null +++ b/shaders/text.vsh @@ -0,0 +1,12 @@ +layout (location = 0) in vec2 coords; + +out vec2 tex_coord; + +uniform vec2 screen; +uniform vec2 offset; +uniform vec2 size; + +void main() { + tex_coord = coords; + gl_Position = vec4((vec2(offset.x, screen.y - offset.y) + (coords * vec2(size.x, -size.y))) / screen * 2.0 - 1.0, 0.0, 1.0); +} diff --git a/shaders/vis.fsh b/shaders/vis.fsh new file mode 100644 index 0000000..1d2fb5b --- /dev/null +++ b/shaders/vis.fsh @@ -0,0 +1,7 @@ +out vec4 FragColor; + +in float ypos; + +void main() { + FragColor = vec4(clamp(-0.3 + 1.8 * (ypos + 64.0) / 128.0, 0.0, 1.0), clamp(-0.7 + 2.2 * (ypos + 64.0) / 128.0, 0.0, 1.0), clamp(0.1 + 2.0 * (ypos + 64.0) / 128.0, 0.0, 1.0), 1.0); +} diff --git a/shaders/vis.vsh b/shaders/vis.vsh new file mode 100644 index 0000000..58ad507 --- /dev/null +++ b/shaders/vis.vsh @@ -0,0 +1,15 @@ +layout (location = 0) in vec3 pos; +layout (location = 1) in vec3 norm; +layout (location = 2) in vec2 coord; + +out float ypos; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() { + vec3 vertex = vec3(model * vec4(pos, 1.0)); + ypos = vertex.y; + gl_Position = projection * view * vec4(vertex, 1.0); +} diff --git a/shaders/world.fsh b/shaders/world.fsh new file mode 100644 index 0000000..091fd70 --- /dev/null +++ b/shaders/world.fsh @@ -0,0 +1,87 @@ +out vec4 FragColor; + +struct light_t { + vec4 position; + vec4 ambient; // constant + vec4 diffuse; // linear + vec4 specular; // quadratic + vec4 range; +}; + +in vec3 vertex; +in vec3 normal; +in vec2 tex_coord; + +uniform vec3 cam_pos; +uniform sampler2D tex; +uniform vec3 specular; +uniform float shine; +uniform float max_vert_dist; +uniform float max_cam_dist; +uniform float light_factor; +uniform vec3 dir_direction; +uniform vec3 dir_ambient; +uniform vec3 dir_diffuse; +uniform vec3 dir_specular; +uniform int n_lights; +uniform light_block { + light_t lights[MAX_LIGHTS]; +}; + +/* +vec4 pcf_sample(sampler2D stex, vec2 pos) { + vec4 avg = vec4(0.0); + vec2 texel = 1.0 / textureSize(stex, 0); + for(int x = -1; x <= 1; x++) { + for(int y = -1; y <= 1; y++) { + avg += texture(stex, pos + vec2(x, y) * texel); + } + } + return avg /= 9.0; +} +*/ + +vec3 calc_dir_light(vec3 norm, vec3 dir, vec3 rgb) { + vec3 ldir = normalize(-dir_direction); + float diff = max(dot(norm, ldir), 0.0); + vec3 rdir = reflect(-ldir, norm); + float spec = pow(max(dot(dir, rdir), 0.0), shine); + vec3 ambient = dir_ambient * rgb + clamp(vec3(0.0), 0.0, 1.0); + vec3 diffuse = dir_diffuse * diff * rgb; + vec3 specular = dir_specular * spec * specular * rgb; + return (ambient + diffuse + specular); +} + +vec3 calc_point_light(light_t light, vec3 norm, vec3 dir, vec3 rgb) { + rgb = clamp(rgb + light_factor, 0.0, 1.0); + vec3 ldir = normalize(light.position.xyz - vertex); + float diff = max(dot(norm, ldir), 0.0); + vec3 rdir = reflect(-ldir, norm); + float spec = pow(max(dot(dir, rdir), 0.0), shine); + float distance = length((light.position.xyz - vertex) / light.range.xyz); + float attenuation = 1.0 / (light.ambient.w + light.diffuse.w * distance + light.specular.w * (distance * distance)); + vec3 ambient = light.ambient.xyz * rgb; + vec3 diffuse = light.diffuse.xyz * diff * rgb; + vec3 specular = light.specular.xyz * spec * specular * rgb; + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular); +} + +void main() { + vec3 norm = normalize(normal); + vec3 dir = normalize(cam_pos - vertex); + vec4 texel = texture(tex, tex_coord); + vec3 rgb = texel.rgb * texel.a; + vec3 result = calc_dir_light(norm, dir, rgb); + // int l = 0; + for(int z = 0; z < n_lights; z++) { + // if(lights[z].enabled) { + if(distance(vertex, lights[z].position.xyz) <= max_vert_dist && distance(cam_pos, lights[z].position.xyz) <= max_cam_dist) + result += calc_point_light(lights[z], norm, dir, rgb); + // l++; + // } + } + FragColor = vec4(result, 1.0); +} diff --git a/shaders/world.vsh b/shaders/world.vsh new file mode 100644 index 0000000..ee7ec23 --- /dev/null +++ b/shaders/world.vsh @@ -0,0 +1,20 @@ +layout (location = 0) in vec3 pos; +layout (location = 1) in vec3 norm; +layout (location = 2) in vec2 coord; + +out vec3 vertex; +out vec3 normal; +out vec2 tex_coord; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform sampler2D tex; +uniform float density; + +void main() { + vertex = vec3(model * vec4(pos, 1.0)); + normal = mat3(transpose(inverse(model))) * norm; + tex_coord = coord * density / textureSize(tex, 0); + gl_Position = projection * view * vec4(vertex, 1.0); +} diff --git a/smap.h b/smap.h new file mode 100644 index 0000000..e769d28 --- /dev/null +++ b/smap.h @@ -0,0 +1,112 @@ + +byte smap_key(const char *str) { + char start = str_lower(str[0]); + return ((start >= 'a') && (start <= 'z')) ? (start - 'a') : 26; +} + +void smap_init(smap_t *map, uint load, byte cat) { + memset(map->elems, 0, sizeof(map->elems)); + map->load = load; + map->cat = cat; + map->stored = 0; +} + +byte smap_put(smap_t *map, const char *key, void *elem) { + byte k = smap_key(key); + void *list = map->elems[k]; + uint size; + uint stored; + void **elems; + if(!list) { + map->elems[k] = list = mem_alloc(sizeof(void *) * map->load * 2 + sizeof(uint) * 2, map->cat); + memset(list, 0, sizeof(void *) * map->load * 2 + sizeof(uint) * 2); + ((uint*)list)[0] = size = map->load; + stored = 0; + } + else { + size = ((uint*)list)[0]; + stored = ((uint*)list)[1]; + } + elems = (void**)(((uint*)list) + 2); + for(uint z = 0; z < size; z++) { + if((elems[z*2] != NULL) && str_eq_lower(key, (char*)(elems[z*2]))) { + elems[z*2+0] = (void*)key; + elems[z*2+1] = elem; + return 0; + } + } + if(stored == size) { + map->elems[k] = list = mem_realloc(list, sizeof(void *) * (size+(map->load)) * 2 + sizeof(uint) * 2, map->cat); + elems = (void**)(((uint*)list) + 2); + memset(&elems[size*2], 0, sizeof(void *) * map->load * 2); // + sizeof(uint) * 2); + ((uint*)list)[0] = size += map->load; + } + for(uint z = 0; z < size; z++) { + if(elems[z*2] == NULL) { + elems[z*2+0] = (void*)key; + elems[z*2+1] = elem; + break; + } + } + ((uint*)list)[1] = stored + 1; + map->stored += 1; + return 1; +} + +// void logd(const char *prefix, const char *fmt, ...); + +void *smap_get(smap_t *map, const char *key) { + byte k = smap_key(key); + uint size; + void *list = map->elems[k]; + void **elems; + if(!list) { + // logd("tst", "n: %d / %s", k, key); + return NULL; + } + size = ((uint*)list)[0]; + elems = (void**)(((uint*)list) + 2); + for(uint z = 0; z < size; z++) { + if((elems[z*2] != NULL) && str_eq_lower(key, (char*)(elems[z*2+0]))) { + // logd("tst", "h: %d / %d / %s / %s / %s / %d", k, z, key, (char*)elems[z*2], (char*)elems[z*2+1], map->stored); + return elems[z*2+1]; + } + } + // logd("tst", "l: %d / %s", k, key); + return NULL; +} + +void smap_clear(smap_t *map) { + map->stored = 0; + for(int z = 0; z < 27; z++) { + if(map->elems[z]) { + mem_free(map->elems[z]); + map->elems[z] = NULL; + } + } +} + +void *smap_iter(smap_t *map, int *ptr) { + int k = (*ptr) >> 27; + int l = (*ptr) & 0x07ffffff; + void *list; + uint size; + void **elems; + void *elem; + for(; k < 27; k++) { + if(!(list = map->elems[k])) { + l = 0; + continue; + } + size = ((uint*)list)[0]; + elems = (void**)(((uint*)list) + 2); + for(; l < size; l++) { + if(elem = elems[l*2+1]) { + *ptr = (k << 27) | (l + 1); + return elem; + } + } + l = 0; + } + return NULL; +} diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..7c9422c --- /dev/null +++ b/sound.h @@ -0,0 +1,309 @@ + +typedef struct { + const char *devname; + uint samplerate; + uint frames; + uint bufsize; + uint width; +} snd_devconfig; + +enum snd_cmd { + SND_CMD_STOP, + SND_CMD_FLUSH, + SND_CMD_QUERY, + SND_CMD_RESET, + SND_CMD_MKOPL, + SND_CMD_VOICES, + // SND_CMD_BANK, + SND_CMD_PAUSE, + SND_CMD_LOAD, + SND_CMD_OFF, + SND_CMD_DEBUG, + SND_CMD_WAVECAP, + SND_CMD_VOLUME +}; + +enum snd_stat { + SND_STAT_RUN, + SND_STAT_STOPPED +}; + +ulong snd_process() { + ulong elapsed = 1000ULL; + if(!sgt.paused) { + if(sgt.mid.track && (sgt.playing ^ 1) && (sgt.nowait || !(sgt.chip) || !OPL3_Playing(sgt.chip))) { + // flush ? + if(sgt.mid.track) { + free(sgt.mid.track); + sgt.mid.track = NULL; + } + if(sgt.chip) { + OPL3_Reset(sgt.chip); + bank_reset(sgt.chip, sgt.bank); + } + sgt.mid_tick = 0; + sgt.mid_time = 0ULL; + sgt.stopped = 1; + } + else if(sgt.mid.track && sgt.playing && mid_tick(&sgt.mid, sgt.bank, sgt.chip)) { + elapsed = ((ulong)sgt.mid.ticktime) * 1000ULL; + sgt.mid_tick += 1; + sgt.mid_time += ((ulong)sgt.mid.ticktime); + } + else if(sgt.playing) { + sgt.playing = 0; + if(sgt.bank) + bank_alloff(sgt.bank); + } + } + // sampling ... + return elapsed; +} + +void snd_generate(short *sample) { + int mix[2]; + if(!sgt.paused && sgt.chip) + OPL3_GenerateResampled(sgt.chip, &sample[SND_VOL_MUSIC*2]); + else + sample[SND_VOL_MUSIC*2+0] = sample[SND_VOL_MUSIC*2+1] = 0; + // sampling ... + sample[SND_VOL_SFX*2+0] = sample[SND_VOL_SFX*2+1] = 0; + sample[SND_VOL_GUI*2+0] = sample[SND_VOL_GUI*2+1] = 0; + mix[0] = mix[1] = 0; + for(int z = 1; z < SND_VOLUMES; z++) { + mix[0] += (short)(int)(((int)sample[z*2+0] * (int)sgt.volumes[z]) / 32767); + mix[1] += (short)(int)(((int)sample[z*2+1] * (int)sgt.volumes[z]) / 32767); + } + sample[0] = (short)(int)((mix[0] * (int)sgt.volumes[0]) / 32767); + sample[1] = (short)(int)((mix[1] * (int)sgt.volumes[0]) / 32767); +} + +void snd_dispatch(void *buf, short op, short addr, uint param, uint rate) { + switch(op) { + case SND_CMD_PAUSE: + sgt.paused = (byte)param; + break; + case SND_CMD_LOAD: + // mid_cmlog(); + sgt.playing = 0; + if(sgt.mid.track) { + free(sgt.mid.track); + sgt.mid.track = NULL; + } + if(buf && sgt.chip) { + sgt.mid.tracks = param & 0xffff; + sgt.mid.tpqn = (param >> 16) & 0x7fff; + mid_settempo(&sgt.mid, MID_DEFTEMPO); + sgt.mid.track = (mid_track *)buf; + buf += (sizeof(mid_track) * sgt.mid.tracks); + for(int z = 0; z < sgt.mid.tracks; z++) { + sgt.mid.track[z].buffer = buf; + buf += sgt.mid.track[z].size; + } + sgt.playing = 1; + sgt.nowait = (byte)(param >> 31); + } + else if(buf) { + free(buf); + } + case SND_CMD_RESET: + if(sgt.chip) { + OPL3_Reset(sgt.chip); + bank_reset(sgt.chip, sgt.bank); + } + sgt.mid_tick = 0; + sgt.mid_time = 0ULL; + break; + case SND_CMD_OFF: + if(sgt.mid.track) + bank_alloff(sgt.bank); + break; + case SND_CMD_MKOPL: + if(sgt.chip) { + free(sgt.bank); + free(sgt.chip); + } + sgt.chip = param ? OPL3_Alloc(rate, param & 0xff) : NULL; + sgt.bank = param ? bank_alloc(sgt.chip, sgt.ibank, (param >> 16) & 1, (param >> 17) & 1, (param >> 8) & 0xff) : NULL; + sgt.stopped = param ? 1 : 0; + break; + case SND_CMD_VOICES: + if(buf) { + memcpy(sgt.ibank, buf, sizeof(bank_instr) * 256); + free(buf); + } + // else { + // memcpy(sgt.ibank, snd_banks[addr], sizeof(bank_instr) * 256); + // } + break; + // case SND_CMD_BANK: + // sgt.instr = addr >= 31 ? sgt.ibank : (bank_instr *)snd_banks[addr]; + // break; + case SND_CMD_DEBUG: + sgt.log_debug = (byte)param; + break; + case SND_CMD_WAVECAP: + if(sgt.capture) + wav_close(&sgt.wav); + sgt.capture = buf && wav_open(&sgt.wav, (char *)buf, rate, 2, 2); + if(buf) + free(buf); + break; + case SND_CMD_VOLUME: + sgt.volumes[addr] = (ushort)param; + // snd_logd("V %d = %d", addr, param); + break; + } +} + +uint snd_status(short addr, uint param) { + switch(addr) { + case SND_STAT_STOPPED: + param = sgt.stopped; + sgt.stopped = 0; + return param; + default: + return 0; + } +} + +void *snd_loop(void *arg) { + snd_devconfig *cfg = (snd_devconfig*)arg; + aud_handle handle; + uint pop = 0; + short sample[2 * SND_VOLUMES]; + uint rate = strcmp(cfg->devname, "none") ? aud_open_dev(&handle, cfg->devname, cfg->samplerate, 2, (byte)cfg->width, cfg->frames, cfg->bufsize) : 0; + ulong sampletime = rate ? (1000000000ULL / (ulong)rate) : 100000000000000000ULL; + byte running = 1; + ulong elapsed = 0; + ulong samples = 0; + sample[0] = sample[1] = 0; + while(running) { + pthread_mutex_lock(&sgt.lock); + while(sgt.cmd_queue) { + snd_cmd_t *cmd = &sgt.cmds[pop]; + switch(cmd->command) { + case SND_CMD_STOP: + running = 0; + break; + case SND_CMD_FLUSH: + + elapsed = 0; + if(rate) + aud_flush(&handle); + break; + case SND_CMD_QUERY: + switch(cmd->address) { + case SND_STAT_RUN: + sgt.query = rate; + break; + default: + sgt.query = snd_status(cmd->address, cmd->param); + break; + } + sgt.waiting = 0; + break; + default: + snd_dispatch(cmd->buffer, cmd->command, cmd->address, cmd->param, rate); + break; + } + if(++pop == SND_QUEUE) + pop = 0; + sgt.cmd_queue -= 1; + } + pthread_mutex_unlock(&sgt.lock); + if(sgt.interrupt) + sgt.interrupt = 0; + else + elapsed += snd_process(); + // if((elapsed < sampletime) || !rate) + // usleep(1000); + while((elapsed >= sampletime) && !sgt.interrupt) { + snd_generate(sample); + if(rate) + aud_push(&handle, sample); + if(sgt.capture) + wav_write16(&sgt.wav, sample, 1); + samples++; + elapsed -= sampletime; + } + } + if(rate) { + aud_flush(&handle); + aud_close(&handle); + } + if(sgt.mid.track) + free(sgt.mid.track); + if(sgt.chip) { + free(sgt.bank); + free(sgt.chip); + } + sgt.query = rate; + sgt.waiting = 1; +} + +void snd_instruct(const void *buf, uint size, short op, short addr, uint param) { + if(sgt.disabled) + return; + sgt.interrupt = 1; + while(sgt.cmd_queue == SND_QUEUE) { + ; + } + pthread_mutex_lock(&sgt.lock); + sgt.interrupt = 1; + snd_cmd_t *cmd = &sgt.cmds[sgt.cmd_push]; + cmd->command = op; + cmd->address = addr; + cmd->param = param; + if(buf) { + cmd->buffer = malloc(size); + memcpy(cmd->buffer, buf, size); + } + else { + cmd->buffer = NULL; + } + if(++(sgt.cmd_push) == SND_QUEUE) + sgt.cmd_push = 0; + sgt.cmd_queue += 1; + pthread_mutex_unlock(&sgt.lock); +} + +uint snd_query(short addr, uint param) { + if(sgt.disabled || sgt.waiting) + return 0; + sgt.waiting = 1; + snd_instruct(NULL, 0, SND_CMD_QUERY, addr, param); + while(sgt.waiting) { + ; + } + return sgt.query; +} + +uint snd_init(const char *devname, uint samplerate, uint width, uint frames, uint bufsize, byte disabled) { + if(disabled) { + sgt.disabled = 1; + return 0; + } + snd_devconfig snd_cfg; + snd_cfg.devname = devname; + snd_cfg.samplerate = samplerate; + snd_cfg.width = ((width == 32) ? 32 : 16) / 8; + snd_cfg.frames = frames; + snd_cfg.bufsize = bufsize; + pthread_mutex_init(&sgt.lock, NULL); + pthread_mutex_init(&sgt.log_lock, NULL); + pthread_create(&sgt.thread, NULL, snd_loop, &snd_cfg); + return snd_query(SND_STAT_RUN, 0); +} + +uint snd_end() { + if(sgt.disabled) + return 0; + snd_instruct(NULL, 0, SND_CMD_STOP, 0, 0); + while(sgt.waiting ^ 1) { + ; + } + pthread_mutex_destroy(&sgt.lock); + pthread_mutex_destroy(&sgt.log_lock); + return sgt.query; +} diff --git a/stb_timage.h b/stb_timage.h new file mode 100644 index 0000000..fff7d3e --- /dev/null +++ b/stb_timage.h @@ -0,0 +1,2112 @@ +// stb_image 2.28_tiny + stb_image_write 1.16_tiny - modified by sen - public domain image loader + writer - http://nothings.org/stb + +// #ifndef STBI_STDMEM +#define STBI_MALLOC(s) mem_alloc(s, MEM_IMAGE) +#define STBI_REALLOC(p, s) mem_realloc(p, s, MEM_IMAGE) +#define STBI_FREE(p) mem_free(p) +// #else +// #define STBI_MALLOC(s) malloc(s) +// #define STBI_REALLOC(p, s) realloc(p, s) +// #define STBI_FREE(p) free(p) +// #endif +#define STBI_MEMMOVE(a,b,sz) memmove(a,b,sz) + +#define STBI_MAX_DIMENSIONS (1 << 24) +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet +// stbi__err - error +// stbi__errpuc - error returning pointer to byte +#define stbi__errpuc(x,y) ((byte *)(ulong) (stbi__err(x,y)?NULL:NULL)) +#define STBI__BYTECAST(x) ((byte) ((x) & 255)) // truncate int to byte without warnings +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBI__BYTECAST(a),(o)[1]=STBI__BYTECAST(b),(o)[2]=STBI__BYTECAST(c),(o)[3]=STBI__BYTECAST(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBI_FREE(stbiw__sbraw(a)),0 : 0) +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) +#define stbiw__ZHASH 16384 + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + byte buffer_start[128]; + int callback_already_read; + + byte *img_buffer, *img_buffer_end; + byte *img_buffer_original, *img_buffer_original_end; +} stbi__context; + +typedef struct +{ + int bits_per_channel; + int num_channels; +} stbi__result_info; + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + ushort fast[1 << STBI__ZFAST_BITS]; + ushort firstcode[16]; + int maxcode[17]; + ushort firstsymbol[16]; + byte size[STBI__ZNSYMS]; + ushort value[STBI__ZNSYMS]; +} stbi__zhuffman; + +typedef struct +{ + byte *zbuffer, *zbuffer_end; + int num_bits; + uint code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +typedef struct +{ + uint length; + uint type; +} stbi__pngchunk; + +typedef struct +{ + stbi__context *s; + byte *idata, *expanded, *out; + int depth; +} stbi__png; + +static const char *stbi__g_failure_reason; +static const char *stbi__g_failure_desc; +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_force_png_filter = -1; + +// get a VERY brief reason for failure +static const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +// get a VERY brief reason for failure (user message) +static const char *stbi_failure_desc(void) +{ + return stbi__g_failure_desc; +} + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +static void stbi_set_unpremultiply_on_load(int should_unpremultiply) +{ + stbi__unpremultiply_on_load = should_unpremultiply; +} + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +static void stbi_convert_iphone_png_to_rgb(int should_convert) +{ + stbi__de_iphone_flag = should_convert; +} + +static void stbi_set_png_compression_level(int png_compression_level) +{ + stbi_write_png_compression_level = png_compression_level; +} + +static void stbi_set_force_png_filter(int force_png_filter) +{ + stbi_write_force_png_filter = force_png_filter; +} + +static int stbi__err(const char *reason, const char *desc) +{ + stbi__g_failure_reason = reason; + stbi__g_failure_desc = desc; + return 0; +} + +// ZLIB client - used by PNG, available for other purposes + +static char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +static char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +static char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +static int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +static char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +static int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, byte const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (byte *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (byte *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, stbi__result_info *ri); + +static void *stbi__malloc(ulong size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, stbi__result_info *ri) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->num_channels = 0; + + if (stbi__png_test(s)) return stbi__png_load(s,x,y, ri); + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static byte *stbi__convert_16_to_8(ushort *orig, int w, int h) +{ + int i; + int img_len = w * h * 4; + byte *reduced; + + reduced = (byte *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (byte)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static void stbi__vertical_flip(void *image, int w, int h) +{ + int row; + ulong bytes_per_row = (ulong)w * 4 * sizeof(byte); + byte temp[2048]; + byte *bytes = (byte *)image; + + for (row = 0; row < (h>>1); row++) { + byte *row0 = bytes + row*bytes_per_row; + byte *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + ulong bytes_left = bytes_per_row; + while (bytes_left) { + ulong bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +static byte *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, byte flip) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, &ri); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + sys_assert(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((ushort *) result, *x, *y); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (flip) { + stbi__vertical_flip(result, *x, *y); + } + + return (byte *) result; +} + +static byte *stbi_load(FILE *f, int *x, int *y, byte flip) +{ + byte *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y, flip); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +static byte *stbi_load_mem(byte const *buffer, int len, int *x, int *y, byte flip) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y, flip); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +static byte stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, byte *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static uint stbi__get32be(stbi__context *s) +{ + uint z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static byte stbi__compute_y(int r, int g, int b) +{ + return (byte) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static byte *stbi__convert_format(byte *data, int img_n, uint x, uint y) +{ + int i,j; + byte *good; + + if (4 == img_n) return data; + + good = (byte *) stbi__malloc_mad3(4, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + byte *src = data + j * x * img_n ; + byte *dest = good + j * x * 4; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with 4 components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, 4)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: sys_assert(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static ushort stbi__compute_y_16(int r, int g, int b) +{ + return (ushort) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static ushort *stbi__convert_format16(ushort *data, int img_n, uint x, uint y) +{ + int i,j; + ushort *good; + + if (4 == img_n) return data; + + good = (ushort *) stbi__malloc(4 * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (ushort *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + ushort *src = data + j * x * img_n ; + ushort *dest = good + j * x * 4; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with 4 components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, 4)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: sys_assert(0); STBI_FREE(data); STBI_FREE(good); return (ushort*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + + +static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +static int stbi__bit_reverse(int v, int bits) +{ + sys_assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const byte *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (ushort) code; + z->firstsymbol[i] = (ushort) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + ushort fastv = (ushort) ((s << 9) | i); + z->size [c] = (byte ) s; + z->value[c] = (ushort) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + + +static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +static byte stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (uint) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +static uint stbi__zreceive(stbi__zbuf *z, int n) +{ + uint k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an sys_assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + uint cur, limit; // , old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (uint) (z->zout - z->zout_start); + limit = /* old_limit = */ (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC(z->zout_start, limit); + // STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + byte *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (byte *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + byte v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const byte length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + byte lencodes[286+32+137];//padding for maximum single op + byte codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (byte) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (byte) c; + else { + byte fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + byte header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (byte) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const byte stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const byte stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +static char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (byte *) buffer; + a.zbuffer_end = (byte *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +static char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +static char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (byte *) buffer; + a.zbuffer_end = (byte *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +static int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (byte *) ibuffer; + a.zbuffer_end = (byte *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +static char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (byte *) buffer; + a.zbuffer_end = (byte *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +static int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (byte *) ibuffer; + a.zbuffer_end = (byte *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const byte png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static byte first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const byte stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, byte *raw, uint raw_len, int out_n, uint x, uint y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + uint i,j,stride = x*out_n*bytes; + uint img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + sys_assert(out_n == s->img_n || out_n == s->img_n+1); + a->out = (byte *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + byte *cur = a->out + stride*j; + byte *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + sys_assert(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + byte *cur = a->out + stride*j; + byte *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + byte scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + sys_assert(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + byte *cur = a->out; + ushort *cur16 = (ushort*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, byte *image_data, uint image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + byte *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (byte *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + uint img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, byte tc[3], int out_n) +{ + stbi__context *s = z->s; + uint i, pixel_count = s->img_x * s->img_y; + byte *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + sys_assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, ushort tc[3], int out_n) +{ + stbi__context *s = z->s; + uint i, pixel_count = s->img_x * s->img_y; + ushort *p = (ushort*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + sys_assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, byte *palette, int pal_img_n) +{ + uint i, pixel_count = a->s->img_x * a->s->img_y; + byte *p, *temp_out, *orig = a->out; + + p = (byte *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + // STBI_NOTUSED(len); + + return 1; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + uint i, pixel_count = s->img_x * s->img_y; + byte *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + byte t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + sys_assert(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + byte a = p[3]; + byte t = p[0]; + if (a) { + byte half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + byte t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + + +static int stbi__parse_png_file(stbi__png *z) +{ + byte palette[1024], pal_img_n=0; + byte has_trans=0, tc[3]={0}; + ushort tc16[3]; + uint ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + // if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + // if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + // if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (ushort)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (byte)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + // if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + // if (pal_img_n) + // s->img_n = pal_img_n; + // return 1; + // } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + // uint idata_limit_old = idata_limit; + byte *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + // STBI_NOTUSED(idata_limit_old); + p = (byte *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + uint raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + // if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (byte *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((4 == s->img_n+1 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + // s->img_out_n = pal_img_n; + s->img_out_n = 4; + if (!stbi__expand_png_palette(z, palette, /* pal_len, */ s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, stbi__result_info *ri) +{ + void *result=NULL; + if (stbi__parse_png_file(p)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (4 != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((byte *) result, p->s->img_out_n, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((ushort *) result, p->s->img_out_n, p->s->img_x, p->s->img_y); + p->s->img_out_n = 4; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBI_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, /* *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, */ itemsize * m + sizeof(int)*2); + sys_assert(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static byte *stbiw__zlib_flushf(byte *data, uint *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBI__BYTECAST(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static uint stbiw__zlib_countm(byte *a, byte *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static uint stbiw__zhash(byte *data) +{ + uint hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static byte * stbi_zlib_compress(byte *data, int data_len, int *out_len, int quality) +{ + static ushort lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static byte lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static ushort distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static byte disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + uint bitbuf=0; + int i,j, bitcount=0; + byte *out = NULL; + byte ***hash_table = (byte***) STBI_MALLOC(stbiw__ZHASH * sizeof(byte**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + byte *bestloc = 0; + byte **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBI_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + sys_assert(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBI_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBI__BYTECAST(blocklen)); // LEN + stbiw__sbpush(out, STBI__BYTECAST(blocklen >> 8)); + stbiw__sbpush(out, STBI__BYTECAST(~blocklen)); // NLEN + stbiw__sbpush(out, STBI__BYTECAST(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + uint s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBI__BYTECAST(s2 >> 8)); + stbiw__sbpush(out, STBI__BYTECAST(s2)); + stbiw__sbpush(out, STBI__BYTECAST(s1 >> 8)); + stbiw__sbpush(out, STBI__BYTECAST(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBI_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (byte *) stbiw__sbraw(out); +} + +static uint stbiw__crc32(byte *buffer, int len) +{ + static uint crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + uint crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +static void stbiw__wpcrc(byte **data, int len) +{ + uint crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static byte stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBI__BYTECAST(a); + if (pb <= pc) return STBI__BYTECAST(b); + return STBI__BYTECAST(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(byte *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, char *line_buffer, byte flip) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + byte *z = pixels + stride_bytes * (flip ? height-1-y : y); + int signed_stride = flip ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +// For PNG, "stride_in_bytes" is the distance in bytes from the first byte of +// a row of pixels to the first byte of the next row of pixels. +static byte *stbi_write_png_to_mem(const byte *pixels, int stride_bytes, int x, int y, int n, int *out_len, byte flip) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + byte sig[8] = { 137,80,78,71,13,10,26,10 }; + byte *out,*o, *filt, *zlib; + char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (byte *) STBI_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (char *) STBI_MALLOC(x * n); if (!line_buffer) { STBI_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((byte*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer, flip); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((byte*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer, flip); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((byte*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer, flip); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (byte) filter_type; + STBI_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBI_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBI_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (byte *) STBI_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBI_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBI__BYTECAST(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBI_MEMMOVE(o, zlib, zlen); + o += zlen; + STBI_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + sys_assert(o == out + *out_len); + + return out; +} diff --git a/str.h b/str.h new file mode 100644 index 0000000..0d4acda --- /dev/null +++ b/str.h @@ -0,0 +1,239 @@ + +int str_time(char *str, ulong time) { + ulong secs = (time /= 1000000ULL) % 60ULL; + ulong mins = (time /= 60ULL) % 60ULL; + ulong hrs = (time /= 60ULL) % 24ULL; + time /= 24ULL; + return time ? sprintf(str, "%dD+%02d:%02d:%02d", time, hrs, mins, secs) : sprintf(str, "%02d:%02d:%02d", hrs, mins, secs); +} + +int str_mtime(char *str, ulong time) { + int pos = str_time(str, time); + return pos + sprintf(str+pos, ".%03d", (time / 1000ULL) % 1000ULL); +} + +int str_utime(char *str, ulong time) { + int pos = str_time(str, time); + return pos + sprintf(str+pos, ".%03d+%03d", (time / 1000ULL) % 1000ULL, time % 1000ULL); +} + +int str_strip(const char *str, char *buf, int size, int len, char newl, char tab, char unk) { + int off = 0; + int pos = 0; + char c; + while(((len < 0) || (off < len)) && (c = str[off++]) && (pos < size - 1)) { + if(c == '\n') { + buf[pos++] = newl; + if(!newl) { + return pos - 1; + } + } + else if((c == '\t') && tab) { + for(int z = 0; (z < 4) && (pos < size - 1); z++) + buf[pos++] = tab; + } + else if(c == CHR_UNK) { + if(unk) + buf[pos++] = unk; + } + else if((c < 0) || (c >= CHR_SPC)) { + buf[pos++] = c; + } + } + buf[pos] = 0; + return pos; +} + +// void str_stripq(char *str) { + // char c; + // int pos = 0; + // while(c = str[pos]) { + // if((c >= 0) && (c < CHR_SPC)) { + // str[pos] = '\x1f'; + // } + // pos++; + // } +// } + +uint utf_read(const char **ptr) { + uint ch; + byte utf; + char c = *((*ptr)++); + for(utf = 0; (utf < 6) && (c & (0x80 >> utf)); utf++) { + ; + } + if(utf == 1) + return CHR_UNK; + for(ch = ((!utf) || ((((uint)c) << 6) | (((**ptr) & 0x3f) & ~(0xff >> utf)))) ? (c & (0x7f >> utf)) : 0; utf > 1; utf--) { + if(((c = **ptr) & 0xc0) == 0x80) { + // if(ch) { + ch <<= 6; + ch |= c & 0x3f; + // } + } + else { + return c ? CHR_UNK : 0; + } + (*ptr)++; + } + // fprintf(stderr, "%d / %c\n", ch, ch); + return (utf && !ch) ? CHR_UNK : ch; +} + +uint utf_readn(const char *ptr, int *pos) { + const char *str = &ptr[*pos]; + uint ch = utf_read(&str); + *pos = (int)(str-ptr); + return ch; +} + +byte utf_write(char **ptr, int *len, uint ch) { + uint test; + uint mask = 0xffffff80; + char utf; + char c; + if(ch & 0x80000000) { + return 1; + } + for(utf = 0; ch & mask & ~(test = (0xffffffff << ((utf + 1) * 6 + (5 - utf)))); utf++) { + mask &= test; + } + // fprintf(stderr, "%d\n", utf); + if(utf + 1 >= (*len)) { + return 0; + } + (*len) -= utf + 1; + *((*ptr)++) = utf ? (~(0x7f >> utf) | (((uint)(ch >> (utf * 6))) & (0x3f >> utf))) : ch; + for(--utf; utf >= 0; utf--) { + *((*ptr)++) = 0x80 | (((uint)(ch >> (utf * 6))) & 0x3f); + } + return 1; +} + +byte utf_rwriten(char *ptr, int *pos, int len, uint ch) { + char *str = &ptr[*pos]; + len -= (*pos); + byte res = utf_write(&str, &len, ch); + *pos = (int)(str-ptr); + return res; +} + +uint utf_rread(const char **ptr, const char *start) { + const char *tp = *ptr; + uint ch; + char c; + do { + if(tp == start) + return 0; + tp--; + } while(((c = (*tp)) & 0xc0) == 0x80); + *ptr = tp; + return (ch = utf_read(&tp)) ? ch : CHR_UNK; +} + +uint utf_rreadn(const char *ptr, int *pos) { + const char *str = &ptr[*pos]; + uint ch = utf_rread(&str, ptr); + *pos = (int)(str-ptr); + return ch; +} + +int utf_len(const char *str) { + int len; + for(len = 0; utf_read(&str); len++) { + ; + } + return len; +} + +char str_lower(char c) { + return ((c >= 'A') && (c <= 'Z')) ? (c + 32) : c; +} + +char str_upper(char c) { + return ((c >= 'a') && (c <= 'z')) ? (c - 32) : c; +} + +byte str_eq_lower(const char *str1, const char *str2) { + int pos = 0; + char c; + while(c = str_lower(str1[pos])) { + if(str_lower(str2[pos++]) != c) + return 0; + } + return str2[pos] ? 0 : 1; +} + +int str_cmp_lower(const char *str1, const char *str2) { + int pos = 0; + char c; + while(c = str_lower(str2[pos])) { + if(str_lower(str1[pos++]) != c) + return 0; + } + return str1[pos] ? pos : 0x7fffffff; +} + +byte str_parse_int(const char *str, int *value, int base) { + char c; + byte tbase = 0; + int pos = 0; + int digits = 0; + long v = 0; + long nbase = base ? (base < 0 ? -base : base) : 10; + long nsign = 1; + while(c = str_lower(str[pos++])) { + if(pos == 1 && ((c == '+') || ((c == '-') && (base >= 0)))) + nsign = (c == '+') ? 1 : -1; + else if(pos == 1 && c == '0') { + tbase = 1; + digits++; + } + else if(tbase && pos == 2 && c == 'x') { + nbase = 16; + digits--; + } + else if(((nbase == 16) && (c >= 'a' && c <= 'f')) || (c >= '0' && c <= '9')) { + v *= nbase; + v += ((long)((c >= '0' && c <= '9') ? (c - '0') : (10 + (c - 'a')))); + digits++; + } + else + return 0; + } + if(!digits) + return 0; + v *= nsign; + // errno = 0; + // long v = strtol(str, NULL, (base < 0) ? -base : base); + if((base < 0) ? (v < 0LL || v > 0xffffffffLL) : (v < -0x80000000LL || v > 0x7fffffffLL)) { + return 0; + } + *value = (int)((base < 0) ? (v & 0xffffffff) : (((v >> 32) & 0x80000000) | (v & 0x7fffffff))); + return 1; +} + +byte str_parse_enum(const char *str, const char **names, int states, int *value) { + int comp; + int max = 0; + int best = 0; + if(str_parse_int(str, &comp, 0) && (comp >= 0) && (comp < states)) { + *value = comp; + return 1; + } + for(int z = 0; z < states; z++) { + if((comp = str_cmp_lower(names[z], str)) > best) { + max = comp; + best = z; + } + } + if(max) { + *value = best; + return 1; + } + return 0; +} + +// char char_to_dc32(char c) { +// +// } diff --git a/strdraw.h b/strdraw.h new file mode 100644 index 0000000..3e55e0e --- /dev/null +++ b/strdraw.h @@ -0,0 +1,195 @@ + +#ifdef GFX_TEXT_COORD +int txt_offset(int *xc, int *yc, int ox, int oy, int x, int y, int h, int x1, int y1, int x2, int y2, font_t *font, const char *str) { +#undef GFX_TEXT_COORD +#define GFX_TEXT_OFFSET +#endif + +#ifdef GFX_TEXT_SIZE +void txt_coord(int *xc, int *yc, int offset, int x, int y, int h, int x1, int y1, int x2, int y2, font_t *font, const char *str) { +#undef GFX_TEXT_SIZE +#define GFX_TEXT_COORD +#endif + +#ifdef GFX_TEXT_DRAW_R +void txt_size(int *xs, int *ys, int x, int y, int w, int h, int l, int x1, int y1, int x2, int y2, font_t *font, const char *str) { +#undef GFX_TEXT_DRAW_R +#define GFX_TEXT_SIZE +#endif + +#ifdef GFX_TEXT_DRAW +byte txt_draw_range(int start, int end, int x, int y, int w, int h, int l, int x1, int y1, int x2, int y2, uint color, uint back, font_t *font, const char *str) { +#define GFX_TEXT_DRAW_R +#endif + +#ifndef GFX_TEXT_BASE +byte txt_draw(int x, int y, int w, int h, int l, int x1, int y1, int x2, int y2, uint color, uint back, font_t *font, const char *str) { +#define GFX_TEXT_BASE +#define GFX_TEXT_DRAW +#endif + +#ifdef GFX_TEXT_SIZE + int ix = x; + int iy = y; +#endif +#if defined(GFX_TEXT_OFFSET) || defined(GFX_TEXT_COORD) + int w = 0; + int l = h; +#endif + int tx, ty, u, v; + fchar_t glyph; + uint tex; + uint ch; +#if defined(GFX_TEXT_OFFSET) || defined(GFX_TEXT_COORD) || defined(GFX_TEXT_DRAW_R) + int pos = 0; + int lpos = 0; +#endif +#ifdef GFX_TEXT_DRAW + byte flags; + byte sflags; +#endif + x = (w && l < 0) ? x+l : x; + y = (h && l < 0) ? y+l : y; +#ifdef GFX_TEXT_OFFSET + if(oy < y) { + return 0; + } + ox = ox < x1 ? x1 : ox; +#endif +#ifdef GFX_TEXT_DRAW + shd_sel(gdr.shd_text, NULL); + shd_vec2v("font_size", (float)font->xglyph, (float)font->yglyph); + shd_int("flags", sflags = flags = 0); + shd_color("color", color); + shd_color("back", back); +#endif +#if defined(GFX_TEXT_OFFSET) || defined(GFX_TEXT_COORD) || defined(GFX_TEXT_DRAW_R) + while(1) { + pos = lpos; + if(!(ch = utf_readn(str, &lpos))) + break; +#else + while(ch = utf_read(&str)) { +#endif +#ifdef GFX_TEXT_COORD + if(pos == offset) { + *xc = x; + *yc = y; + return; + } +#endif + if(ch == CHR_NLN) { +#ifdef GFX_TEXT_OFFSET + if(ox >= x && oy >= y && oy < (y + h)) { + *xc = x; + *yc = y; + return pos; + } +#endif + x = w ? (x+l) : ((h < 0) ? x2 : x1); + y = h ? (y+l) : ((w < 0) ? y2 : y1); + continue; + } +#ifdef GFX_TEXT_DRAW + else if(ch >= CHR_ULINE && ch <= CHR_FADE) { + shd_int("flags", flags ^= 1 << (ch - CHR_ULINE)); + sflags |= flags; + continue; + } + else if(ch >= CHR_COLORS1 && ch <= CHR_COLORE1) { + shd_color("color", text_colors[ch - CHR_COLORS1] | (color & 0xff000000)); + continue; + } + else if(ch >= CHR_COLORS2 && ch <= CHR_COLORE2) { + shd_color("color", gdr.aux_colors[ch - CHR_COLORS2] | (color & 0xff000000)); + continue; + } + else if(ch == CHR_FRESET) { + shd_int("flags", flags = 0); + continue; + } + else if(ch == CHR_CRESET) { + shd_color("color", color); + continue; + } +#else + else if(ch < CHR_SPC) { + continue; + } +#endif + if(ch >= (UNI_MAX_PAGES * 256) || !(tex = font->textures[ch >> 8])) + ch = CHR_UNK; + glyph = font->sizes[ch >> 8][ch & 0xff]; + if((!(glyph.u)) && glyph.v) + continue; + else if(!(glyph.u)) + glyph = font->sizes[0][CHR_UNK]; + u = h ? ((glyph.u + 3 - glyph.s) * (h < 0 ? -h : h) / font->yglyph) : (w < 0 ? -w : w); + v = w ? ((glyph.v + 3 - glyph.t) * (w < 0 ? -w : w) / font->xglyph) : (h < 0 ? -h : h); + tx = (h < 0) ? (((x - u) < x1) ? x2 : x) : ((h > 0) ? (((x + u) > x2) ? x1 : x) : ((w < 0) ? (((y - v) < y1) ? x+l : x) : ((w > 0) ? (((y + v) > y2) ? x+l : x) : x))); + ty = (w < 0) ? (((y - v) < y1) ? y2 : y) : ((w > 0) ? (((y + v) > y2) ? y1 : y) : ((h < 0) ? (((x - u) < x1) ? y+l : y) : ((h > 0) ? (((x + u) > x2) ? y+l : y) : y))); +#ifdef GFX_TEXT_OFFSET + if(ty > y && ox >= x && oy >= y && oy < (y + h)) { + *xc = tx; + *yc = ty; + return pos; + } +#endif + x = tx; + y = ty; + if(x < x1 || y < y1 || x > x2 || y > y2) { +#ifdef GFX_TEXT_OFFSET + pos = lpos; +#endif + break; + } +#ifdef GFX_TEXT_DRAW +#ifdef GFX_TEXT_DRAW_R + if(pos >= start && pos < end) { +#endif + shd_vec2v("offset", (float)(h < 0 ? (x - u) : x), (float)(w < 0 ? (y - v) : y)); + shd_vec4v("pad", (float)(h < 0 ? 2 : (h > 0 ? 1 : 0)), (float)(w < 0 ? 2 : (w > 0 ? 1 : 0)), (float)(h > 0 ? 2 : (h < 0 ? 1 : 0)), (float)(w > 0 ? 2 : (w < 0 ? 1 : 0))); + shd_vec2v("size", (float)u, (float)v); + shd_vec4v("glyph", (float)(h ? glyph.s : 0), (float)(w ? glyph.t : 0), (float)(h ? glyph.u : font->xglyph), (float)(w ? glyph.v : font->yglyph)); + shd_int("ch_index", ch & 0xff); + glBindTexture(GL_TEXTURE_2D, font->textures[ch >> 8]); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +#ifdef GFX_TEXT_DRAW_R + } +#endif +#endif +#ifdef GFX_TEXT_OFFSET + if(ox >= x && oy >= y && ox < (x + u) && oy < (y + h)) { + *xc = x; + *yc = y; + return pos; + } +#endif + x += ((h < 0) ? -u : ((h > 0) ? u : 0)); + y += ((w < 0) ? -v : ((w > 0) ? v : 0)); + } +#ifdef GFX_TEXT_SIZE + *xs = x - ix; + *ys = y - iy; +#endif +#ifdef GFX_TEXT_OFFSET + *xc = x; + *yc = y; + return pos; +#endif +#ifdef GFX_TEXT_COORD + *xc = x; + *yc = y; +#endif +#ifdef GFX_TEXT_DRAW + return sflags & (SHD_FLAG_BL | SHD_FLAG_FD); +#endif +} + +#ifdef GFX_TEXT_OFFSET +#undef GFX_TEXT_OFFSET +#endif + +#ifdef GFX_TEXT_DRAW_R +#undef GFX_TEXT_DRAW +#endif diff --git a/strings.h b/strings.h new file mode 100644 index 0000000..a389352 --- /dev/null +++ b/strings.h @@ -0,0 +1,482 @@ + +STR(MEM_ALLOC, "lld llx lld s", "$1 Bytes wurden bei $$$2 alloziert, $3 Bytes in Verwendung ($4)") +STR(MEM_FREE, "lld llx lld s", "$1 Bytes wurden bei $$$2 freigegeben, $3 Bytes in Verwendung ($4)") +STR(MEM_REALLOC, "lld lld llx lld s", "$1 Bytes wurden auf $2 Bytes bei $$$3 neu alloziert, $4 Bytes in Verwendung ($5)") +STR(MEM_PEAK, "lld", "Höchster Speicherverbrauch: $1 Bytes") +STR(MEM_LEAK, "lld", "Speicher wurde nicht vollständig freigeben! Verbleibend: $1 Bytes") +STR(STARTUP, "", "Starte ...") +STR(SHUTDOWN, "", "Beende ...") +STR(EXIT, "", "Beendet.") +STR(BUILD_COMPILER, "s", "Kompiliert mit $1") +STR(BUILD_SYSTEM, "s", "Auf $1") +STR(OGL_ERR, "d s", "OpenGL-Fehler $1 in $2") +STR(OGL_LOADER_ERR, "", "Konnte OpenGL-Funktionen nicht laden") +STR(WIN_CREATE_ERR, "", "Konnte kein Fenster für das Programm erstellen") +STR(FILE_OPENERR, "s s d", "Fehler beim Öffnen von Datei '$1': $2 ($3)") +#define STR_FILE_READERR "Fehler beim Lesen von Datei '$1': " +STR(FILE_EIO, "s", STR_FILE_READERR "E/A-Fehler") +STR(FILE_EEOF, "s", STR_FILE_READERR "Ende der Datei erreicht") +STR(FILE_EGEN, "s s d", STR_FILE_READERR "$2 ($3)") +STR(FILE_ESHORT, "s", STR_FILE_READERR "Datei zu klein") +STR(FILE_EEMPTY, "s", STR_FILE_READERR "Datei ist leer") +STR(FILE_EMFHDR, "s", STR_FILE_READERR "Fehlerhafter Dateiheader oder falscher Dateityp") +STR(SHD_EVERTEX, "s", "Fehler beim Kompilieren des Vertex-Shaders '$1'") +STR(SHD_EFRAGMENT, "s", "Fehler beim Kompilieren des Fragment-Shaders '$1'") +STR(SHD_EPROGRAM, "s s", "Fehler beim Verbinden des Shader-Programms '$1' / '$2'") +STR(SHD_ELOAD, "s", "Konnte erforderliches Shader-Programm '$1' nicht erstellen") +// STR(SHD_EFULL, "s s d", "Konnte Shader '$1' / '$2' nicht hinzufügen: Alle $3 Shader sind belegt") +STR(TEX_EOPEN, "s s d", "Fehler beim Öffnen der Textur '$1': $2 ($3)") +STR(TEX_ELOAD, "s s s", "Fehler beim Laden der Textur '$1': $2 ($3)") +// STR(TEX_EFULL, "s d", "Konnte Textur '$1' nicht hinzufügen: Alle $2 Texturen sind belegt") +STR(TEX_EWOPEN, "s s d", "Fehler beim Öffnen von Bilddatei '$1' zum Schreiben: $2 ($3)") +STR(TEX_EWRITE, "s", "Fehler beim Schreiben nach Bilddatei '$1': E/A-Fehler") +STR(TEX_ESAVE, "s", "Konnte PNG-Daten für Bilddatei '$1' nicht enkodieren") +STR(OGL_COMPAT, "s", "Eine Standard-OpenGL-Version ist erforderlich. Aktuell: $1") +STR(OGL_OLD, "d d", "Eine verfügbare OpenGL-Version ab 4.6 oder höher ist erforderlich. Aktuell: $1.$2") +STR(OGL_LOADED, "d", "$1 OpenGL-Funktionen wurden geladen") +// STR(SHD_ILOADED, "s d", "Interner Shader '$1' wurde mit ID #$2 geladen") +// STR(SHD_IUNLOADED, "d", "Interner Shader mit ID #$1 wurde gelöscht") +// STR(SHD_IREASSIGN, "d d", "Interner Shader mit ID #$1 wurde mit ID #$2 neu geladen") +STR(SHD_LOADED, "s s d", "Shader '$1' / '$2' wurde mit ID #$3 geladen") +STR(SHD_UNLOADED, "s s d", "Shader '$1' / '$2' / ID #$3 wurde gelöscht") +STR(SHD_REASSIGN, "s s d d", "Shader '$1' / '$2' / ID #$3 wurde mit ID #$4 neu geladen") +STR(SHD_REFRESH, "d", "$1 Shader wurden neu geladen") +STR(BUF_ILOADED, "d d d d", "Interner Puffer mit Größe $1 ($2F) wurde mit VBO #$3 / VAO #$4 geladen") +STR(BUF_IUNLOADED, "d d", "Interner Puffer mit VBO #$1 / VAO #$2 wurde gelöscht") +STR(BUF_LOADED, "d d d d", "Puffer mit Größe $1 ($2F) wurde mit VBO #$3 / VAO #$4 geladen") +STR(BUF_UNLOADED, "d d d d", "Puffer mit Größe $1 ($2F) / VBO #$3 / VAO #$4 wurde gelöscht") +// STR(BUF_EFULL, "d", "Konnte Puffer nicht hinzufügen: Alle $1 Puffer sind belegt") +STR(BUF_RESIZED, "d d d d d", "Puffer mit VBO #$1 / VAO #$2 wurde von Größe $3 auf $4 ($5F) geändert") +STR(TEX_ILOADED, "s d", "Interne Textur '$1' wurde mit ID #$2 geladen") +STR(TEX_IUNLOADED, "d", "Interne Textur mit ID #$1 wurde gelöscht") +STR(TEX_LOADED, "s d", "Textur '$1' wurde mit ID #$2 geladen") +STR(TEX_UNLOADED, "s d", "Textur '$1' / ID #$2 wurde gelöscht") +STR(TEX_PARAMS, "d", "$1 Textur-Parameter wurden gesetzt") +STR(FBO_ADDED, "d d", "Bildpuffer #$1 / Textur #$2 wurden erstellt") +STR(FBO_ADDED_RBO, "d d d", "Bildpuffer #$1 / Textur #$2 / RBO #$3 wurden erstellt") +STR(FBO_DELETED, "d d", "Bildpuffer #$1 / Textur #$2 wurden gelöscht") +STR(FBO_DELETED_RBO, "d d d", "Bildpuffer #$1 / Textur #$2 / RBO #$3 wurden gelöscht") +STR(FBO_INCOMPLETE, "d d", "Bildpuffer #$1 ist unvollständig ($2)") +// STR(FBO_EFULL, "d", "Konnte Bildpuffer nicht hinzufügen: Alle $1 Bildpuffer sind belegt") +STR(FBO_RESIZE, "d", "$1 Bildpuffer wurden neu alloziert") +STR(MSG_SCREENSHOT, "s", "Bildschirmfoto als '$1' gespeichert") +STR(ON, "", "An") +STR(OFF, "", "Aus") +STR(RESET, "", "Zurücksetzen") +STR(APPLY, "", "Übernehmen") +STR(BACK, "", "Zurück") +STR(CLOSE, "", "Menü Schließen") +STR(QUIT, "", "Beenden") +STR(CVAR_EWOPEN, "s s d", "Fehler beim Öffnen von Konfigurationsdatei '$1' zum Schreiben: $2 ($3)") +STR(CVAR_INVALID, "s s", "Kann CVAR '$1' nicht auf '$2' setzen") +STR(CVAR_READONLY, "s", "CVAR '$1' kann nicht geändert werden") +STR(CVAR_UNKNOWN, "s", "CVAR '$1' existiert nicht") +STR(CVAR_MOREARGS, "d", "Zu viele argumente für CVAR, höchstens 1 erlaubt, $1 wurden angegeben") +STR(CVAR_CCMD_UNKNOWN, "s", "CVAR oder Befehl '$1' existiert nicht") +STR(CCMD_UNKNOWN, "s", "Befehl '$1' existiert nicht") +STR(CCMD_LESSARGS, "s d d", "Befehl '$1' benötigt mindestens $2 Argumente, $3 wurden angegeben") +STR(CCMD_MOREARGS, "s d d", "Befehl '$1' benötigt höchstens $2 Argumente, $3 wurden angegeben") +STR(CMD_CVARLIST, "d d", "CVARs insgesamt registriert: $1 ($2 nur lesbar)") +STR(CMD_CCMDLIST, "d", "Befehle insgesamt registriert: $1") +STR(UNLIMITED, "", "Unbegrenzt") +STR(WIN_SYNC, "", "Max. Bildrate") +STR(WIN_VSYNC, "", "VSync") +STR(WIN_FLUSH, "", "Puffer vor Synch. leeren") +STR(WIN_FULLSCREEN, "", "Vollbildmodus") +STR(WIN_FULLRES, "", "Auflösung") +STR(GUI_DCLICK_DELAY, "", "Doppelklick bei") +STR(GUI_GENERAL, "", "Allgemein") +STR(GUI_OTHER, "", "Weiteres") +STR(GUI_BORDER_WIDTH, "", "Rahmenbreite") +STR(GUI_BORDER_COLOR, "", "Umrahmung") +STR(GUI_WBORDER_COLOR, "", "Fensterrahmen") +STR(GUI_FILL_COLOR, "", "Füllfarbe") +STR(GUI_TEXT_COLOR, "", "Textfarbe") +STR(GUI_SELCUR_COLOR, "", "Auswahl/Marke") +STR(GUI_BACK_COLOR, "", "Hintergrund") +STR(GUI_HOVER_COLOR, "", "Gewählt") +STR(GUI_PRESS_COLOR, "", "Gedrückt") +STR(GUI_DESC_LABEL, "", "Beschriftung") +STR(GUI_DESC_BUTTON, "", "Knopf") +STR(GUI_DESC_TOGGLE_OFF, "", "Schalter (aus)") +STR(GUI_DESC_FIELD, "", "Textfeld") +STR(GUI_DESC_WINDOW, "", "Titelleiste") +STR(GUI_SLIDER_WIDTH, "", "Griffbreite") + +STR(TEST, "", "Test") + +STR(THEME_BLUE, "", "Blau (Standard)") +STR(THEME_GRAY, "", "Grau") +STR(THEME_SHINE, "", "Glänzend") + +STR(GFX_FOV, "", "Sichtfeld (FOV)") +STR(GFX_LIGHT_MAX, "", "Max. Dyn. Lichtquellen") +STR(GFX_LIGHT_CHUNK, "", "Lichtq. Sichtweite") +STR(GFX_LIGHT_VIEW, "", "Entfernung Lichtq. -> Kamera") +STR(GFX_LIGHT_VERT, "", "Entfernung Lichtq. -> Mitte") +STR(GFX_LIGHT_BLEND, "", "Lichtüberdeckung") +STR(GFX_TEXFILTER, "", "Texturfilterung") +STR(GFX_MIPMAPS, "", "Mipmaps") +STR(GFX_MIPMAP_OFF, "", "Keine") +STR(GFX_MIPMAP_NEAREST, "", "Nächster nachbar") +STR(GFX_MIPMAP_LINEAR, "", "Linear interpoliert") +STR(GFX_ANISOTROPIC, "", "Anisotrope Filterung") +STR(GFX_DRAWDIST_H, "", "Horizontale Sichtw.") +STR(GFX_DRAWDIST_V, "", "Vertikale Sichtw.") +STR(GFX_AMBIENT, "", "Hintergrundlicht") +STR(GFX_AMBIENT_X, "", "Hintergrundlicht X") +STR(GFX_AMBIENT_Y, "", "Hintergrundlicht Y") +STR(GFX_AMBIENT_Z, "", "Hintergrundlicht Z") +STR(PHY_SENSITIVITY, "", "Mausempfindlichkeit") +STR(PHY_SPEED, "", "Geschwindigkeit") +STR(PHY_GRAVITY, "", "Schwerkraft") +STR(PHY_FRICTION, "", "Oberflächenreibung") + +STR(SND_START, "s d", "Audiogerät '$1' mit Abtastrate $2 Hz geöffnet") +STR(SND_STWAV, "s d s", "Audiogerät '$1' mit Abtastrate $2 Hz geöffnet, mit WAV-Aufnahme '$3'") +STR(SND_STARTERR, "s", "Audiogerät '$1' konnte nicht geöffnet werden") +STR(SND_STOP, "", "Audiogerät geschlossen") + +STR(WAV_OPNERR, "s s d", "Fehler beim Öffnen von WAV-Datei '$1': $2 ($3)") +#define STR_WAV_WRITERR "Fehler beim Schreiben nach WAV-Datei '$1': " +STR(WAV_EIO, "s", STR_WAV_WRITERR "E/A-Fehler") +STR(WAV_EGEN, "s s d", STR_WAV_WRITERR "$2 ($3)") + +STR(AUD_UNDERR, "d", "ALSA Puffer-Unterlauf! ($1)") +STR(AUD_WRITERR, "s d", "Fehler beim Schreiben von PCM: $1 ($2)") +STR(AUD_OPNERR, "s s d", "Fehler beim Öffnen von ALSA PCM-Gerät '$1': $2 ($3)") +STR(AUD_HWERR, "s s d", "Fehler beim Setzen von Hardware-Parametern von ALSA PCM-Gerät '$1': $2 ($3)") +STR(AUD_SWERR, "s s d", "Fehler beim Setzen von Software-Parametern von ALSA PCM-Gerät '$1': $2 ($3)") +STR(AUD_FMTERR, "s", "Fehler beim Setzen von Sample-Format von ALSA PCM-Gerät '$1': Kein Format verfügbar") +STR(AUD_FMTCH, "s d d", "ALSA PCM-Gerät '$1': Sample-Format S$2LE nicht verfügbar, setze auf S$3LE") +STR(AUD_PREPERR, "", "Fehler beim Vorbereiten des ALSA PCM-Geräts") + +STR(LOG_LEVEL_SILENT, "", "Nichts") +STR(LOG_LEVEL_USER, "", "Benutzer") +STR(LOG_LEVEL_ERROR, "", "Fehler") +STR(LOG_LEVEL_WARN, "", "Warnung") +STR(LOG_LEVEL_INFO, "", "Info") +STR(LOG_LEVEL_PERF, "", "Leistung") +STR(LOG_LEVEL_DEBUG, "", "*Debug*") +STR(LOG_LEVEL_TRACE, "", "*Trace*") + +STR(NOCLIP_ON, "", "NoClip eingeschaltet") +STR(NOCLIP_OFF, "", "NoClip ausgeschaltet") +STR(CAMERA_ON, "", "Kameramodus eingeschaltet") +STR(CAMERA_OFF, "", "Kameramodus ausgeschaltet") + +STR(TICK_TIMEOUT, "d d d", "Ticks benötigten $1 ms dieses Frame (maximal $2 ms), überspringe $3 Ticks") + +// STR(CON_AUTOCLOSE, "", "Schließen") +STR(CON_AUTOSCROLL, "", "Scrollen") +STR(CON_TIMESTAMPS, "", "Zeiten") +STR(CON_LOGLEVEL, "", "Ausgabe") +STR(CON_CLEAR, "", "Löschen") +STR(PARSER_ESC_UNKNOWN, "d c", "Unbekannte Sequenz bei Zeichen $1: '\\$2'") +STR(PARSER_ESC_SHORT, "d", "Unvollständinge Sequenz bei Zeichen $1") +STR(PARSER_NO_QUOTE, "d", "Nicht geschlossenes Anführungszeichen bei Zeichen $1") + +STR(OPL_CHANNEL, "", "Kanal") +STR(OPL_MODULATOR, "", "Operator 1 + 3 / Modulator") +STR(OPL_CARRIER, "", "Operator 2 + 4 / Träger") +STR(OPL_POINTER, "", "Stimmen-Zeiger") +STR(OPL_OUTPUT, "", "Pegel") +STR(OPL_MODE, "", "Modus/Al.") +STR(OPL_FEEDBACK, "", "Feedback") +STR(OPL_FREQUENCY, "", "Frequenz") +STR(OPL_FLAGS, "", "T-V-S-K") +STR(OPL_MULTIPLIER, "", "Frq.-Mult.") +STR(OPL_LEVEL, "", "Op.-Pegel") +STR(OPL_KEYSCALE, "", "Tst.-Skale") +STR(OPL_WAVEFORM, "", "Wellenf.") +STR(OPL_ATTACK, "", "Attack") +STR(OPL_DECAY, "", "Decay") +STR(OPL_SUSTAIN, "", "Sustain") +STR(OPL_RELEASE, "", "Release") +STR(OPL_ENVELOPE, "", "Hüllkurve") +STR(OPL_TICK, "", "Tick/Pos.") +STR(OPL_ACTIVE, "", "Aktiv") +STR(OPL_TEMPO, "", "Tempo BPM") + + +STR(MID_OPNERR, "s s d", "Fehler beim Öffnen von MIDI-Datei '$1': $2 ($3)") +#define STR_MID_READERR "Fehler beim Lesen von MIDI-Datei '$1': " +STR(MID_EIO, "s", STR_MID_READERR "E/A-Fehler") +STR(MID_EEOF, "s", STR_MID_READERR "Ende der Datei erreicht") +STR(MID_EGEN, "s s d", STR_MID_READERR "$2 ($3)") +STR(MID_ESHORT, "s", STR_MID_READERR "Datei zu klein") +STR(MID_EMFHDR, "s", STR_MID_READERR "Fehlerhafter Dateiheader oder falscher Dateityp") +STR(MID_EHSIZE, "s", STR_MID_READERR "Dateiheader mit falscher Länge") +STR(MID_ETKFMT, "s", STR_MID_READERR "Fehlerhaftes Spurformat") +STR(MID_ENOTRK, "s", STR_MID_READERR "Keine Spuren definiert") +STR(MID_ESMULT, "s", STR_MID_READERR "Einzelspur-Format mit mehreren Spuren") +STR(MID_ESMPTE, "s", STR_MID_READERR "SMPTE-Zeitformat nicht unterstützt") +STR(MID_EDZERO, "s", STR_MID_READERR "Zeitformat teilt durch null") +STR(MID_ETKHDR, "s", STR_MID_READERR "Fehlerhafter Spurheader") +STR(MID_EOUTOF, "s d", STR_MID_READERR "Spur #$2 ausserhalb der Datei") +STR(MID_SEQNUMO, "d d", "MIDI Spur #$1 Sequenz-Nummer = $2 (eigene)") +STR(MID_SEQNUM, "d d", "MIDI Spur #$1 Sequenz-Nummer = $2") +STR(MID_CHNPFX, "d d", "MIDI Spur #$1 Kanal-Praefix = $2") +STR(MID_END, "d", "MIDI Spur #$1 Ende erreicht") +STR(MID_TEMPO, "d", "MIDI Tempo = $1") +STR(MID_SMPTE, "", "MIDI SMPTE-Event (nicht unterstützt)") +STR(MID_TIMESIG, "d d d d", "MIDI Takt-Signatur = $1/$2 - $3 CPC - $4 32PQ") +STR(MID_KEYSIG, "d s", "MIDI Noten-Signatur = $1 - $2") +STR(MID_TEXT, "d s", "MIDI Text ($1): $2") +STR(MID_SEQDATA, "d d", "MIDI Spur #$1 Sequenzer-Daten empfangen - $2 Bytes") +STR(MID_UNKMETA, "d 02x d", "MIDI Spur #$1 Meta-Event 0x$2 ($3)") +STR(MID_METALEN, "d 02x d d", "MIDI Spur #$1 Meta-Event 0x$2 - erwartet $3, hat $4") +STR(MID_NOTEOFF, "02d 03d 03d", "MIDI Note-Aus - C$1 N$2 V$3") +STR(MID_NOTEON, "02d 03d 03d", "MIDI Note-An - C$1 N$2 V$3") +STR(MID_PRESS, "02d 03d", "MIDI Aftertouch - C$1 P$2") +STR(MID_CC, "02d 03d 03d", "MIDI Controller - C$1 N$2 V$3") +STR(MID_PROG, "02d 03d", "MIDI Programm - C$1 P$2") +STR(MID_CPRESS, "02d 03d", "MIDI Kanal-Druck - C$1 P$2") +STR(MID_PITCH, "02d 05d", "MIDI Pitch-Bend - C$1 P$2") +STR(MID_SYSEX, "d", "MIDI Sysex (Normal) mit Länge = $1") +STR(MID_SONGPOS, "d", "MIDI Song-Position = $1") +STR(MID_SONGSEL, "d", "MIDI Song-Auswahl = $1") +STR(MID_TUNE, "", "MIDI Stimmung angefordert, nichts zu tun?!") +STR(MID_ESYSEX, "d", "MIDI Sysex (Escape) mit Länge = $1") +STR(MID_GENSTAT, "d", "MIDI Status $1") +STR(MID_UNKSTAT, "02x", "MIDI Status unbekannt: 0x$1") +STR(MID_EEND, "d", "MIDI Spur #$1 endete zu früh") + +STR(BNK_UNKN, "s", "Format von Bank-Datei '$1' unbekannt") +STR(BNK_OPNERR, "s s d", "Fehler beim Öffnen von Bank-Datei '$1': $2 ($3)") +#define STR_BNK_READERR "Fehler beim Lesen von Bank-Datei '$1': " +#define STR_BNK_WRITERR "Fehler beim Schreiben nach Bank-Datei '$1': " +#define STR_BNK_PARSERR "Fehler beim Verarbeiten von Bank-Datei '$1': " +STR(BNK_EIO, "s", STR_BNK_READERR "E/A-Fehler") +STR(BNK_EEOF, "s", STR_BNK_READERR "Ende der Datei erreicht") +STR(BNK_EGEN, "s s d", STR_BNK_READERR "$2 ($3)") +STR(BNK_ESHORT, "s", STR_BNK_READERR "Datei zu klein") +STR(BNK_EMFHDR, "s", STR_BNK_READERR "Fehlerhafter Dateiheader oder falscher Dateityp") +STR(BNK_EWIO, "s", STR_BNK_WRITERR "E/A-Fehler") +STR(BNK_EEOD, "s", STR_BNK_PARSERR "Ende der Daten erreicht") + +STR(MID_PLAY, "s", "Spiele MIDI '$1'") +STR(CMD_PLAY_QUEUED, "d", "$1 MIDIs werden abgespielt") +STR(CMD_PLAY_STOPPED, "", "Wiedergabe ist angehalten") +STR(CMD_PLAY_PLAYING, "d", "$1 MIDIs sind in der Liste") +STR(CMD_PLAY_ENDED, "", "Wiedergabe gestoppt") +STR(CMD_PLAY_BANKID, "d", "Bank-ID: $1") +STR(CON_TOOLONG, "d", "Befehl #$1 ist zu lang") +STR(CMD_ARGREQ, "s", "Option '$1' benötigt ein Argument") +STR(CMD_ARGREQS, "s d", "Option '$1' benötigt $2 Argumente") + +STR(SND_FMT_S16LE, "", "16-Bit PCM") +STR(SND_FMT_S32LE, "", "32-Bit PCM") +STR(SND_DEV_DEFAULT, "", "Standard") +STR(SND_DEV_NULL, "", "Stumm") +STR(SND_DEV_JACK, "", "JACK-Server") +STR(SND_DEV_PULSE, "", "Pulseaudio") +STR(SND_DEV_HARDWARE, "", "Hardware (Karte+Port)") +STR(SND_DEV_SOFTWARE, "", "Software (Karte+Port)") +STR(SND_DEV_NONE, "", "[Keins]") +STR(SND_DEVTYPE, "", "Gerätetyp") +STR(SND_DEVICE_ID, "", "Karten-Nummer (-1=Standard)") +STR(SND_DEVICE_SUB, "", "Port-Nummer (-1=Standard)") +STR(SND_SAMPLERATE, "", "Abtastrate") +STR(SND_FORMAT, "", "Sample-Format") +STR(SND_BUFSIZE, "", "Puffergröße") +STR(SND_FRAMESIZE, "", "PCM-Intervall") +STR(SND_RESTART, "", "Übernehmen und Audio-Thread neu starten") + +STR(MID_USEUNKN, "", "Unbekannte Banken") +STR(MID_KEEPNOTES, "", "Stimmen behalten") +STR(MID_DONTFADE, "", "Nicht ausklingen") +STR(MID_DEBUGEVT, "", "MIDI-Debug") +STR(MID_VELOFUNC, "", "Anschlag") +STR(MID_VELO_LOG, "", "Log.+Minimum [m+nlog(x)]") +STR(MID_VELO_ATTN, "", "Log. Gedämpft [nlog(x)]") +STR(MID_VELO_LIN, "", "Linear [x]") +STR(MID_VELO_ONE, "", "Vollklang [1]") +STR(MID_OPVOICES, "", "OPL-Stimmen") +STR(MID_BANK, "", "Bank") + +STR(CON_OVERLAY, "", "Konsolen-Overlay") +STR(CON_HEIGHT, "", "Nachrichten im Overlay") +STR(CON_FADEOUT, "", "Dauer bis zum Ausblenden") +STR(CON_POS, "", "Position") +STR(CON_POS_TOP, "", "Am oberen Bildschirmrand") +STR(CON_POS_BTM, "", "Am unteren Bildschirmrand") +// STR(CON_MONOFONT, "", "Einheitliche Schriftart") +STR(CON_OPACITY, "", "Deckkraft Hintergrund") + +STR(CHUNK, "", "Chunk") +STR(CHUNKS, "", "Chunks") + +STR(PLR_MODE_NORM, "", "Normal") +STR(PLR_MODE_REPEAT, "", "Wiederholen") +STR(PLR_MODE_LOOP, "", "Schleife") +STR(PLR_MODE_RAND, "", "Zufällig") +STR(PLR_START, "", "Start") +STR(PLR_STOP, "", "Stop") +STR(PLR_PAUSE, "", "Pause") +STR(PLR_MODE, "", "Modus") +STR(PLR_PREV, "", "Vorheriger") +STR(PLR_NEXT, "", "Nächster") +STR(PLR_RANDOM, "", "Zufälliger") +STR(PLR_JUMP, "", "Springe zu") +STR(PLR_INFO, "", "Titelinfo") +STR(PLR_KARAOKE, "", "Karaoke-Texte") + +STR(IMID_TITLE, "", "Titel") +STR(IMID_INFO, "", "Info") +STR(IMID_COPY, "", "Hinweis") +STR(IMID_WARN, "", "Lizenz") +STR(IMID_LANG, "", "Sprache") +STR(IMID_VER, "", "Version") + +STR(TITLE_VOLUME, "", "Lautstärke") +STR(SND_VOL_MASTER, "", "Gesamt") +STR(SND_VOL_MUSIC, "", "Musik") +STR(SND_VOL_SFX, "", "Geräusche") +STR(SND_VOL_GUI, "", "Oberfläche") + +STR(TITLE_NONE, "", "Welt / Render (Maus gefangen)") +STR(TITLE_CONSOLE, "", "Konsole") +STR(TITLE_MAIN, "", "Hauptmenü") +STR(TITLE_MENU, "", "Welt / Render") +STR(TITLE_BINDS, "", "Tastenbelegung") +STR(TITLE_CONTROL, "", "Steuerung und Bedienung") +STR(TITLE_STYLE, "", "Benutzeroberfläche") +STR(TITLE_DISPLAY, "", "Anzeige und Bildschirm") +STR(TITLE_GRAPHICS, "", "Grafik und Darstellung") +STR(TITLE_SOUND, "", "Musik und Ton") +STR(TITLE_INFO, "", "Über dieses Programm") +STR(TITLE_PLAYER, "", "MIDI-Player") + +STR(TITLE_OPTIONS, "", "Einstellungen") +STR(TITLE_MIDI, "", "Optionen für MIDI-Wiedergabe") +STR(TITLE_PLR_FULL, "", "SKC OPL MIDI Player v 0.0.1 (~ Sen)") +STR(TITLE_INFO_SHORT, "", "Info / Über") + + +STR(BIND_QUIT, "", "Beenden") +STR(BIND_SCREENSHOT, "", "Bildschirmfoto") +STR(BIND_CONSOLE, "", "Konsole") +STR(BIND_SHOW, "", "Perf.-Anzeige") +STR(BIND_FULLSCREEN, "", "Vollbild") +STR(BIND_SYNC, "", "Bild-Synch.") +STR(BIND_MENU, "", "Menü") +STR(BIND_DOWN, "", "Abwärts, Langsam") +STR(BIND_UP, "", "Aufwärts, Springen") +STR(BIND_FORWARD, "", "Vorwärts") +STR(BIND_BACKWARD, "", "Rückwärts") +STR(BIND_LEFT, "", "Nach links") +STR(BIND_RIGHT, "", "Nach rechts") +STR(BIND_FAST, "", "Schneller") +STR(BIND_NOCLIP, "", "NoClip") +STR(BIND_FLY, "", "Fliegen") +STR(BIND_ZOOM_IN, "", "Hinein zoomen") +STR(BIND_ZOOM_OUT, "", "Heraus zoomen") +STR(BIND_CAMERA, "", "Kameramodus") +STR(BIND_PLAYER, "", "Zufälliges MIDI") + +STR(KEY_MOUSE_LEFT, "", "Linke Maustaste") +STR(KEY_MOUSE_RIGHT, "", "Rechte Maustaste") +STR(KEY_MOUSE_MIDDLE, "", "Mittlere Maustaste") +STR(KEY_MOUSE_BTN_X, "", "Maustaste Seite 1") +STR(KEY_MOUSE_BTN_Y, "", "Maustaste Seite 2") +STR(KEY_MOUSE_BTN_A, "", "Maustaste 6") +STR(KEY_MOUSE_BTN_B, "", "Maustaste 7") +STR(KEY_MOUSE_BTN_C, "", "Maustaste 8") +STR(KEY_SCROLL_UP, "", "Mausrad aufwärts") +STR(KEY_SCROLL_DOWN, "", "Mausrad abwärts") +STR(KEY_SCROLL_LEFT, "", "Mausrad links") +STR(KEY_SCROLL_RIGHT, "", "Mausrad rechts") + +STR(KEY_SPACE, "", "Leertaste") +STR(KEY_WORLD_1, "", "Welt 1") +STR(KEY_WORLD_2, "", "Welt 2") +STR(KEY_ESCAPE, "", "Esc") +STR(KEY_ENTER, "", "Enter") +STR(KEY_TAB, "", "Tab") +STR(KEY_BACKSPACE, "", "Rücktaste") +STR(KEY_INSERT, "", "Einfg") +STR(KEY_DELETE, "", "Entf") +STR(KEY_RIGHT, "", "Pfeil rechts") +STR(KEY_LEFT, "", "Pfeil links") +STR(KEY_DOWN, "", "Pfeil unten") +STR(KEY_UP, "", "Pfeil oben") +STR(KEY_PAGE_UP, "", "Bild auf") +STR(KEY_PAGE_DOWN, "", "Bild ab") +STR(KEY_HOME, "", "Pos1") +STR(KEY_END, "", "Ende") +STR(KEY_CAPS_LOCK, "", "Feststellen") +STR(KEY_SCROLL_LOCK, "", "Scroll Lock") +STR(KEY_NUM_LOCK, "", "Num Lock") +STR(KEY_PRINT_SCREEN, "", "Druck") +STR(KEY_PAUSE, "", "Pause") +STR(KEY_KP_DECIMAL, "", "Num .") +STR(KEY_KP_DIVIDE, "", "Num /") +STR(KEY_KP_MULTIPLY, "", "Num *") +STR(KEY_KP_SUBTRACT, "", "Num -") +STR(KEY_KP_ADD, "", "Num +") +STR(KEY_KP_ENTER, "", "Num Enter") +STR(KEY_KP_EQUAL, "", "Num =") +STR(KEY_LEFT_SHIFT, "", "Umschalt links") +STR(KEY_LEFT_CONTROL, "", "Strg links") +STR(KEY_LEFT_ALT, "", "Alt links") +STR(KEY_LEFT_SUPER, "", "Meta links") +STR(KEY_RIGHT_SHIFT, "", "Umschalt rechts") +STR(KEY_RIGHT_CONTROL, "", "Strg rechts") +STR(KEY_RIGHT_ALT, "", "Alt rechts") +STR(KEY_RIGHT_SUPER, "", "Meta rechts") +STR(KEY_MENU, "", "Menü") + +STR(KEY_SPACE, "", "Leertaste") +STR(KEY_CIRCUMFLEX, "", "<^>") +STR(KEY_SHARP_S, "", "<ß>") +STR(KEY_ACUTE, "", "<´>") +STR(KEY_UE, "", "<Ü>") +STR(KEY_PLUS, "", "<+>") +STR(KEY_OE, "", "<Ö>") +STR(KEY_AE, "", "<Ä>") +STR(KEY_NUMBER_SIGN, "", "<#>") +STR(KEY_LESS_THAN, "", "<<>") +STR(KEY_COMMA, "", "<,>") +STR(KEY_PERIOD, "", "<.>") +STR(KEY_HYPHEN, "", "<->") + +STR(KEY_KP_DECIMAL, "", "Num .") +STR(KEY_KP_DIVIDE, "", "Num /") +STR(KEY_KP_MULTIPLY, "", "Num *") +STR(KEY_KP_SUBTRACT, "", "Num -") +STR(KEY_KP_ADD, "", "Num +") +STR(KEY_KP_ENTER, "", "Num Enter") +STR(KEY_KP_EQUAL, "", "Num =") + +STR(KEY_CAPS_LOCK, "", "Feststellen") +STR(KEY_SCROLL_LOCK, "", "Scroll Lock") +STR(KEY_NUM_LOCK, "", "Num Lock") + +STR(KEY_ESCAPE, "", "Esc") +STR(KEY_RETURN, "", "Enter") +STR(KEY_TAB, "", "Tab") +STR(KEY_BACKSPACE, "", "Rücktaste") +STR(KEY_INSERT, "", "Einfg") +STR(KEY_DELETE, "", "Entf") +STR(KEY_RIGHT, "", "Pfeil rechts") +STR(KEY_LEFT, "", "Pfeil links") +STR(KEY_DOWN, "", "Pfeil unten") +STR(KEY_UP, "", "Pfeil oben") +STR(KEY_PAGE_UP, "", "Bild auf") +STR(KEY_PAGE_DOWN, "", "Bild ab") +STR(KEY_HOME, "", "Pos1") +STR(KEY_END, "", "Ende") +STR(KEY_PRINT_SCREEN, "", "Druck") +STR(KEY_PAUSE, "", "Pause") +STR(KEY_LEFT_SHIFT, "", "Umschalt links") +STR(KEY_LEFT_CONTROL, "", "Strg links") +STR(KEY_ALT, "", "Alt") +STR(KEY_LEFT_META, "", "Meta links") +STR(KEY_RIGHT_SHIFT, "", "Umschalt rechts") +STR(KEY_RIGHT_CONTROL, "", "Strg rechts") +STR(KEY_ALT_GRAPH, "", "Alt Gr") +STR(KEY_RIGHT_META, "", "Meta rechts") +STR(KEY_MENU, "", "Menü") + +// STR( , "", "") diff --git a/system.h b/system.h new file mode 100644 index 0000000..b74efb5 --- /dev/null +++ b/system.h @@ -0,0 +1,172 @@ + +void *mem_alloc(ulong size, byte cat); +void *mem_realloc(void *vptr, ulong size, byte cat); +void mem_free(void *vptr); + +void sys_panic(const char *error, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + void *rip; + void *rsp; + void *rbp; + void **bt; + char **bs; + int frames; + asm volatile("lea (%%rip),%%rax \n\tmov %%rax,%0" : "=m" (rip) : ); + asm volatile("mov %%rsp,%0" : "=m" (rsp) : ); + asm volatile("mov %%rbp,%0" : "=m" (rbp) : ); + fprintf(stderr, "\n========================= !ABSTURZ! ========================="); + fprintf(stderr, "\n TRACEBACK [Jetzt: %%rsp=$%llx, %%rbp=$%llx]:", (ulong)(rsp), (ulong)(rbp)); + bs = backtrace_symbols(&rip, 1); + if(bs) { + fprintf(stderr, "\n -> [RIP] %s", bs[0] ? bs[0] : ""); + free(bs); + } + else { + fprintf(stderr, "\n -> [RIP] $%llx", (ulong)(rip)); + } + if(bt = malloc(128 * sizeof(void *))) { + // memset(bt, 0, 128 * sizeof(void *)); + frames = backtrace(bt, 128); + if(bs = backtrace_symbols(bt, frames)) { + for(int z = 0; (z < frames) && bt[z]; z++) { + fprintf(stderr, "\n -> [%03d] %s", z, bs[z] ? bs[z] : ""); + } + free(bs); + } + else { + for(int z = 0; (z < frames) && bt[z]; z++) { + fprintf(stderr, "\n -> [%03d] $%llx", z, bt[z]); + } + } + free(bt); + } + else { + fprintf(stderr, "\n *Konnte keinen Speicher für Traceback reservieren*"); + } + fprintf(stderr, "\n\n FEHLER#: %s\n URSACHE: ", error); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n======================== ABGEBROCHEN ========================\n\n"); + // abort(); + va_end(ap); + abort(); +} + +void sys_abort() { + sys_panic("MANUAL_ABORT", "*Programm wurde manuell abgebrochen*"); +} + +ulong sys_segtest(ulong addr) { + ulong *ptr = (ulong*)addr; + *ptr = 666ULL; + return *ptr; +} + +ulong sys_fpetest() { + ulong cma = 666ULL; + ulong cmb = 0ULL; + return cma / cmb; +} + +ulong sys_overtest(ulong v) { + ulong cma = 666ULL; + return sys_overtest(v * cma); +} + +ulong sys_illtest() { + asm volatile(".byte 0x0f, 0x04"); + // asm volatile("jmp *%0" : : "m" (cma)); +} + +void sys_coretest() { + srand(time(0)); + sys_panic("CORE_MELTDOWN", "Nuclear processor core meltdown imminent\n\n" + " ************************ CAUTION ************************\n" + " KCTL: Processor core #%02d has reached a critical\n" + " temperature, system explosion is imminent! \n" + " According to the general core density \n" + " calculation routine defined by the SKC \n" + " (Hard) Warfare Testing Facility (SKC-WTF) \n" + " your processor will cause a detonation with \n" + " a radius of (roughly) %d.%d kilometers. \n" + " In addition, it will release appoximately \n" + " %d megajoules of ionizing radiation. \n" + " You have an estimate time of %d minutes and \n" + " %d seconds left to clear the detonation area. \n" + " ************************ CAUTION ************************\n" + , 1 + (rand() % 64), 1 + (rand() % 9), rand() % 10, 10000 + (rand() % 30000), 3 + (rand() % 7), 2 + (rand() % 58)); +} + +void sys_handle_seg(int sig, siginfo_t *info, void *ucontext) { + if(info->si_addr) { + sys_panic("SEGMENTATION_FAULT", "Speicherzugriffsfehler bei $%llx", (ulong)info->si_addr); + } + else { + sys_panic("NULL_POINTER", "Versuchte Zugriff auf Speicher bei $0"); + } +} + +void sys_handle_ill(int sig, siginfo_t *info, void *ucontext) { + sys_panic("ILLEGAL_INSTRUCTION", "Ungültige Prozessorinstruktion bei $%llx", (ulong)info->si_addr); +} + +void sys_handle_fpe(int sig, siginfo_t *info, void *ucontext) { + sys_panic("FLOATING_POINT_EXCEPTION", "Ungültige Gleitkommaoperation bei $%llx", (ulong)info->si_addr); +} + +void sys_test_handlers(const char *test) { + if(!test) { + return; + } + else if(!strcmp("NULL", test)) { + sys_segtest(0ULL); + } + else if(!strcmp("SEG", test)) { + sys_segtest(0x666ULL); + } + else if(!strcmp("FPE", test)) { + sys_fpetest(); + } + else if(!strcmp("OVER", test)) { + sys_overtest(1); + } + else if(!strcmp("ILL", test)) { + sys_illtest(); + } + else if(!strcmp("ABORT", test)) { + sys_abort(); + } + else if(!strcmp("CORE", test)) { + sys_coretest(); + } + else if(!strcmp("MEM", test)) { + mem_alloc(0x7ffffffffffffff0, 0); + } +} + +void *sys_altstack; + +void sys_setup_handlers() { + struct sigaction seg; + stack_t stack; + memset(&seg, 0, sizeof(struct sigaction)); + seg.sa_sigaction = sys_handle_seg; + seg.sa_flags = SA_ONSTACK | SA_SIGINFO; + sigaction(SIGSEGV, &seg, NULL); + memset(&seg, 0, sizeof(struct sigaction)); + seg.sa_sigaction = sys_handle_fpe; + seg.sa_flags = SA_ONSTACK | SA_SIGINFO; + sigaction(SIGFPE, &seg, NULL); + memset(&seg, 0, sizeof(struct sigaction)); + seg.sa_sigaction = sys_handle_ill; + seg.sa_flags = SA_ONSTACK | SA_SIGINFO; + sigaction(SIGILL, &seg, NULL); + stack.ss_sp = sys_altstack = malloc(16384); + stack.ss_flags = 0; + stack.ss_size = 16384; + sigaltstack(&stack, NULL); +} + +void sys_remove_handlers() { + free(sys_altstack); +} diff --git a/table.h b/table.h new file mode 100644 index 0000000..9921c55 --- /dev/null +++ b/table.h @@ -0,0 +1,246 @@ + +void *itbl_push(itable_t *table) { + table_e *elem = table->free; + uint *block; + int cid; + int eid; + if(elem == NULL) { + return NULL; + } + table->free = table->free->f_next ? table->free->f_next : table->free->f_prev; + if(elem->f_prev) { + elem->f_prev->f_next = elem->f_next; + } + if(elem->f_next) { + elem->f_next->f_prev = elem->f_prev; + } + elem->f_next = elem->f_prev = NULL; + if(table->used && table->used->u_prev) { + table->used->u_prev->u_next = elem; + } + elem->u_prev = table->used ? table->used->u_prev : NULL; // unused + elem->u_next = table->used; + if(table->used) { + table->used->u_prev = elem; + } + table->used = elem; + + cid = elem->cid; + eid = elem->eid; + if(table->data[cid] == NULL) { + table->data[cid] = mem_alloc((sizeof(uint) * 2 + table->stride) * table->load, table->cat); + table->alloc += 1; + } + table->sizes[cid] += 1; + block = (uint *)(&((byte *)(table->data[cid]))[eid * (sizeof(uint) * 2 + table->stride)]); + block[0] = cid; + block[1] = eid; + block += 2; + table->stored += 1; + // elem->value = (void *)block; + return (void *)block; +} + +void itbl_init(itable_t *table, uint size, uint load, uint stride, byte cat); + +void *tbl_push(table_t *table) { + void *elem; + for(int z = 0; z < table->size; z++) { + if(elem = itbl_push(&table->nodes[z])) { + table->stored += 1; + return elem; + } + } + table->nodes = mem_realloc(table->nodes, sizeof(itable_t) * (table->size + 1), table->cat); + itbl_init(&table->nodes[table->size], table->block, table->load, table->stride, table->cat); + elem = itbl_push(&table->nodes[table->size]); + table->size += 1; + table->stored += 1; + return elem; +} + +byte itbl_pop(itable_t *table, void *ptr) { + uint *block = ((uint *)ptr) - 2; + int cid = block[0]; + int eid = block[1]; + if(block != (uint *)(&((byte *)(table->data[cid]))[eid * (sizeof(uint) * 2 + table->stride)])) + return 0; + table_e *elem = &table->elems[cid * table->load + eid]; + if(elem == table->used) { + table->used = table->used->u_next ? table->used->u_next : table->used->u_prev; + } + if(elem->u_prev) { + elem->u_prev->u_next = elem->u_next; + } + if(elem->u_next) { + elem->u_next->u_prev = elem->u_prev; + } + elem->u_next = elem->u_prev = NULL; + if(table->free && table->free->f_prev) { + table->free->f_prev->f_next = elem; + } + elem->f_prev = table->free ? table->free->f_prev : NULL; + elem->f_next = table->free; + if(table->free) { + table->free->f_prev = elem; + } + table->free = elem; + + if((table->sizes[cid] -= 1) == 0) { + mem_free(table->data[cid]); + table->data[cid] = NULL; + table->alloc -= 1; + } + table->stored -= 1; + // elem->value = NULL; + return 1; +} + +void itbl_free(itable_t *table); + +byte tbl_pop(table_t *table, void *ptr) { + itable_t *itable; + for(int z = 0; z < table->size; z++) { + itable = &table->nodes[z]; + if(itbl_pop(itable, ptr)) { + table->stored -= 1; + if(!itable->stored) { + itbl_free(&table->nodes[z]); + itable = table->size == 1 ? NULL : mem_alloc(sizeof(itable_t) * (table->size - 1), table->cat); + table->size -= 1; + if(z) + memcpy(itable, table->nodes, z * sizeof(itable_t)); + if(z < table->size) + memcpy(&itable[z], &table->nodes[z+1], (table->size - z) * sizeof(itable_t)); + mem_free(table->nodes); + table->nodes = itable; + } + return 1; + } + } + return 0; +} + +void *itbl_iter(itable_t *table, uint *ptr) { + table_e *elem = ((*ptr) == 0xffffffff) ? NULL : (((*ptr) == 0) ? table->used : &table->elems[(*ptr)-1]); + if(elem == NULL) { + return NULL; + } + *ptr = elem->u_next ? ((elem->u_next->cid * table->load + elem->u_next->eid) + 1) : 0xffffffff; + return ((uint *)(&((byte *)(table->data[elem->cid]))[elem->eid * (sizeof(uint) * 2 + table->stride)])) + 2; +} + +void *tbl_iter(table_t *table, ulong *ptr) { + uint tid = (uint)((*ptr) & 0xffffffff); + uint nid = (uint)((*ptr) >> 32); + void *elem; + if(tid >= table->size) { + return NULL; + } + elem = itbl_iter(&table->nodes[tid], &nid); + while(!elem) { + if(++tid >= table->size) + return NULL; + nid = 0; + elem = itbl_iter(&table->nodes[tid], &nid); + } + *ptr = (ulong)tid | ((ulong)nid << 32); + return elem; +} + + +void itbl_dealloc(itable_t *table) { + for(int z = 0; z < table->chunks; z++) { + if(table->data[z]) { + mem_free(table->data[z]); + table->data[z] = NULL; + table->sizes[z] = 0; + } + } +} + +void itbl_clear(itable_t *table) { + table_e *elem; + itbl_dealloc(table); + table->stored = table->alloc = 0; + table->used = NULL; + table->free = table->elems; + for(int z = 0; z < table->size; z++) { + elem = &table->elems[z]; + elem->f_prev = (z == 0) ? NULL : &table->elems[z-1]; + elem->f_next = (z == (table->size - 1)) ? NULL : &table->elems[z+1]; + elem->u_prev = elem->u_next = NULL; + // elem->value = NULL; + elem->cid = z / table->load; + elem->eid = z % table->load; + } +} + +void itbl_init(itable_t *table, uint size, uint load, uint stride, byte cat) { + table->size = size * load; + table->load = load; + table->chunks = table->size / table->load; + table->stride = stride; + table->cat = cat; + table->elems = mem_alloc(table->size * sizeof(table_e), cat); + table->data = mem_alloc(table->chunks * sizeof(void *), cat); + memset(table->data, 0, table->chunks * sizeof(void *)); + table->sizes = mem_alloc(table->chunks * sizeof(uint), cat); + memset(table->sizes, 0, table->chunks * sizeof(uint)); + itbl_clear(table); +} + +void tbl_init(table_t *table, uint block, uint load, uint stride, byte cat) { + table->load = load; + table->block = block; + table->stride = stride; + table->cat = cat; + table->nodes = NULL; + table->size = table->stored = 0; +} + +void itbl_free(itable_t *table) { + itbl_dealloc(table); + mem_free(table->elems); + mem_free(table->data); + mem_free(table->sizes); +} + +void tbl_clear(table_t *table) { + for(int z = 0; z < table->size; z++) { + itbl_free(&table->nodes[z]); + } + if(table->nodes) { + mem_free(table->nodes); + table->nodes = NULL; + } + table->size = table->stored = 0; +} + +int tbl_clear_func(table_t *table, clear_func *func) { + ulong iter = 0; + int pos = 0; + void *elem; + while(elem = tbl_iter(table, &iter)) { + func(elem); + pos++; + } + tbl_clear(table); + return pos; +} + +int tbl_clear_func_sel(table_t *table, clear_sfunc *func, int count) { + ulong iter = 0; + int pos = 0; + void *elem; + void **clr = mem_alloc(sizeof(void*) * count, table->cat); + while(elem = tbl_iter(table, &iter)) { + if(func(elem)) + clr[pos++] = elem; + } + for(iter = 0; iter < pos; iter++) { + tbl_pop(table, clr[iter]); + } + mem_free(clr); + return pos; +} diff --git a/tcglm.h b/tcglm.h new file mode 100644 index 0000000..3226f38 --- /dev/null +++ b/tcglm.h @@ -0,0 +1,11558 @@ +/* + * Copyright (c), Recep Aslantas. + * + * MIT License (MIT), http://opensource.org/licenses/MIT + * Full license can be found in the LICENSE file + */ + +#define GLM_SHUFFLE4(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w)) +#define GLM_SHUFFLE3(z, y, x) (((z) << 4) | ((y) << 2) | (x)) + +#ifdef __GNUC__ +# define CGLM_ASSUME_ALIGNED(expr, alignment) \ + __builtin_assume_aligned((expr), (alignment)) +#else +# define CGLM_ASSUME_ALIGNED(expr, alignment) (expr) +#endif +#define CGLM_CASTPTR_ASSUME_ALIGNED(expr, type) \ + ((type*)CGLM_ASSUME_ALIGNED((expr), __alignof__(type))) + + + +#if defined( __SSE__ ) || defined( __SSE2__ ) +#define CGLM_SIMD + +#define glmm_load(p) _mm_load_ps(p) +#define glmm_store(p, a) _mm_store_ps(p, a) + +#define glmm_set1(x) _mm_set1_ps(x) +#define glmm_128 __m128 + +#ifdef CGLM_USE_INT_DOMAIN +# define glmm_shuff1(xmm, z, y, x, w) \ + _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(xmm), \ + _MM_SHUFFLE(z, y, x, w))) +#else +# define glmm_shuff1(xmm, z, y, x, w) \ + _mm_shuffle_ps(xmm, xmm, _MM_SHUFFLE(z, y, x, w)) +#endif + +#define glmm_splat(x, lane) glmm_shuff1(x, lane, lane, lane, lane) + +#define glmm_splat_x(x) glmm_splat(x, 0) +#define glmm_splat_y(x) glmm_splat(x, 1) +#define glmm_splat_z(x) glmm_splat(x, 2) +#define glmm_splat_w(x) glmm_splat(x, 3) + +#define glmm_shuff2(a, b, z0, y0, x0, w0, z1, y1, x1, w1) \ + glmm_shuff1(_mm_shuffle_ps(a, b, _MM_SHUFFLE(z0, y0, x0, w0)), \ + z1, y1, x1, w1) + +static inline +__m128 +glmm_abs(__m128 x) { + return _mm_andnot_ps(_mm_set1_ps(-0.0f), x); +} + +static inline +__m128 +glmm_vhadd(__m128 v) { + __m128 x0; + x0 = _mm_add_ps(v, glmm_shuff1(v, 0, 1, 2, 3)); + x0 = _mm_add_ps(x0, glmm_shuff1(x0, 1, 0, 0, 1)); + return x0; +} + +static inline +__m128 +glmm_vhadds(__m128 v) { + __m128 shuf, sums; + shuf = glmm_shuff1(v, 2, 3, 0, 1); + sums = _mm_add_ps(v, shuf); + shuf = _mm_movehl_ps(shuf, sums); + sums = _mm_add_ss(sums, shuf); + return sums; +} + +static inline +float +glmm_hadd(__m128 v) { + return _mm_cvtss_f32(glmm_vhadds(v)); +} + +static inline +__m128 +glmm_vhmin(__m128 v) { + __m128 x0, x1, x2; + x0 = _mm_movehl_ps(v, v); /* [2, 3, 2, 3] */ + x1 = _mm_min_ps(x0, v); /* [0|2, 1|3, 2|2, 3|3] */ + x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */ + return _mm_min_ss(x1, x2); +} + +static inline +float +glmm_hmin(__m128 v) { + return _mm_cvtss_f32(glmm_vhmin(v)); +} + +static inline +__m128 +glmm_vhmax(__m128 v) { + __m128 x0, x1, x2; + x0 = _mm_movehl_ps(v, v); /* [2, 3, 2, 3] */ + x1 = _mm_max_ps(x0, v); /* [0|2, 1|3, 2|2, 3|3] */ + x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */ + return _mm_max_ss(x1, x2); +} + +static inline +float +glmm_hmax(__m128 v) { + return _mm_cvtss_f32(glmm_vhmax(v)); +} + +static inline +__m128 +glmm_vdots(__m128 a, __m128 b) { + return glmm_vhadds(_mm_mul_ps(a, b)); +} + +static inline +__m128 +glmm_vdot(__m128 a, __m128 b) { + __m128 x0; + x0 = _mm_mul_ps(a, b); + x0 = _mm_add_ps(x0, glmm_shuff1(x0, 1, 0, 3, 2)); + return _mm_add_ps(x0, glmm_shuff1(x0, 0, 1, 0, 1)); +} + +static inline +float +glmm_dot(__m128 a, __m128 b) { + return _mm_cvtss_f32(glmm_vdots(a, b)); +} + +static inline +float +glmm_norm(__m128 a) { + return _mm_cvtss_f32(_mm_sqrt_ss(glmm_vhadds(_mm_mul_ps(a, a)))); +} + +static inline +float +glmm_norm2(__m128 a) { + return _mm_cvtss_f32(glmm_vhadds(_mm_mul_ps(a, a))); +} + +static inline +float +glmm_norm_one(__m128 a) { + return _mm_cvtss_f32(glmm_vhadds(glmm_abs(a))); +} + +static inline +float +glmm_norm_inf(__m128 a) { + return _mm_cvtss_f32(glmm_vhmax(glmm_abs(a))); +} + +static inline +__m128 +glmm_load3(float v[3]) { + __m128i xy; + __m128 z; + + xy = _mm_loadl_epi64(CGLM_CASTPTR_ASSUME_ALIGNED(v, const __m128i)); + z = _mm_load_ss(&v[2]); + + return _mm_movelh_ps(_mm_castsi128_ps(xy), z); +} + +static inline +void +glmm_store3(float v[3], __m128 vx) { + _mm_storel_pi(CGLM_CASTPTR_ASSUME_ALIGNED(v, __m64), vx); + _mm_store_ss(&v[2], glmm_shuff1(vx, 2, 2, 2, 2)); +} + +static inline +__m128 +glmm_div(__m128 a, __m128 b) { + return _mm_div_ps(a, b); +} + + +static inline +__m128 +glmm_fmadd(__m128 a, __m128 b, __m128 c) { + return _mm_add_ps(c, _mm_mul_ps(a, b)); +} + +static inline +__m128 +glmm_fnmadd(__m128 a, __m128 b, __m128 c) { + return _mm_sub_ps(c, _mm_mul_ps(a, b)); +} + +static inline +__m128 +glmm_fmsub(__m128 a, __m128 b, __m128 c) { + return _mm_sub_ps(_mm_mul_ps(a, b), c); +} + +static inline +__m128 +glmm_fnmsub(__m128 a, __m128 b, __m128 c) { + return _mm_xor_ps(_mm_add_ps(_mm_mul_ps(a, b), c), _mm_set1_ps(-0.0f)); +} + +#endif + + + + +#ifndef CGLM_USE_DEFAULT_EPSILON +# ifndef GLM_FLT_EPSILON +# define GLM_FLT_EPSILON 1e-5f +# endif +#else +# define GLM_FLT_EPSILON FLT_EPSILON +#endif + +/* + * Clip control: define CGLM_FORCE_DEPTH_ZERO_TO_ONE before including + * CGLM to use a clip space between 0 to 1. + * Coordinate system: define CGLM_FORCE_LEFT_HANDED before including + * CGLM to use the left handed coordinate system by default. + */ + +#define CGLM_CLIP_CONTROL_ZO_BIT (1 << 0) /* ZERO_TO_ONE */ +#define CGLM_CLIP_CONTROL_NO_BIT (1 << 1) /* NEGATIVE_ONE_TO_ONE */ +#define CGLM_CLIP_CONTROL_LH_BIT (1 << 2) /* LEFT_HANDED, For DirectX, Metal, Vulkan */ +#define CGLM_CLIP_CONTROL_RH_BIT (1 << 3) /* RIGHT_HANDED, For OpenGL, default in GLM */ + +#define CGLM_CLIP_CONTROL_LH_ZO (CGLM_CLIP_CONTROL_LH_BIT | CGLM_CLIP_CONTROL_ZO_BIT) +#define CGLM_CLIP_CONTROL_LH_NO (CGLM_CLIP_CONTROL_LH_BIT | CGLM_CLIP_CONTROL_NO_BIT) +#define CGLM_CLIP_CONTROL_RH_ZO (CGLM_CLIP_CONTROL_RH_BIT | CGLM_CLIP_CONTROL_ZO_BIT) +#define CGLM_CLIP_CONTROL_RH_NO (CGLM_CLIP_CONTROL_RH_BIT | CGLM_CLIP_CONTROL_NO_BIT) + +#define CGLM_CONFIG_CLIP_CONTROL CGLM_CLIP_CONTROL_RH_NO + + +#define GLM_MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define GLM_MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) + +/*! + * @brief get sign of 32 bit integer as +1, -1, 0 + * + * Important: It returns 0 for zero input + * + * @param val integer value + */ +f_inline +int +glm_sign(int val) { + return ((val >> 31) - (-val >> 31)); +} + +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param val float value + */ +f_inline +float +glm_signf(float val) { + return (float)((val > 0.0f) - (val < 0.0f)); +} + +/*! + * @brief convert degree to radians + * + * @param[in] deg angle in degrees + */ +f_inline +float +glm_rad(float deg) { + return deg * GLM_PIf / 180.0f; +} + +/*! + * @brief convert radians to degree + * + * @param[in] rad angle in radians + */ +f_inline +float +glm_deg(float rad) { + return rad * 180.0f / GLM_PIf; +} + +/*! + * @brief convert exsisting degree to radians. this will override degrees value + * + * @param[in, out] deg pointer to angle in degrees + */ +f_inline +void +glm_make_rad(float *deg) { + *deg = *deg * GLM_PIf / 180.0f; +} + +/*! + * @brief convert exsisting radians to degree. this will override radians value + * + * @param[in, out] rad pointer to angle in radians + */ +f_inline +void +glm_make_deg(float *rad) { + *rad = *rad * 180.0f / GLM_PIf; +} + +/*! + * @brief multiplies given parameter with itself = x * x or powf(x, 2) + * + * @param[in] x x + */ +f_inline +float +glm_pow2(float x) { + return x * x; +} + +/*! + * @brief find minimum of given two values + * + * @param[in] a number 1 + * @param[in] b number 2 + */ +f_inline +float +glm_min(float a, float b) { + if (a < b) + return a; + return b; +} + +/*! + * @brief find maximum of given two values + * + * @param[in] a number 1 + * @param[in] b number 2 + */ +f_inline +float +glm_max(float a, float b) { + if (a > b) + return a; + return b; +} + +/*! + * @brief clamp a number between min and max + * + * @param[in] val value to clamp + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +float +glm_clamp(float val, float minVal, float maxVal) { + return glm_min(glm_max(val, minVal), maxVal); +} + +/*! + * @brief clamp a number to zero and one + * + * @param[in] val value to clamp + */ +f_inline +float +glm_clamp_zo(float val) { + return glm_clamp(val, 0.0f, 1.0f); +} + +/*! + * @brief linear interpolation between two numbers + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + */ +f_inline +float +glm_lerp(float from, float to, float t) { + return from + t * (to - from); +} + +/*! + * @brief clamped linear interpolation between two numbers + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + */ +f_inline +float +glm_lerpc(float from, float to, float t) { + return glm_lerp(from, to, glm_clamp_zo(t)); +} + +/*! + * @brief threshold function + * + * @param[in] edge threshold + * @param[in] x value to test against threshold + * @return returns 0.0 if x < edge, else 1.0 + */ +f_inline +float +glm_step(float edge, float x) { + /* branching - no type conversion */ + return (x < edge) ? 0.0f : 1.0f; + /* + * An alternative implementation without branching + * but with type conversion could be: + * return !(x < edge); + */ +} + +/*! + * @brief smooth Hermite interpolation + * + * formula: t^2 * (3-2t) + * + * @param[in] t interpolant (amount) + */ +f_inline +float +glm_smooth(float t) { + return t * t * (3.0f - 2.0f * t); +} + +/*! + * @brief threshold function with a smooth transition (according to OpenCL specs) + * + * formula: t^2 * (3-2t) + * + * @param[in] edge0 low threshold + * @param[in] edge1 high threshold + * @param[in] x interpolant (amount) + */ +f_inline +float +glm_smoothstep(float edge0, float edge1, float x) { + float t; + t = glm_clamp_zo((x - edge0) / (edge1 - edge0)); + return glm_smooth(t); +} + +/*! + * @brief smoothstep interpolation between two numbers + * + * formula: from + smoothstep(t) * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + */ +f_inline +float +glm_smoothinterp(float from, float to, float t) { + return from + glm_smooth(t) * (to - from); +} + +/*! + * @brief clamped smoothstep interpolation between two numbers + * + * formula: from + smoothstep(t) * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + */ +f_inline +float +glm_smoothinterpc(float from, float to, float t) { + return glm_smoothinterp(from, to, glm_clamp_zo(t)); +} + +/*! + * @brief check if two float equal with using EPSILON + * + * @param[in] a a + * @param[in] b b + */ +f_inline +byte +glm_eq(float a, float b) { + return fabsf(a - b) <= GLM_FLT_EPSILON; +} + +/*! + * @brief percentage of current value between start and end value + * + * maybe fraction could be alternative name. + * + * @param[in] from from value + * @param[in] to to value + * @param[in] current current value + */ +f_inline +float +glm_percent(float from, float to, float current) { + float t; + + if ((t = to - from) == 0.0f) + return 1.0f; + + return (current - from) / t; +} + +/*! + * @brief clamped percentage of current value between start and end value + * + * @param[in] from from value + * @param[in] to to value + * @param[in] current current value + */ +f_inline +float +glm_percentc(float from, float to, float current) { + return glm_clamp_zo(glm_percent(from, to, current)); +} + +/*! +* @brief swap two float values +* +* @param[in] a float value 1 (pointer) +* @param[in] b float value 2 (pointer) +*/ +f_inline +void +glm_swapf(float * __restrict a, float * __restrict b) { + float t; + t = *a; + *a = *b; + *b = t; +} + + + + +/*! + * @brief fill a vector with specified value + * + * @param[out] v dest + * @param[in] val value + */ +f_inline +void +glm_vec2_fill(vec2 v, float val) { + v[0] = v[1] = val; +} + +/*! + * @brief check if vector is equal to value (without epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +f_inline +byte +glm_vec2_eq(vec2 v, float val) { + return v[0] == val && v[0] == v[1]; +} + +/*! + * @brief check if vector is equal to value (with epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +f_inline +byte +glm_vec2_eq_eps(vec2 v, float val) { + return fabsf(v[0] - val) <= GLM_FLT_EPSILON + && fabsf(v[1] - val) <= GLM_FLT_EPSILON; +} + +/*! + * @brief check if vectors members are equal (without epsilon) + * + * @param[in] v vector + */ +f_inline +byte +glm_vec2_eq_all(vec2 v) { + return glm_vec2_eq_eps(v, v[0]); +} + +/*! + * @brief check if vector is equal to another (without epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +f_inline +byte +glm_vec2_eqv(vec2 a, vec2 b) { + return a[0] == b[0] && a[1] == b[1]; +} + +/*! + * @brief check if vector is equal to another (with epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +f_inline +byte +glm_vec2_eqv_eps(vec2 a, vec2 b) { + return fabsf(a[0] - b[0]) <= GLM_FLT_EPSILON + && fabsf(a[1] - b[1]) <= GLM_FLT_EPSILON; +} + +/*! + * @brief max value of vector + * + * @param[in] v vector + */ +f_inline +float +glm_vec2_max(vec2 v) { + return glm_max(v[0], v[1]); +} + +/*! + * @brief min value of vector + * + * @param[in] v vector + */ +f_inline +float +glm_vec2_min(vec2 v) { + return glm_min(v[0], v[1]); +} + +/*! + * @brief check if all items are NaN (not a number) + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec2_isnan(vec2 v) { + return isnan(v[0]) || isnan(v[1]); +} + +/*! + * @brief check if all items are INFINITY + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec2_isinf(vec2 v) { + return isinf(v[0]) || isinf(v[1]); +} + +/*! + * @brief check if all items are valid number + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec2_isvalid(vec2 v) { + return !glm_vec2_isnan(v) && !glm_vec2_isinf(v); +} + +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +f_inline +void +glm_vec2_sign(vec2 v, vec2 dest) { + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); +} + +/*! + * @brief absolute value of v + * + * @param[in] v vector + * @param[out] dest destination + */ +f_inline +void +glm_vec2_abs(vec2 v, vec2 dest) { + dest[0] = fabsf(v[0]); + dest[1] = fabsf(v[1]); +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_sqrt(vec2 v, vec2 dest) { + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); +} + +/*! + * @brief treat vectors as complex numbers and multiply them as such. + * + * @param[in] a left number + * @param[in] b right number + * @param[out] dest destination number + */ +f_inline +void +glm_vec2_complex_mul(vec2 a, vec2 b, vec2 dest) { + float tr, ti; + tr = a[0] * b[0] - a[1] * b[1]; + ti = a[0] * b[1] + a[1] * b[0]; + dest[0] = tr; + dest[1] = ti; +} + +/*! + * @brief treat vectors as complex numbers and divide them as such. + * + * @param[in] a left number (numerator) + * @param[in] b right number (denominator) + * @param[out] dest destination number + */ +f_inline +void +glm_vec2_complex_div(vec2 a, vec2 b, vec2 dest) { + float tr, ti; + float const ibnorm2 = 1.0f / (b[0] * b[0] + b[1] * b[1]); + tr = ibnorm2 * (a[0] * b[0] + a[1] * b[1]); + ti = ibnorm2 * (a[1] * b[0] - a[0] * b[1]); + dest[0] = tr; + dest[1] = ti; +} + +/*! + * @brief treat the vector as a complex number and conjugate it as such. + * + * @param[in] a the number + * @param[out] dest destination number + */ +f_inline +void +glm_vec2_complex_conjugate(vec2 a, vec2 dest) { + dest[0] = a[0]; + dest[1] = -a[1]; +} + + + + +#define GLM_VEC2_ONE_INIT {1.0f, 1.0f} +#define GLM_VEC2_ZERO_INIT {0.0f, 0.0f} + +#define GLM_VEC2_ONE ((vec2)GLM_VEC2_ONE_INIT) +#define GLM_VEC2_ZERO ((vec2)GLM_VEC2_ZERO_INIT) + +/*! + * @brief init vec2 using another vector + * + * @param[in] v a vector + * @param[out] dest destination + */ +f_inline +void +glm_vec2(float * __restrict v, vec2 dest) { + dest[0] = v[0]; + dest[1] = v[1]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source + * @param[out] dest destination + */ +f_inline +void +glm_vec2_copy(vec2 a, vec2 dest) { + dest[0] = a[0]; + dest[1] = a[1]; +} + +/*! + * @brief make vector zero + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec2_zero(vec2 v) { + v[0] = v[1] = 0.0f; +} + +/*! + * @brief make vector one + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec2_one(vec2 v) { + v[0] = v[1] = 1.0f; +} + +/*! + * @brief vec2 dot product + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return dot product + */ +f_inline +float +glm_vec2_dot(vec2 a, vec2 b) { + return a[0] * b[0] + a[1] * b[1]; +} + +/*! + * @brief vec2 cross product + * + * REF: http://allenchou.net/2013/07/cross-product-of-2d-vectors/ + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return Z component of cross product + */ +f_inline +float +glm_vec2_cross(vec2 a, vec2 b) { + /* just calculate the z-component */ + return a[0] * b[1] - a[1] * b[0]; +} + +/*! + * @brief norm * norm (magnitude) of vec + * + * we can use this func instead of calling norm * norm, because it would call + * sqrtf fuction twice but with this func we can avoid func call, maybe this is + * not good name for this func + * + * @param[in] v vector + * + * @return norm * norm + */ +f_inline +float +glm_vec2_norm2(vec2 v) { + return glm_vec2_dot(v, v); +} + +/*! + * @brief norm (magnitude) of vec2 + * + * @param[in] vec vector + * + * @return norm + */ +f_inline +float +glm_vec2_norm(vec2 vec) { + return sqrtf(glm_vec2_norm2(vec)); +} + +/*! + * @brief add a vector to b vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_add(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; +} + +/*! + * @brief add scalar to v vector store result in dest (d = v + s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_adds(vec2 v, float s, vec2 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; +} + +/*! + * @brief subtract b vector from a vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_sub(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; +} + +/*! + * @brief subtract scalar from v vector store result in dest (d = v - s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_subs(vec2 v, float s, vec2 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; +} + +/*! + * @brief multiply two vector (component-wise multiplication) + * + * @param a v1 + * @param b v2 + * @param dest v3 = (a[0] * b[0], a[1] * b[1]) + */ +f_inline +void +glm_vec2_mul(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; +} + +/*! + * @brief multiply/scale vector with scalar: result = v * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_scale(vec2 v, float s, vec2 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; +} + +/*! + * @brief scale as vector specified: result = unit(v) * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_scale_as(vec2 v, float s, vec2 dest) { + float norm; + norm = glm_vec2_norm(v); + + if (norm == 0.0f) { + glm_vec2_zero(dest); + return; + } + + glm_vec2_scale(v, s / norm, dest); +} + +/*! + * @brief div vector with another component-wise division: d = a / b + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest result = (a[0]/b[0], a[1]/b[1]) + */ +f_inline +void +glm_vec2_div(vec2 a, vec2 b, vec2 dest) { + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; +} + +/*! + * @brief div vector with scalar: d = v / s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest result = (a[0]/s, a[1]/s) + */ +f_inline +void +glm_vec2_divs(vec2 v, float s, vec2 dest) { + dest[0] = v[0] / s; + dest[1] = v[1] / s; +} + +/*! + * @brief add two vectors and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +f_inline +void +glm_vec2_addadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] + b[0]; + dest[1] += a[1] + b[1]; +} + +/*! + * @brief sub two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +f_inline +void +glm_vec2_subadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] - b[0]; + dest[1] += a[1] - b[1]; +} + +/*! + * @brief mul two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec2_muladd(vec2 a, vec2 b, vec2 dest) { + dest[0] += a[0] * b[0]; + dest[1] += a[1] * b[1]; +} + +/*! + * @brief mul vector with scalar and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector + * @param[in] s scalar + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec2_muladds(vec2 a, float s, vec2 dest) { + dest[0] += a[0] * s; + dest[1] += a[1] * s; +} + +/*! + * @brief add max of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += max(a, b) + */ +f_inline +void +glm_vec2_maxadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += glm_max(a[0], b[0]); + dest[1] += glm_max(a[1], b[1]); +} + +/*! + * @brief add min of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += min(a, b) + */ +f_inline +void +glm_vec2_minadd(vec2 a, vec2 b, vec2 dest) { + dest[0] += glm_min(a[0], b[0]); + dest[1] += glm_min(a[1], b[1]); +} + +/*! + * @brief negate vector components and store result in dest + * + * @param[in] v vector + * @param[out] dest result vector + */ +f_inline +void +glm_vec2_negate_to(vec2 v, vec2 dest) { + dest[0] = -v[0]; + dest[1] = -v[1]; +} + +/*! + * @brief negate vector components + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec2_negate(vec2 v) { + glm_vec2_negate_to(v, v); +} + +/*! + * @brief normalize vector and store result in same vec + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec2_normalize(vec2 v) { + float norm; + + norm = glm_vec2_norm(v); + + if (norm == 0.0f) { + v[0] = v[1] = 0.0f; + return; + } + + glm_vec2_scale(v, 1.0f / norm, v); +} + +/*! + * @brief normalize vector to dest + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_vec2_normalize_to(vec2 v, vec2 dest) { + float norm; + + norm = glm_vec2_norm(v); + + if (norm == 0.0f) { + glm_vec2_zero(dest); + return; + } + + glm_vec2_scale(v, 1.0f / norm, dest); +} + +/*! + * @brief rotate vec2 around origin by angle (CCW: counterclockwise) + * + * Formula: + * 𝑥2 = cos(a)𝑥1 − sin(a)𝑦1 + * 𝑦2 = sin(a)𝑥1 + cos(a)𝑦1 + * + * @param[in] v vector to rotate + * @param[in] angle angle by radians + * @param[out] dest destination vector + */ +f_inline +void +glm_vec2_rotate(vec2 v, float angle, vec2 dest) { + float c, s, x1, y1; + + c = cosf(angle); + s = sinf(angle); + + x1 = v[0]; + y1 = v[1]; + + dest[0] = c * x1 - s * y1; + dest[1] = s * x1 + c * y1; +} + +/** + * @brief squared distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns squared distance (distance * distance) + */ +f_inline +float +glm_vec2_distance2(vec2 a, vec2 b) { + return glm_pow2(b[0] - a[0]) + glm_pow2(b[1] - a[1]); +} + +/** + * @brief distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns distance + */ +f_inline +float +glm_vec2_distance(vec2 a, vec2 b) { + return sqrtf(glm_vec2_distance2(a, b)); +} + +/*! + * @brief max values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec2_maxv(vec2 a, vec2 b, vec2 dest) { + dest[0] = glm_max(a[0], b[0]); + dest[1] = glm_max(a[1], b[1]); +} + +/*! + * @brief min values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec2_minv(vec2 a, vec2 b, vec2 dest) { + dest[0] = glm_min(a[0], b[0]); + dest[1] = glm_min(a[1], b[1]); +} + +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minval minimum value + * @param[in] maxval maximum value + */ +f_inline +void +glm_vec2_clamp(vec2 v, float minval, float maxval) { + v[0] = glm_clamp(v[0], minval, maxval); + v[1] = glm_clamp(v[1], minval, maxval); +} + +/*! + * @brief linear interpolation between two vector + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec2_lerp(vec2 from, vec2 to, float t, vec2 dest) { + vec2 s, v; + + /* from + s * (to - from) */ + glm_vec2_fill(s, glm_clamp_zo(t)); + glm_vec2_sub(to, from, v); + glm_vec2_mul(s, v, v); + glm_vec2_add(from, v, dest); +} + + + + + + + + +/*! + * @brief fill a vector with specified value + * + * @param[in] val value + * @param[out] d dest + */ +f_inline +void +glm_vec3_broadcast(float val, vec3 d) { + d[0] = d[1] = d[2] = val; +} + +/*! + * @brief fill a vector with specified value + * + * @param[out] v dest + * @param[in] val value + */ +f_inline +void +glm_vec3_fill(vec3 v, float val) { + v[0] = v[1] = v[2] = val; +} + +/*! + * @brief check if vector is equal to value (without epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +f_inline +byte +glm_vec3_eq(vec3 v, float val) { + return v[0] == val && v[0] == v[1] && v[0] == v[2]; +} + +/*! + * @brief check if vector is equal to value (with epsilon) + * + * @param[in] v vector + * @param[in] val value + */ +f_inline +byte +glm_vec3_eq_eps(vec3 v, float val) { + return fabsf(v[0] - val) <= GLM_FLT_EPSILON + && fabsf(v[1] - val) <= GLM_FLT_EPSILON + && fabsf(v[2] - val) <= GLM_FLT_EPSILON; +} + +/*! + * @brief check if vectors members are equal (without epsilon) + * + * @param[in] v vector + */ +f_inline +byte +glm_vec3_eq_all(vec3 v) { + return glm_vec3_eq_eps(v, v[0]); +} + +/*! + * @brief check if vector is equal to another (without epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +f_inline +byte +glm_vec3_eqv(vec3 a, vec3 b) { + return a[0] == b[0] + && a[1] == b[1] + && a[2] == b[2]; +} + +/*! + * @brief check if vector is equal to another (with epsilon) + * + * @param[in] a vector + * @param[in] b vector + */ +f_inline +byte +glm_vec3_eqv_eps(vec3 a, vec3 b) { + return fabsf(a[0] - b[0]) <= GLM_FLT_EPSILON + && fabsf(a[1] - b[1]) <= GLM_FLT_EPSILON + && fabsf(a[2] - b[2]) <= GLM_FLT_EPSILON; +} + +/*! + * @brief max value of vector + * + * @param[in] v vector + */ +f_inline +float +glm_vec3_max(vec3 v) { + float max; + + max = v[0]; + if (v[1] > max) + max = v[1]; + if (v[2] > max) + max = v[2]; + + return max; +} + +/*! + * @brief min value of vector + * + * @param[in] v vector + */ +f_inline +float +glm_vec3_min(vec3 v) { + float min; + + min = v[0]; + if (v[1] < min) + min = v[1]; + if (v[2] < min) + min = v[2]; + + return min; +} + +/*! + * @brief check if all items are NaN (not a number) + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec3_isnan(vec3 v) { + return isnan(v[0]) || isnan(v[1]) || isnan(v[2]); +} + +/*! + * @brief check if all items are INFINITY + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec3_isinf(vec3 v) { + return isinf(v[0]) || isinf(v[1]) || isinf(v[2]); +} + +/*! + * @brief check if all items are valid number + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec3_isvalid(vec3 v) { + return !glm_vec3_isnan(v) && !glm_vec3_isinf(v); +} + +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +f_inline +void +glm_vec3_sign(vec3 v, vec3 dest) { + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); + dest[2] = glm_signf(v[2]); +} + +/*! + * @brief absolute value of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_abs(vec3 v, vec3 dest) { + dest[0] = fabsf(v[0]); + dest[1] = fabsf(v[1]); + dest[2] = fabsf(v[2]); +} + +/*! + * @brief fractional part of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_fract(vec3 v, vec3 dest) { + dest[0] = fminf(v[0] - floorf(v[0]), 0.999999940395355224609375f); + dest[1] = fminf(v[1] - floorf(v[1]), 0.999999940395355224609375f); + dest[2] = fminf(v[2] - floorf(v[2]), 0.999999940395355224609375f); +} + +/*! + * @brief vector reduction by summation + * @warning could overflow + * + * @param[in] v vector + * @return sum of all vector's elements + */ +f_inline +float +glm_vec3_hadd(vec3 v) { + return v[0] + v[1] + v[2]; +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_sqrt(vec3 v, vec3 dest) { + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); + dest[2] = sqrtf(v[2]); +} + + + + + + + +/*! + * @brief fill a vector with specified value + * + * @param val value + * @param d dest + */ +f_inline +void +glm_vec4_broadcast(float val, vec4 d) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(d, _mm_set1_ps(val)); +#else + d[0] = d[1] = d[2] = d[3] = val; +#endif +} + +/*! + * @brief fill a vector with specified value + * + * @param v dest + * @param val value + */ +f_inline +void +glm_vec4_fill(vec4 v, float val) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(v, _mm_set1_ps(val)); +#else + v[0] = v[1] = v[2] = v[3] = val; +#endif +} + +/*! + * @brief check if vector is equal to value (without epsilon) + * + * @param v vector + * @param val value + */ +f_inline +byte +glm_vec4_eq(vec4 v, float val) { + return v[0] == val + && v[0] == v[1] + && v[0] == v[2] + && v[0] == v[3]; +} + +/*! + * @brief check if vector is equal to value (with epsilon) + * + * @param v vector + * @param val value + */ +f_inline +byte +glm_vec4_eq_eps(vec4 v, float val) { + return fabsf(v[0] - val) <= GLM_FLT_EPSILON + && fabsf(v[1] - val) <= GLM_FLT_EPSILON + && fabsf(v[2] - val) <= GLM_FLT_EPSILON + && fabsf(v[3] - val) <= GLM_FLT_EPSILON; +} + +/*! + * @brief check if vectors members are equal (without epsilon) + * + * @param v vector + */ +f_inline +byte +glm_vec4_eq_all(vec4 v) { + return glm_vec4_eq_eps(v, v[0]); +} + +/*! + * @brief check if vector is equal to another (without epsilon) + * + * @param a vector + * @param b vector + */ +f_inline +byte +glm_vec4_eqv(vec4 a, vec4 b) { + return a[0] == b[0] + && a[1] == b[1] + && a[2] == b[2] + && a[3] == b[3]; +} + +/*! + * @brief check if vector is equal to another (with epsilon) + * + * @param a vector + * @param b vector + */ +f_inline +byte +glm_vec4_eqv_eps(vec4 a, vec4 b) { + return fabsf(a[0] - b[0]) <= GLM_FLT_EPSILON + && fabsf(a[1] - b[1]) <= GLM_FLT_EPSILON + && fabsf(a[2] - b[2]) <= GLM_FLT_EPSILON + && fabsf(a[3] - b[3]) <= GLM_FLT_EPSILON; +} + +/*! + * @brief max value of vector + * + * @param v vector + */ +f_inline +float +glm_vec4_max(vec4 v) { + float max; + + max = glm_vec3_max(v); + if (v[3] > max) + max = v[3]; + + return max; +} + +/*! + * @brief min value of vector + * + * @param v vector + */ +f_inline +float +glm_vec4_min(vec4 v) { + float min; + + min = glm_vec3_min(v); + if (v[3] < min) + min = v[3]; + + return min; +} + +/*! + * @brief check if one of items is NaN (not a number) + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec4_isnan(vec4 v) { + return isnan(v[0]) || isnan(v[1]) || isnan(v[2]) || isnan(v[3]); +} + +/*! + * @brief check if one of items is INFINITY + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec4_isinf(vec4 v) { + return isinf(v[0]) || isinf(v[1]) || isinf(v[2]) || isinf(v[3]); +} + +/*! + * @brief check if all items are valid number + * you should only use this in DEBUG mode or very critical asserts + * + * @param[in] v vector + */ +f_inline +byte +glm_vec4_isvalid(vec4 v) { + return !glm_vec4_isnan(v) && !glm_vec4_isinf(v); +} + +/*! + * @brief get sign of 32 bit float as +1, -1, 0 + * + * Important: It returns 0 for zero/NaN input + * + * @param v vector + */ +f_inline +void +glm_vec4_sign(vec4 v, vec4 dest) { +#if defined( __SSE2__ ) || defined( __SSE2__ ) + __m128 x0, x1, x2, x3, x4; + + x0 = glmm_load(v); + x1 = _mm_set_ps(0.0f, 0.0f, 1.0f, -1.0f); + x2 = glmm_splat(x1, 2); + + x3 = _mm_and_ps(_mm_cmpgt_ps(x0, x2), glmm_splat(x1, 1)); + x4 = _mm_and_ps(_mm_cmplt_ps(x0, x2), glmm_splat(x1, 0)); + + glmm_store(dest, _mm_or_ps(x3, x4)); +#else + dest[0] = glm_signf(v[0]); + dest[1] = glm_signf(v[1]); + dest[2] = glm_signf(v[2]); + dest[3] = glm_signf(v[3]); +#endif +} + +/*! + * @brief absolute value of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_abs(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, glmm_abs(glmm_load(v))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vabsq_f32(vld1q_f32(v))); +#else + dest[0] = fabsf(v[0]); + dest[1] = fabsf(v[1]); + dest[2] = fabsf(v[2]); + dest[3] = fabsf(v[3]); +#endif +} + +/*! + * @brief fractional part of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_fract(vec4 v, vec4 dest) { + dest[0] = fminf(v[0] - floorf(v[0]), 0.999999940395355224609375f); + dest[1] = fminf(v[1] - floorf(v[1]), 0.999999940395355224609375f); + dest[2] = fminf(v[2] - floorf(v[2]), 0.999999940395355224609375f); + dest[3] = fminf(v[3] - floorf(v[3]), 0.999999940395355224609375f); +} + +/*! + * @brief vector reduction by summation + * @warning could overflow + * + * @param[in] v vector + * @return sum of all vector's elements + */ +f_inline +float +glm_vec4_hadd(vec4 v) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + return glmm_hadd(glmm_load(v)); +#else + return v[0] + v[1] + v[2] + v[3]; +#endif +} + +/*! + * @brief square root of each vector item + * + * @param[in] v vector + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_sqrt(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_sqrt_ps(glmm_load(v))); +#else + dest[0] = sqrtf(v[0]); + dest[1] = sqrtf(v[1]); + dest[2] = sqrtf(v[2]); + dest[3] = sqrtf(v[3]); +#endif +} + + + + + + + + +#define GLM_VEC4_ONE_INIT {1.0f, 1.0f, 1.0f, 1.0f} +#define GLM_VEC4_BLACK_INIT {0.0f, 0.0f, 0.0f, 1.0f} +#define GLM_VEC4_ZERO_INIT {0.0f, 0.0f, 0.0f, 0.0f} + +#define GLM_VEC4_ONE ((vec4)GLM_VEC4_ONE_INIT) +#define GLM_VEC4_BLACK ((vec4)GLM_VEC4_BLACK_INIT) +#define GLM_VEC4_ZERO ((vec4)GLM_VEC4_ZERO_INIT) + +#define GLM_XXXX GLM_SHUFFLE4(0, 0, 0, 0) +#define GLM_YYYY GLM_SHUFFLE4(1, 1, 1, 1) +#define GLM_ZZZZ GLM_SHUFFLE4(2, 2, 2, 2) +#define GLM_WWWW GLM_SHUFFLE4(3, 3, 3, 3) +#define GLM_WZYX GLM_SHUFFLE4(0, 1, 2, 3) + +/*! + * @brief init vec4 using vec3 + * + * @param[in] v3 vector3 + * @param[in] last last item + * @param[out] dest destination + */ +f_inline +void +glm_vec4(vec3 v3, float last, vec4 dest) { + dest[0] = v3[0]; + dest[1] = v3[1]; + dest[2] = v3[2]; + dest[3] = last; +} + +/*! + * @brief copy first 3 members of [a] to [dest] + * + * @param[in] a source + * @param[out] dest destination + */ +f_inline +void +glm_vec4_copy3(vec4 a, vec3 dest) { + dest[0] = a[0]; + dest[1] = a[1]; + dest[2] = a[2]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_vec4_copy(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, glmm_load(v)); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vld1q_f32(v)); +#else + dest[0] = v[0]; + dest[1] = v[1]; + dest[2] = v[2]; + dest[3] = v[3]; +#endif +} + +/*! + * @brief copy all members of [a] to [dest] + * + * alignment is not required + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_vec4_ucopy(vec4 v, vec4 dest) { + dest[0] = v[0]; + dest[1] = v[1]; + dest[2] = v[2]; + dest[3] = v[3]; +} + +/*! + * @brief make vector zero + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec4_zero(vec4 v) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(v, _mm_setzero_ps()); +#elif defined(CGLM_NEON_FP) + vst1q_f32(v, vdupq_n_f32(0.0f)); +#else + v[0] = 0.0f; + v[1] = 0.0f; + v[2] = 0.0f; + v[3] = 0.0f; +#endif +} + +/*! + * @brief make vector one + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec4_one(vec4 v) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(v, _mm_set1_ps(1.0f)); +#elif defined(CGLM_NEON_FP) + vst1q_f32(v, vdupq_n_f32(1.0f)); +#else + v[0] = 1.0f; + v[1] = 1.0f; + v[2] = 1.0f; + v[3] = 1.0f; +#endif +} + +/*! + * @brief vec4 dot product + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return dot product + */ +f_inline +float +glm_vec4_dot(vec4 a, vec4 b) { +#if defined(CGLM_SIMD) + return glmm_dot(glmm_load(a), glmm_load(b)); +#else + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +#endif +} + +/*! + * @brief norm * norm (magnitude) of vec + * + * we can use this func instead of calling norm * norm, because it would call + * sqrtf fuction twice but with this func we can avoid func call, maybe this is + * not good name for this func + * + * @param[in] v vec4 + * + * @return norm * norm + */ +f_inline +float +glm_vec4_norm2(vec4 v) { + return glm_vec4_dot(v, v); +} + +/*! + * @brief euclidean norm (magnitude), also called L2 norm + * this will give magnitude of vector in euclidean space + * + * @param[in] v vector + * + * @return norm + */ +f_inline +float +glm_vec4_norm(vec4 v) { +#if defined(CGLM_SIMD) + return glmm_norm(glmm_load(v)); +#else + return sqrtf(glm_vec4_dot(v, v)); +#endif +} + +/*! + * @brief L1 norm of vec4 + * Also known as Manhattan Distance or Taxicab norm. + * L1 Norm is the sum of the magnitudes of the vectors in a space. + * It is calculated as the sum of the absolute values of the vector components. + * In this norm, all the components of the vector are weighted equally. + * + * This computes: + * L1 norm = |v[0]| + |v[1]| + |v[2]| + |v[3]| + * + * @param[in] v vector + * + * @return L1 norm + */ +f_inline +float +glm_vec4_norm_one(vec4 v) { +#if defined(CGLM_SIMD) + return glmm_norm_one(glmm_load(v)); +#else + vec4 t; + glm_vec4_abs(v, t); + return glm_vec4_hadd(t); +#endif +} + +/*! + * @brief infinity norm of vec4 + * Also known as Maximum norm. + * Infinity Norm is the largest magnitude among each element of a vector. + * It is calculated as the maximum of the absolute values of the vector components. + * + * This computes: + * inf norm = max(|v[0]|, |v[1]|, |v[2]|, |v[3]|) + * + * @param[in] v vector + * + * @return infinity norm + */ +f_inline +float +glm_vec4_norm_inf(vec4 v) { +#if defined(CGLM_SIMD) + return glmm_norm_inf(glmm_load(v)); +#else + vec4 t; + glm_vec4_abs(v, t); + return glm_vec4_max(t); +#endif +} + +/*! + * @brief add b vector to a vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_add(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(a), vld1q_f32(b))); +#else + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; + dest[3] = a[3] + b[3]; +#endif +} + +/*! + * @brief add scalar to v vector store result in dest (d = v + vec(s)) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_adds(vec4 v, float s, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(v), _mm_set1_ps(s))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(v), vdupq_n_f32(s))); +#else + dest[0] = v[0] + s; + dest[1] = v[1] + s; + dest[2] = v[2] + s; + dest[3] = v[3] + s; +#endif +} + +/*! + * @brief subtract b vector from a vector store result in dest (d = a - b) + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_sub(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_sub_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vsubq_f32(vld1q_f32(a), vld1q_f32(b))); +#else + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; + dest[3] = a[3] - b[3]; +#endif +} + +/*! + * @brief subtract scalar from v vector store result in dest (d = v - vec(s)) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_subs(vec4 v, float s, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_sub_ps(glmm_load(v), _mm_set1_ps(s))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vsubq_f32(vld1q_f32(v), vdupq_n_f32(s))); +#else + dest[0] = v[0] - s; + dest[1] = v[1] - s; + dest[2] = v[2] - s; + dest[3] = v[3] - s; +#endif +} + +/*! + * @brief multiply two vector (component-wise multiplication) + * + * @param a vector1 + * @param b vector2 + * @param dest dest = (a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]) + */ +f_inline +void +glm_vec4_mul(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_mul_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vmulq_f32(vld1q_f32(a), vld1q_f32(b))); +#else + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; + dest[3] = a[3] * b[3]; +#endif +} + +/*! + * @brief multiply/scale vec4 vector with scalar: result = v * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_scale(vec4 v, float s, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_mul_ps(glmm_load(v), _mm_set1_ps(s))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vmulq_f32(vld1q_f32(v), vdupq_n_f32(s))); +#else + dest[0] = v[0] * s; + dest[1] = v[1] * s; + dest[2] = v[2] * s; + dest[3] = v[3] * s; +#endif +} + +/*! + * @brief make vec4 vector scale as specified: result = unit(v) * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_scale_as(vec4 v, float s, vec4 dest) { + float norm; + norm = glm_vec4_norm(v); + + if (norm == 0.0f) { + glm_vec4_zero(dest); + return; + } + + glm_vec4_scale(v, s / norm, dest); +} + +/*! + * @brief div vector with another component-wise division: d = a / b + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest result = (a[0]/b[0], a[1]/b[1], a[2]/b[2], a[3]/b[3]) + */ +f_inline +void +glm_vec4_div(vec4 a, vec4 b, vec4 dest) { +#if defined(CGLM_SIMD) + glmm_store(dest, glmm_div(glmm_load(a), glmm_load(b))); +#else + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; + dest[2] = a[2] / b[2]; + dest[3] = a[3] / b[3]; +#endif +} + +/*! + * @brief div vec4 vector with scalar: d = v / s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec4_divs(vec4 v, float s, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_div_ps(glmm_load(v), _mm_set1_ps(s))); +#else + glm_vec4_scale(v, 1.0f / s, dest); +#endif +} + +/*! + * @brief add two vectors and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +f_inline +void +glm_vec4_addadd(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(dest), + _mm_add_ps(glmm_load(a), + glmm_load(b)))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(dest), + vaddq_f32(vld1q_f32(a), + vld1q_f32(b)))); +#else + dest[0] += a[0] + b[0]; + dest[1] += a[1] + b[1]; + dest[2] += a[2] + b[2]; + dest[3] += a[3] + b[3]; +#endif +} + +/*! + * @brief sub two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a - b) + */ +f_inline +void +glm_vec4_subadd(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(dest), + _mm_sub_ps(glmm_load(a), + glmm_load(b)))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(dest), + vsubq_f32(vld1q_f32(a), + vld1q_f32(b)))); +#else + dest[0] += a[0] - b[0]; + dest[1] += a[1] - b[1]; + dest[2] += a[2] - b[2]; + dest[3] += a[3] - b[3]; +#endif +} + +/*! + * @brief mul two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec4_muladd(vec4 a, vec4 b, vec4 dest) { +#if defined(CGLM_SIMD) + glmm_store(dest, glmm_fmadd(glmm_load(a), glmm_load(b), glmm_load(dest))); +#else + dest[0] += a[0] * b[0]; + dest[1] += a[1] * b[1]; + dest[2] += a[2] * b[2]; + dest[3] += a[3] * b[3]; +#endif +} + +/*! + * @brief mul vector with scalar and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector + * @param[in] s scalar + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec4_muladds(vec4 a, float s, vec4 dest) { +#if defined(CGLM_SIMD) + glmm_store(dest, glmm_fmadd(glmm_load(a), glmm_set1(s), glmm_load(dest))); +#else + dest[0] += a[0] * s; + dest[1] += a[1] * s; + dest[2] += a[2] * s; + dest[3] += a[3] * s; +#endif +} + +/*! + * @brief add max of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += max(a, b) + */ +f_inline +void +glm_vec4_maxadd(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(dest), + _mm_max_ps(glmm_load(a), + glmm_load(b)))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(dest), + vmaxq_f32(vld1q_f32(a), + vld1q_f32(b)))); +#else + dest[0] += glm_max(a[0], b[0]); + dest[1] += glm_max(a[1], b[1]); + dest[2] += glm_max(a[2], b[2]); + dest[3] += glm_max(a[3], b[3]); +#endif +} + +/*! + * @brief add min of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += min(a, b) + */ +f_inline +void +glm_vec4_minadd(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_add_ps(glmm_load(dest), + _mm_min_ps(glmm_load(a), + glmm_load(b)))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vaddq_f32(vld1q_f32(dest), + vminq_f32(vld1q_f32(a), + vld1q_f32(b)))); +#else + dest[0] += glm_min(a[0], b[0]); + dest[1] += glm_min(a[1], b[1]); + dest[2] += glm_min(a[2], b[2]); + dest[3] += glm_min(a[3], b[3]); +#endif +} + +/*! + * @brief negate vector components and store result in dest + * + * @param[in] v vector + * @param[out] dest result vector + */ +f_inline +void +glm_vec4_negate_to(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_xor_ps(glmm_load(v), _mm_set1_ps(-0.0f))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vnegq_f32(vld1q_f32(v))); +#else + dest[0] = -v[0]; + dest[1] = -v[1]; + dest[2] = -v[2]; + dest[3] = -v[3]; +#endif +} + +/*! + * @brief flip sign of all vec4 members + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec4_negate(vec4 v) { + glm_vec4_negate_to(v, v); +} + +/*! + * @brief normalize vec4 to dest + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_vec4_normalize_to(vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + __m128 xdot, x0; + float dot; + + x0 = glmm_load(v); + xdot = glmm_vdot(x0, x0); + dot = _mm_cvtss_f32(xdot); + + if (dot == 0.0f) { + glmm_store(dest, _mm_setzero_ps()); + return; + } + + glmm_store(dest, _mm_div_ps(x0, _mm_sqrt_ps(xdot))); +#else + float norm; + + norm = glm_vec4_norm(v); + + if (norm == 0.0f) { + glm_vec4_zero(dest); + return; + } + + glm_vec4_scale(v, 1.0f / norm, dest); +#endif +} + +/*! + * @brief normalize vec4 and store result in same vec + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec4_normalize(vec4 v) { + glm_vec4_normalize_to(v, v); +} + +/** + * @brief distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns distance + */ +f_inline +float +glm_vec4_distance(vec4 a, vec4 b) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + return glmm_norm(_mm_sub_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + return glmm_norm(vsubq_f32(glmm_load(a), glmm_load(b))); +#else + return sqrtf(glm_pow2(a[0] - b[0]) + + glm_pow2(a[1] - b[1]) + + glm_pow2(a[2] - b[2]) + + glm_pow2(a[3] - b[3])); +#endif +} + +/** + * @brief squared distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns squared distance + */ +f_inline +float +glm_vec4_distance2(vec4 a, vec4 b) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + return glmm_norm2(_mm_sub_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + return glmm_norm2(vsubq_f32(glmm_load(a), glmm_load(b))); +#else + return glm_pow2(a[0] - b[0]) + + glm_pow2(a[1] - b[1]) + + glm_pow2(a[2] - b[2]) + + glm_pow2(a[3] - b[3]); +#endif +} + +/*! + * @brief max values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec4_maxv(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_max_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vmaxq_f32(vld1q_f32(a), vld1q_f32(b))); +#else + dest[0] = glm_max(a[0], b[0]); + dest[1] = glm_max(a[1], b[1]); + dest[2] = glm_max(a[2], b[2]); + dest[3] = glm_max(a[3], b[3]); +#endif +} + +/*! + * @brief min values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec4_minv(vec4 a, vec4 b, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest, _mm_min_ps(glmm_load(a), glmm_load(b))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest, vminq_f32(vld1q_f32(a), vld1q_f32(b))); +#else + dest[0] = glm_min(a[0], b[0]); + dest[1] = glm_min(a[1], b[1]); + dest[2] = glm_min(a[2], b[2]); + dest[3] = glm_min(a[3], b[3]); +#endif +} + +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +void +glm_vec4_clamp(vec4 v, float minVal, float maxVal) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(v, _mm_min_ps(_mm_max_ps(glmm_load(v), _mm_set1_ps(minVal)), + _mm_set1_ps(maxVal))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(v, vminq_f32(vmaxq_f32(vld1q_f32(v), vdupq_n_f32(minVal)), + vdupq_n_f32(maxVal))); +#else + v[0] = glm_clamp(v[0], minVal, maxVal); + v[1] = glm_clamp(v[1], minVal, maxVal); + v[2] = glm_clamp(v[2], minVal, maxVal); + v[3] = glm_clamp(v[3], minVal, maxVal); +#endif +} + +/*! + * @brief linear interpolation between two vectors + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec4_lerp(vec4 from, vec4 to, float t, vec4 dest) { + vec4 s, v; + + /* from + s * (to - from) */ + glm_vec4_broadcast(t, s); + glm_vec4_sub(to, from, v); + glm_vec4_mul(s, v, v); + glm_vec4_add(from, v, dest); +} + +/*! + * @brief linear interpolation between two vectors (clamped) + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec4_lerpc(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerp(from, to, glm_clamp_zo(t), dest); +} + +/*! + * @brief linear interpolation between two vectors + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec4_mix(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerp(from, to, t, dest); +} + +/*! + * @brief linear interpolation between two vectors (clamped) + * + * formula: from + t * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec4_mixc(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerpc(from, to, t, dest); +} + +/*! + * @brief threshold function (unidimensional) + * + * @param[in] edge threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec4_step_uni(float edge, vec4 x, vec4 dest) { + dest[0] = glm_step(edge, x[0]); + dest[1] = glm_step(edge, x[1]); + dest[2] = glm_step(edge, x[2]); + dest[3] = glm_step(edge, x[3]); +} + +/*! + * @brief threshold function + * + * @param[in] edge threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec4_step(vec4 edge, vec4 x, vec4 dest) { + dest[0] = glm_step(edge[0], x[0]); + dest[1] = glm_step(edge[1], x[1]); + dest[2] = glm_step(edge[2], x[2]); + dest[3] = glm_step(edge[3], x[3]); +} + +/*! + * @brief threshold function with a smooth transition (unidimensional) + * + * @param[in] edge0 low threshold + * @param[in] edge1 high threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec4_smoothstep_uni(float edge0, float edge1, vec4 x, vec4 dest) { + dest[0] = glm_smoothstep(edge0, edge1, x[0]); + dest[1] = glm_smoothstep(edge0, edge1, x[1]); + dest[2] = glm_smoothstep(edge0, edge1, x[2]); + dest[3] = glm_smoothstep(edge0, edge1, x[3]); +} + +/*! + * @brief threshold function with a smooth transition + * + * @param[in] edge0 low threshold + * @param[in] edge1 high threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec4_smoothstep(vec4 edge0, vec4 edge1, vec4 x, vec4 dest) { + dest[0] = glm_smoothstep(edge0[0], edge1[0], x[0]); + dest[1] = glm_smoothstep(edge0[1], edge1[1], x[1]); + dest[2] = glm_smoothstep(edge0[2], edge1[2], x[2]); + dest[3] = glm_smoothstep(edge0[3], edge1[3], x[3]); +} + +/*! + * @brief smooth Hermite interpolation between two vectors + * + * formula: t^2 * (3 - 2*t) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec4_smoothinterp(vec4 from, vec4 to, float t, vec4 dest) { + vec4 s, v; + + /* from + smoothstep * (to - from) */ + glm_vec4_broadcast(glm_smooth(t), s); + glm_vec4_sub(to, from, v); + glm_vec4_mul(s, v, v); + glm_vec4_add(from, v, dest); +} + +/*! + * @brief smooth Hermite interpolation between two vectors (clamped) + * + * formula: t^2 * (3 - 2*t) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec4_smoothinterpc(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_smoothinterp(from, to, glm_clamp_zo(t), dest); +} + +/*! + * @brief helper to fill vec4 as [S^3, S^2, S, 1] + * + * @param[in] s parameter + * @param[out] dest destination + */ +f_inline +void +glm_vec4_cubic(float s, vec4 dest) { + float ss; + + ss = s * s; + + dest[0] = ss * s; + dest[1] = ss; + dest[2] = s; + dest[3] = 1.0f; +} + +/*! + * @brief swizzle vector components + * + * you can use existin masks e.g. GLM_XXXX, GLM_WZYX + * + * @param[in] v source + * @param[in] mask mask + * @param[out] dest destination + */ +f_inline +void +glm_vec4_swizzle(vec4 v, int mask, vec4 dest) { + vec4 t; + + t[0] = v[(mask & (3 << 0))]; + t[1] = v[(mask & (3 << 2)) >> 2]; + t[2] = v[(mask & (3 << 4)) >> 4]; + t[3] = v[(mask & (3 << 6)) >> 6]; + + glm_vec4_copy(t, dest); +} + + + + + + + + + +#define GLM_VEC3_ONE_INIT {1.0f, 1.0f, 1.0f} +#define GLM_VEC3_ZERO_INIT {0.0f, 0.0f, 0.0f} + +#define GLM_VEC3_ONE ((vec3)GLM_VEC3_ONE_INIT) +#define GLM_VEC3_ZERO ((vec3)GLM_VEC3_ZERO_INIT) + +#define GLM_YUP ((vec3){0.0f, 1.0f, 0.0f}) +#define GLM_ZUP ((vec3){0.0f, 0.0f, 1.0f}) +#define GLM_XUP ((vec3){1.0f, 0.0f, 0.0f}) +#define GLM_FORWARD ((vec3){0.0f, 0.0f, -1.0f}) + +#define GLM_XXX GLM_SHUFFLE3(0, 0, 0) +#define GLM_YYY GLM_SHUFFLE3(1, 1, 1) +#define GLM_ZZZ GLM_SHUFFLE3(2, 2, 2) +#define GLM_ZYX GLM_SHUFFLE3(0, 1, 2) + +/*! + * @brief init vec3 using vec4 + * + * @param[in] v4 vector4 + * @param[out] dest destination + */ +f_inline +void +glm_vec3(vec4 v4, vec3 dest) { + dest[0] = v4[0]; + dest[1] = v4[1]; + dest[2] = v4[2]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source + * @param[out] dest destination + */ +f_inline +void +glm_vec3_copy(vec3 a, vec3 dest) { + dest[0] = a[0]; + dest[1] = a[1]; + dest[2] = a[2]; +} + +/*! + * @brief make vector zero + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec3_zero(vec3 v) { + v[0] = v[1] = v[2] = 0.0f; +} + +/*! + * @brief make vector one + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec3_one(vec3 v) { + v[0] = v[1] = v[2] = 1.0f; +} + +/*! + * @brief vec3 dot product + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return dot product + */ +f_inline +float +glm_vec3_dot(vec3 a, vec3 b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +/*! + * @brief norm * norm (magnitude) of vec + * + * we can use this func instead of calling norm * norm, because it would call + * sqrtf fuction twice but with this func we can avoid func call, maybe this is + * not good name for this func + * + * @param[in] v vector + * + * @return norm * norm + */ +f_inline +float +glm_vec3_norm2(vec3 v) { + return glm_vec3_dot(v, v); +} + +/*! + * @brief euclidean norm (magnitude), also called L2 norm + * this will give magnitude of vector in euclidean space + * + * @param[in] v vector + * + * @return norm + */ +f_inline +float +glm_vec3_norm(vec3 v) { + return sqrtf(glm_vec3_norm2(v)); +} + +/*! + * @brief L1 norm of vec3 + * Also known as Manhattan Distance or Taxicab norm. + * L1 Norm is the sum of the magnitudes of the vectors in a space. + * It is calculated as the sum of the absolute values of the vector components. + * In this norm, all the components of the vector are weighted equally. + * + * This computes: + * R = |v[0]| + |v[1]| + |v[2]| + * + * @param[in] v vector + * + * @return L1 norm + */ +f_inline +float +glm_vec3_norm_one(vec3 v) { + vec3 t; + glm_vec3_abs(v, t); + return glm_vec3_hadd(t); +} + +/*! + * @brief infinity norm of vec3 + * Also known as Maximum norm. + * Infinity Norm is the largest magnitude among each element of a vector. + * It is calculated as the maximum of the absolute values of the vector components. + * + * This computes: + * inf norm = max(|v[0]|, |v[1]|, |v[2]|) + * + * @param[in] v vector + * + * @return infinity norm + */ +f_inline +float +glm_vec3_norm_inf(vec3 v) { + vec3 t; + glm_vec3_abs(v, t); + return glm_vec3_max(t); +} + +/*! + * @brief add a vector to b vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_add(vec3 a, vec3 b, vec3 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; +} + +/*! + * @brief add scalar to v vector store result in dest (d = v + s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_adds(vec3 v, float s, vec3 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; + dest[2] = v[2] + s; +} + +/*! + * @brief subtract b vector from a vector store result in dest + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_sub(vec3 a, vec3 b, vec3 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; +} + +/*! + * @brief subtract scalar from v vector store result in dest (d = v - s) + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_subs(vec3 v, float s, vec3 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; + dest[2] = v[2] - s; +} + +/*! + * @brief multiply two vector (component-wise multiplication) + * + * @param a vector1 + * @param b vector2 + * @param dest v3 = (a[0] * b[0], a[1] * b[1], a[2] * b[2]) + */ +f_inline +void +glm_vec3_mul(vec3 a, vec3 b, vec3 dest) { + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; +} + +/*! + * @brief multiply/scale vec3 vector with scalar: result = v * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_scale(vec3 v, float s, vec3 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; + dest[2] = v[2] * s; +} + +/*! + * @brief make vec3 vector scale as specified: result = unit(v) * s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination vector + */ +f_inline +void +glm_vec3_scale_as(vec3 v, float s, vec3 dest) { + float norm; + norm = glm_vec3_norm(v); + + if (norm == 0.0f) { + glm_vec3_zero(dest); + return; + } + + glm_vec3_scale(v, s / norm, dest); +} + +/*! + * @brief div vector with another component-wise division: d = a / b + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest result = (a[0]/b[0], a[1]/b[1], a[2]/b[2]) + */ +f_inline +void +glm_vec3_div(vec3 a, vec3 b, vec3 dest) { + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; + dest[2] = a[2] / b[2]; +} + +/*! + * @brief div vector with scalar: d = v / s + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest result = (a[0]/s, a[1]/s, a[2]/s) + */ +f_inline +void +glm_vec3_divs(vec3 v, float s, vec3 dest) { + dest[0] = v[0] / s; + dest[1] = v[1] / s; + dest[2] = v[2] / s; +} + +/*! + * @brief add two vectors and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +f_inline +void +glm_vec3_addadd(vec3 a, vec3 b, vec3 dest) { + dest[0] += a[0] + b[0]; + dest[1] += a[1] + b[1]; + dest[2] += a[2] + b[2]; +} + +/*! + * @brief sub two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a + b) + */ +f_inline +void +glm_vec3_subadd(vec3 a, vec3 b, vec3 dest) { + dest[0] += a[0] - b[0]; + dest[1] += a[1] - b[1]; + dest[2] += a[2] - b[2]; +} + +/*! + * @brief mul two vectors and add result to dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec3_muladd(vec3 a, vec3 b, vec3 dest) { + dest[0] += a[0] * b[0]; + dest[1] += a[1] * b[1]; + dest[2] += a[2] * b[2]; +} + +/*! + * @brief mul vector with scalar and add result to sum + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector + * @param[in] s scalar + * @param[out] dest dest += (a * b) + */ +f_inline +void +glm_vec3_muladds(vec3 a, float s, vec3 dest) { + dest[0] += a[0] * s; + dest[1] += a[1] * s; + dest[2] += a[2] * s; +} + +/*! + * @brief add max of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += max(a, b) + */ +f_inline +void +glm_vec3_maxadd(vec3 a, vec3 b, vec3 dest) { + dest[0] += glm_max(a[0], b[0]); + dest[1] += glm_max(a[1], b[1]); + dest[2] += glm_max(a[2], b[2]); +} + +/*! + * @brief add min of two vector to result/dest + * + * it applies += operator so dest must be initialized + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest dest += min(a, b) + */ +f_inline +void +glm_vec3_minadd(vec3 a, vec3 b, vec3 dest) { + dest[0] += glm_min(a[0], b[0]); + dest[1] += glm_min(a[1], b[1]); + dest[2] += glm_min(a[2], b[2]); +} + +/*! + * @brief negate vector components and store result in dest + * + * @param[in] v vector + * @param[out] dest result vector + */ +f_inline +void +glm_vec3_negate_to(vec3 v, vec3 dest) { + dest[0] = -v[0]; + dest[1] = -v[1]; + dest[2] = -v[2]; +} + +/*! + * @brief negate vector components + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec3_negate(vec3 v) { + glm_vec3_negate_to(v, v); +} + +/*! + * @brief normalize vec3 and store result in same vec + * + * @param[in, out] v vector + */ +f_inline +void +glm_vec3_normalize(vec3 v) { + float norm; + + norm = glm_vec3_norm(v); + + if (norm == 0.0f) { + v[0] = v[1] = v[2] = 0.0f; + return; + } + + glm_vec3_scale(v, 1.0f / norm, v); +} + +/*! + * @brief normalize vec3 to dest + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_vec3_normalize_to(vec3 v, vec3 dest) { + float norm; + + norm = glm_vec3_norm(v); + + if (norm == 0.0f) { + glm_vec3_zero(dest); + return; + } + + glm_vec3_scale(v, 1.0f / norm, dest); +} + +/*! + * @brief cross product of two vector (RH) + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_cross(vec3 a, vec3 b, vec3 dest) { + vec3 c; + /* (u2.v3 - u3.v2, u3.v1 - u1.v3, u1.v2 - u2.v1) */ + c[0] = a[1] * b[2] - a[2] * b[1]; + c[1] = a[2] * b[0] - a[0] * b[2]; + c[2] = a[0] * b[1] - a[1] * b[0]; + glm_vec3_copy(c, dest); +} + +/*! + * @brief cross product of two vector (RH) and normalize the result + * + * @param[in] a vector 1 + * @param[in] b vector 2 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_crossn(vec3 a, vec3 b, vec3 dest) { + glm_vec3_cross(a, b, dest); + glm_vec3_normalize(dest); +} + +/*! + * @brief angle betwen two vector + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return angle as radians + */ +f_inline +float +glm_vec3_angle(vec3 a, vec3 b) { + float norm, dot; + + /* maybe compiler generate approximation instruction (rcp) */ + norm = 1.0f / (glm_vec3_norm(a) * glm_vec3_norm(b)); + dot = glm_vec3_dot(a, b) * norm; + + if (dot > 1.0f) + return 0.0f; + else if (dot < -1.0f) + return GLM_PIf; + + return acosf(dot); +} + +/*! + * @brief rotate vec3 around axis by angle using Rodrigues' rotation formula + * + * @param[in, out] v vector + * @param[in] axis axis vector (must be unit vector) + * @param[in] angle angle by radians + */ +f_inline +void +glm_vec3_rotate(vec3 v, float angle, vec3 axis) { + vec3 v1, v2, k; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + glm_vec3_normalize_to(axis, k); + + /* Right Hand, Rodrigues' rotation formula: + v = v*cos(t) + (kxv)sin(t) + k*(k.v)(1 - cos(t)) + */ + glm_vec3_scale(v, c, v1); + + glm_vec3_cross(k, v, v2); + glm_vec3_scale(v2, s, v2); + + glm_vec3_add(v1, v2, v1); + + glm_vec3_scale(k, glm_vec3_dot(k, v) * (1.0f - c), v2); + glm_vec3_add(v1, v2, v); +} + +/*! + * @brief apply rotation matrix to vector + * + * matrix format should be (no perspective): + * a b c x + * e f g y + * i j k z + * 0 0 0 w + * + * @param[in] m affine matrix or rot matrix + * @param[in] v vector + * @param[out] dest rotated vector + */ +f_inline +void +glm_vec3_rotate_m4(mat4 m, vec3 v, vec3 dest) { + vec4 x, y, z, res; + + glm_vec4_normalize_to(m[0], x); + glm_vec4_normalize_to(m[1], y); + glm_vec4_normalize_to(m[2], z); + + glm_vec4_scale(x, v[0], res); + glm_vec4_muladds(y, v[1], res); + glm_vec4_muladds(z, v[2], res); + + glm_vec3(res, dest); +} + +/*! + * @brief apply rotation matrix to vector + * + * @param[in] m affine matrix or rot matrix + * @param[in] v vector + * @param[out] dest rotated vector + */ +f_inline +void +glm_vec3_rotate_m3(mat3 m, vec3 v, vec3 dest) { + vec4 res, x, y, z; + + glm_vec4(m[0], 0.0f, x); + glm_vec4(m[1], 0.0f, y); + glm_vec4(m[2], 0.0f, z); + + glm_vec4_normalize(x); + glm_vec4_normalize(y); + glm_vec4_normalize(z); + + glm_vec4_scale(x, v[0], res); + glm_vec4_muladds(y, v[1], res); + glm_vec4_muladds(z, v[2], res); + + glm_vec3(res, dest); +} + +/*! + * @brief project a vector onto b vector + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest projected vector + */ +f_inline +void +glm_vec3_proj(vec3 a, vec3 b, vec3 dest) { + glm_vec3_scale(b, + glm_vec3_dot(a, b) / glm_vec3_norm2(b), + dest); +} + +/** + * @brief find center point of two vector + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest center point + */ +f_inline +void +glm_vec3_center(vec3 a, vec3 b, vec3 dest) { + glm_vec3_add(a, b, dest); + glm_vec3_scale(dest, 0.5f, dest); +} + +/** + * @brief squared distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns squared distance (distance * distance) + */ +f_inline +float +glm_vec3_distance2(vec3 a, vec3 b) { + return glm_pow2(a[0] - b[0]) + + glm_pow2(a[1] - b[1]) + + glm_pow2(a[2] - b[2]); +} + +/** + * @brief distance between two vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @return returns distance + */ +f_inline +float +glm_vec3_distance(vec3 a, vec3 b) { + return sqrtf(glm_vec3_distance2(a, b)); +} + +/*! + * @brief max values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_maxv(vec3 a, vec3 b, vec3 dest) { + dest[0] = glm_max(a[0], b[0]); + dest[1] = glm_max(a[1], b[1]); + dest[2] = glm_max(a[2], b[2]); +} + +/*! + * @brief min values of vectors + * + * @param[in] a vector1 + * @param[in] b vector2 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_minv(vec3 a, vec3 b, vec3 dest) { + dest[0] = glm_min(a[0], b[0]); + dest[1] = glm_min(a[1], b[1]); + dest[2] = glm_min(a[2], b[2]); +} + +/*! + * @brief possible orthogonal/perpendicular vector + * + * @param[in] v vector + * @param[out] dest orthogonal/perpendicular vector + */ +f_inline +void +glm_vec3_ortho(vec3 v, vec3 dest) { + float ignore; + float f = modff(fabsf(v[0]) + 0.5f, &ignore); + vec3 result = {-v[1], v[0] - f * v[2], f * v[1]}; + glm_vec3_copy(result, dest); +} + +/*! + * @brief clamp vector's individual members between min and max values + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +void +glm_vec3_clamp(vec3 v, float minVal, float maxVal) { + v[0] = glm_clamp(v[0], minVal, maxVal); + v[1] = glm_clamp(v[1], minVal, maxVal); + v[2] = glm_clamp(v[2], minVal, maxVal); +} + +/*! + * @brief linear interpolation between two vectors + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec3_lerp(vec3 from, vec3 to, float t, vec3 dest) { + vec3 s, v; + + /* from + s * (to - from) */ + glm_vec3_broadcast(t, s); + glm_vec3_sub(to, from, v); + glm_vec3_mul(s, v, v); + glm_vec3_add(from, v, dest); +} + +/*! + * @brief linear interpolation between two vectors (clamped) + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_lerpc(vec3 from, vec3 to, float t, vec3 dest) { + glm_vec3_lerp(from, to, glm_clamp_zo(t), dest); +} + +/*! + * @brief linear interpolation between two vectors + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec3_mix(vec3 from, vec3 to, float t, vec3 dest) { + glm_vec3_lerp(from, to, t, dest); +} + +/*! + * @brief linear interpolation between two vectors (clamped) + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_mixc(vec3 from, vec3 to, float t, vec3 dest) { + glm_vec3_lerpc(from, to, t, dest); +} + +/*! + * @brief threshold function (unidimensional) + * + * @param[in] edge threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec3_step_uni(float edge, vec3 x, vec3 dest) { + dest[0] = glm_step(edge, x[0]); + dest[1] = glm_step(edge, x[1]); + dest[2] = glm_step(edge, x[2]); +} + +/*! + * @brief threshold function + * + * @param[in] edge threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec3_step(vec3 edge, vec3 x, vec3 dest) { + dest[0] = glm_step(edge[0], x[0]); + dest[1] = glm_step(edge[1], x[1]); + dest[2] = glm_step(edge[2], x[2]); +} + +/*! + * @brief threshold function with a smooth transition (unidimensional) + * + * @param[in] edge0 low threshold + * @param[in] edge1 high threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec3_smoothstep_uni(float edge0, float edge1, vec3 x, vec3 dest) { + dest[0] = glm_smoothstep(edge0, edge1, x[0]); + dest[1] = glm_smoothstep(edge0, edge1, x[1]); + dest[2] = glm_smoothstep(edge0, edge1, x[2]); +} + +/*! + * @brief threshold function with a smooth transition + * + * @param[in] edge0 low threshold + * @param[in] edge1 high threshold + * @param[in] x value to test against threshold + * @param[out] dest destination + */ +f_inline +void +glm_vec3_smoothstep(vec3 edge0, vec3 edge1, vec3 x, vec3 dest) { + dest[0] = glm_smoothstep(edge0[0], edge1[0], x[0]); + dest[1] = glm_smoothstep(edge0[1], edge1[1], x[1]); + dest[2] = glm_smoothstep(edge0[2], edge1[2], x[2]); +} + +/*! + * @brief smooth Hermite interpolation between two vectors + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) + * @param[out] dest destination + */ +f_inline +void +glm_vec3_smoothinterp(vec3 from, vec3 to, float t, vec3 dest) { + vec3 s, v; + + /* from + s * (to - from) */ + glm_vec3_broadcast(glm_smooth(t), s); + glm_vec3_sub(to, from, v); + glm_vec3_mul(s, v, v); + glm_vec3_add(from, v, dest); +} + +/*! + * @brief smooth Hermite interpolation between two vectors (clamped) + * + * formula: from + s * (to - from) + * + * @param[in] from from value + * @param[in] to to value + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest destination + */ +f_inline +void +glm_vec3_smoothinterpc(vec3 from, vec3 to, float t, vec3 dest) { + glm_vec3_smoothinterp(from, to, glm_clamp_zo(t), dest); +} + +/*! + * @brief swizzle vector components + * + * you can use existin masks e.g. GLM_XXX, GLM_ZYX + * + * @param[in] v source + * @param[in] mask mask + * @param[out] dest destination + */ +f_inline +void +glm_vec3_swizzle(vec3 v, int mask, vec3 dest) { + vec3 t; + + t[0] = v[(mask & (3 << 0))]; + t[1] = v[(mask & (3 << 2)) >> 2]; + t[2] = v[(mask & (3 << 4)) >> 4]; + + glm_vec3_copy(t, dest); +} + +/*! + * @brief vec3 cross product + * + * this is just convenient wrapper + * + * @param[in] a source 1 + * @param[in] b source 2 + * @param[out] d destination + */ +f_inline +void +glm_cross(vec3 a, vec3 b, vec3 d) { + glm_vec3_cross(a, b, d); +} + +/*! + * @brief vec3 dot product + * + * this is just convenient wrapper + * + * @param[in] a vector1 + * @param[in] b vector2 + * + * @return dot product + */ +f_inline +float +glm_dot(vec3 a, vec3 b) { + return glm_vec3_dot(a, b); +} + +/*! + * @brief normalize vec3 and store result in same vec + * + * this is just convenient wrapper + * + * @param[in, out] v vector + */ +f_inline +void +glm_normalize(vec3 v) { + glm_vec3_normalize(v); +} + +/*! + * @brief normalize vec3 to dest + * + * this is just convenient wrapper + * + * @param[in] v source + * @param[out] dest destination + */ +f_inline +void +glm_normalize_to(vec3 v, vec3 dest) { + glm_vec3_normalize_to(v, dest); +} + + + + + + + + +#define GLM_IVEC2_ONE_INIT {1, 1} +#define GLM_IVEC2_ZERO_INIT {0, 0} + +#define GLM_IVEC2_ONE ((ivec2)GLM_IVEC2_ONE_INIT) +#define GLM_IVEC2_ZERO ((ivec2)GLM_IVEC2_ZERO_INIT) + +/*! + * @brief init ivec2 using vec3 or vec4 + * + * @param[in] v vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2(int * __restrict v, ivec2 dest) { + dest[0] = v[0]; + dest[1] = v[1]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_copy(ivec2 a, ivec2 dest) { + dest[0] = a[0]; + dest[1] = a[1]; +} + +/*! + * @brief set all members of [v] to zero + * + * @param[out] v vector + */ +f_inline +void +glm_ivec2_zero(ivec2 v) { + v[0] = v[1] = 0; +} + +/*! + * @brief set all members of [v] to one + * + * @param[out] v vector + */ +f_inline +void +glm_ivec2_one(ivec2 v) { + v[0] = v[1] = 1; +} + +/*! + * @brief add vector [a] to vector [b] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_add(ivec2 a, ivec2 b, ivec2 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; +} + +/*! + * @brief add scalar s to vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_adds(ivec2 v, int s, ivec2 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; +} + +/*! + * @brief subtract vector [b] from vector [a] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_sub(ivec2 a, ivec2 b, ivec2 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; +} + +/*! + * @brief subtract scalar s from vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_subs(ivec2 v, int s, ivec2 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; +} + +/*! + * @brief multiply vector [a] with vector [b] and store result in [dest] + * + * @param[in] a frist vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_mul(ivec2 a, ivec2 b, ivec2 dest) { + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; +} + +/*! + * @brief multiply vector [a] with scalar s and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_scale(ivec2 v, int s, ivec2 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; +} + +/*! + * @brief squared distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns squared distance (distance * distance) + */ +f_inline +int +glm_ivec2_distance2(ivec2 a, ivec2 b) { + int xd, yd; + xd = a[0] - b[0]; + yd = a[1] - b[1]; + return xd * xd + yd * yd; +} + +/*! + * @brief distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns distance + */ +f_inline +float +glm_ivec2_distance(ivec2 a, ivec2 b) { + return sqrtf((float)glm_ivec2_distance2(a, b)); +} + +/*! + * @brief set each member of dest to greater of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_maxv(ivec2 a, ivec2 b, ivec2 dest) { + dest[0] = a[0] > b[0] ? a[0] : b[0]; + dest[1] = a[1] > b[1] ? a[1] : b[1]; +} + +/*! + * @brief set each member of dest to lesser of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_minv(ivec2 a, ivec2 b, ivec2 dest) { + dest[0] = a[0] < b[0] ? a[0] : b[0]; + dest[1] = a[1] < b[1] ? a[1] : b[1]; +} + +/*! + * @brief clamp each member of [v] between minVal and maxVal (inclusive) + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +void +glm_ivec2_clamp(ivec2 v, int minVal, int maxVal) { + if (v[0] < minVal) + v[0] = minVal; + else if(v[0] > maxVal) + v[0] = maxVal; + + if (v[1] < minVal) + v[1] = minVal; + else if(v[1] > maxVal) + v[1] = maxVal; +} + +/*! + * @brief absolute value of v + * + * @param[in] v vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec2_abs(ivec2 v, ivec2 dest) { + dest[0] = abs(v[0]); + dest[1] = abs(v[1]); +} + + + + + + + + + +#define GLM_IVEC3_ONE_INIT {1, 1, 1} +#define GLM_IVEC3_ZERO_INIT {0, 0, 0} + +#define GLM_IVEC3_ONE ((ivec3)GLM_IVEC3_ONE_INIT) +#define GLM_IVEC3_ZERO ((ivec3)GLM_IVEC3_ZERO_INIT) + +/*! + * @brief init ivec3 using ivec4 + * + * @param[in] v4 vector4 + * @param[out] dest destination + */ +f_inline +void +glm_ivec3(ivec4 v4, ivec3 dest) { + dest[0] = v4[0]; + dest[1] = v4[1]; + dest[2] = v4[2]; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_copy(ivec3 a, ivec3 dest) { + dest[0] = a[0]; + dest[1] = a[1]; + dest[2] = a[2]; +} + +/*! + * @brief set all members of [v] to zero + * + * @param[out] v vector + */ +f_inline +void +glm_ivec3_zero(ivec3 v) { + v[0] = v[1] = v[2] = 0; +} + +/*! + * @brief set all members of [v] to one + * + * @param[out] v vector + */ +f_inline +void +glm_ivec3_one(ivec3 v) { + v[0] = v[1] = v[2] = 1; +} + +/*! + * @brief add vector [a] to vector [b] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_add(ivec3 a, ivec3 b, ivec3 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; +} + +/*! + * @brief add scalar s to vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_adds(ivec3 v, int s, ivec3 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; + dest[2] = v[2] + s; +} + +/*! + * @brief subtract vector [b] from vector [a] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_sub(ivec3 a, ivec3 b, ivec3 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; +} + +/*! + * @brief subtract scalar s from vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_subs(ivec3 v, int s, ivec3 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; + dest[2] = v[2] - s; +} + +/*! + * @brief multiply vector [a] with vector [b] and store result in [dest] + * + * @param[in] a frist vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_mul(ivec3 a, ivec3 b, ivec3 dest) { + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; +} + +/*! + * @brief multiply vector [a] with scalar s and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_scale(ivec3 v, int s, ivec3 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; + dest[2] = v[2] * s; +} + +/*! + * @brief squared distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns squared distance (distance * distance) + */ +f_inline +int +glm_ivec3_distance2(ivec3 a, ivec3 b) { + int xd, yd, zd; + xd = a[0] - b[0]; + yd = a[1] - b[1]; + zd = a[2] - b[2]; + return xd * xd + yd * yd + zd * zd; +} + +/*! + * @brief distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns distance + */ +f_inline +float +glm_ivec3_distance(ivec3 a, ivec3 b) { + return sqrtf((float)glm_ivec3_distance2(a, b)); +} + +/*! + * @brief set each member of dest to greater of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_maxv(ivec3 a, ivec3 b, ivec3 dest) { + dest[0] = a[0] > b[0] ? a[0] : b[0]; + dest[1] = a[1] > b[1] ? a[1] : b[1]; + dest[2] = a[2] > b[2] ? a[2] : b[2]; +} + +/*! + * @brief set each member of dest to lesser of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_minv(ivec3 a, ivec3 b, ivec3 dest) { + dest[0] = a[0] < b[0] ? a[0] : b[0]; + dest[1] = a[1] < b[1] ? a[1] : b[1]; + dest[2] = a[2] < b[2] ? a[2] : b[2]; +} + +/*! + * @brief clamp each member of [v] between minVal and maxVal (inclusive) + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +void +glm_ivec3_clamp(ivec3 v, int minVal, int maxVal) { + if (v[0] < minVal) + v[0] = minVal; + else if(v[0] > maxVal) + v[0] = maxVal; + + if (v[1] < minVal) + v[1] = minVal; + else if(v[1] > maxVal) + v[1] = maxVal; + + if (v[2] < minVal) + v[2] = minVal; + else if(v[2] > maxVal) + v[2] = maxVal; +} + +/*! + * @brief absolute value of v + * + * @param[in] v vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec3_abs(ivec3 v, ivec3 dest) { + dest[0] = abs(v[0]); + dest[1] = abs(v[1]); + dest[2] = abs(v[2]); +} + + + + + + + + + +#define GLM_IVEC4_ONE_INIT {1, 1, 1, 1} +#define GLM_IVEC4_ZERO_INIT {0, 0, 0, 0} + +#define GLM_IVEC4_ONE ((ivec4)GLM_IVEC4_ONE_INIT) +#define GLM_IVEC4_ZERO ((ivec4)GLM_IVEC4_ZERO_INIT) + +/*! + * @brief init ivec4 using ivec3 + * + * @param[in] v3 vector3 + * @param[in] last last item + * @param[out] dest destination + */ +f_inline +void +glm_ivec4(ivec3 v3, int last, ivec4 dest) { + dest[0] = v3[0]; + dest[1] = v3[1]; + dest[2] = v3[2]; + dest[3] = last; +} + +/*! + * @brief copy all members of [a] to [dest] + * + * @param[in] a source vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_copy(ivec4 a, ivec4 dest) { + dest[0] = a[0]; + dest[1] = a[1]; + dest[2] = a[2]; + dest[3] = a[3]; +} + +/*! + * @brief set all members of [v] to zero + * + * @param[out] v vector + */ +f_inline +void +glm_ivec4_zero(ivec4 v) { + v[0] = v[1] = v[2] = v[3] = 0; +} + +/*! + * @brief set all members of [v] to one + * + * @param[out] v vector + */ +f_inline +void +glm_ivec4_one(ivec4 v) { + v[0] = v[1] = v[2] = v[3] = 1; +} + +/*! + * @brief add vector [a] to vector [b] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_add(ivec4 a, ivec4 b, ivec4 dest) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; + dest[3] = a[3] + b[3]; +} + +/*! + * @brief add scalar s to vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_adds(ivec4 v, int s, ivec4 dest) { + dest[0] = v[0] + s; + dest[1] = v[1] + s; + dest[2] = v[2] + s; + dest[3] = v[3] + s; +} + +/*! + * @brief subtract vector [b] from vector [a] and store result in [dest] + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_sub(ivec4 a, ivec4 b, ivec4 dest) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; + dest[3] = a[3] - b[3]; +} + +/*! + * @brief subtract scalar s from vector [v] and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_subs(ivec4 v, int s, ivec4 dest) { + dest[0] = v[0] - s; + dest[1] = v[1] - s; + dest[2] = v[2] - s; + dest[3] = v[3] - s; +} + +/*! + * @brief multiply vector [a] with vector [b] and store result in [dest] + * + * @param[in] a frist vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_mul(ivec4 a, ivec4 b, ivec4 dest) { + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; + dest[3] = a[3] * b[3]; +} + +/*! + * @brief multiply vector [a] with scalar s and store result in [dest] + * + * @param[in] v vector + * @param[in] s scalar + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_scale(ivec4 v, int s, ivec4 dest) { + dest[0] = v[0] * s; + dest[1] = v[1] * s; + dest[2] = v[2] * s; + dest[3] = v[3] * s; +} + +/*! + * @brief squared distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns squared distance (distance * distance) + */ +f_inline +int +glm_ivec4_distance2(ivec4 a, ivec4 b) { + int xd, yd, zd, wd; + xd = a[0] - b[0]; + yd = a[1] - b[1]; + zd = a[2] - b[2]; + wd = a[3] - b[3]; + return xd * xd + yd * yd + zd * zd + wd * wd; +} + +/*! + * @brief distance between two vectors + * + * @param[in] a first vector + * @param[in] b second vector + * @return returns distance + */ +f_inline +float +glm_ivec4_distance(ivec4 a, ivec4 b) { + return sqrtf((float)glm_ivec4_distance2(a, b)); +} + +/*! + * @brief set each member of dest to greater of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_maxv(ivec4 a, ivec4 b, ivec4 dest) { + dest[0] = a[0] > b[0] ? a[0] : b[0]; + dest[1] = a[1] > b[1] ? a[1] : b[1]; + dest[2] = a[2] > b[2] ? a[2] : b[2]; + dest[3] = a[3] > b[3] ? a[3] : b[3]; +} + +/*! + * @brief set each member of dest to lesser of vector a and b + * + * @param[in] a first vector + * @param[in] b second vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_minv(ivec4 a, ivec4 b, ivec4 dest) { + dest[0] = a[0] < b[0] ? a[0] : b[0]; + dest[1] = a[1] < b[1] ? a[1] : b[1]; + dest[2] = a[2] < b[2] ? a[2] : b[2]; + dest[3] = a[3] < b[3] ? a[3] : b[3]; +} + +/*! + * @brief clamp each member of [v] between minVal and maxVal (inclusive) + * + * @param[in, out] v vector + * @param[in] minVal minimum value + * @param[in] maxVal maximum value + */ +f_inline +void +glm_ivec4_clamp(ivec4 v, int minVal, int maxVal) { + if (v[0] < minVal) + v[0] = minVal; + else if(v[0] > maxVal) + v[0] = maxVal; + + if (v[1] < minVal) + v[1] = minVal; + else if(v[1] > maxVal) + v[1] = maxVal; + + if (v[2] < minVal) + v[2] = minVal; + else if(v[2] > maxVal) + v[2] = maxVal; + + if (v[3] < minVal) + v[3] = minVal; + else if(v[3] > maxVal) + v[3] = maxVal; +} + +/*! + * @brief absolute value of v + * + * @param[in] v vector + * @param[out] dest destination + */ +f_inline +void +glm_ivec4_abs(ivec4 v, ivec4 dest) { + dest[0] = abs(v[0]); + dest[1] = abs(v[1]); + dest[2] = abs(v[2]); + dest[3] = abs(v[3]); +} + + + + + + + + + +#ifdef CGLM_SIMD +#if defined( __SSE__ ) || defined( __SSE2__ ) + +f_inline +void +glm_mat4_scale_sse2(mat4 m, float s) { + __m128 x0; + x0 = _mm_set1_ps(s); + + glmm_store(m[0], _mm_mul_ps(glmm_load(m[0]), x0)); + glmm_store(m[1], _mm_mul_ps(glmm_load(m[1]), x0)); + glmm_store(m[2], _mm_mul_ps(glmm_load(m[2]), x0)); + glmm_store(m[3], _mm_mul_ps(glmm_load(m[3]), x0)); +} + +f_inline +void +glm_mat4_transp_sse2(mat4 m, mat4 dest) { + __m128 r0, r1, r2, r3; + + r0 = glmm_load(m[0]); + r1 = glmm_load(m[1]); + r2 = glmm_load(m[2]); + r3 = glmm_load(m[3]); + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + + glmm_store(dest[0], r0); + glmm_store(dest[1], r1); + glmm_store(dest[2], r2); + glmm_store(dest[3], r3); +} + +f_inline +void +glm_mat4_mul_sse2(mat4 m1, mat4 m2, mat4 dest) { + /* D = R * L (Column-Major) */ + + glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3; + + l = glmm_load(m1[0]); + r0 = glmm_load(m2[0]); + r1 = glmm_load(m2[1]); + r2 = glmm_load(m2[2]); + r3 = glmm_load(m2[3]); + + v0 = _mm_mul_ps(glmm_splat_x(r0), l); + v1 = _mm_mul_ps(glmm_splat_x(r1), l); + v2 = _mm_mul_ps(glmm_splat_x(r2), l); + v3 = _mm_mul_ps(glmm_splat_x(r3), l); + + l = glmm_load(m1[1]); + v0 = glmm_fmadd(glmm_splat_y(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_y(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_y(r2), l, v2); + v3 = glmm_fmadd(glmm_splat_y(r3), l, v3); + + l = glmm_load(m1[2]); + v0 = glmm_fmadd(glmm_splat_z(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_z(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_z(r2), l, v2); + v3 = glmm_fmadd(glmm_splat_z(r3), l, v3); + + l = glmm_load(m1[3]); + v0 = glmm_fmadd(glmm_splat_w(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_w(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_w(r2), l, v2); + v3 = glmm_fmadd(glmm_splat_w(r3), l, v3); + + glmm_store(dest[0], v0); + glmm_store(dest[1], v1); + glmm_store(dest[2], v2); + glmm_store(dest[3], v3); +} + +f_inline +void +glm_mat4_mulv_sse2(mat4 m, vec4 v, vec4 dest) { + __m128 x0, x1, m0, m1, m2, m3, v0, v1, v2, v3; + + m0 = glmm_load(m[0]); + m1 = glmm_load(m[1]); + m2 = glmm_load(m[2]); + m3 = glmm_load(m[3]); + + x0 = glmm_load(v); + v0 = glmm_splat_x(x0); + v1 = glmm_splat_y(x0); + v2 = glmm_splat_z(x0); + v3 = glmm_splat_w(x0); + + x1 = _mm_mul_ps(m3, v3); + x1 = glmm_fmadd(m2, v2, x1); + x1 = glmm_fmadd(m1, v1, x1); + x1 = glmm_fmadd(m0, v0, x1); + + glmm_store(dest, x1); +} + +f_inline +float +glm_mat4_det_sse2(mat4 mat) { + __m128 r0, r1, r2, r3, x0, x1, x2; + + /* 127 <- 0, [square] det(A) = det(At) */ + r0 = glmm_load(mat[0]); /* d c b a */ + r1 = glmm_load(mat[1]); /* h g f e */ + r2 = glmm_load(mat[2]); /* l k j i */ + r3 = glmm_load(mat[3]); /* p o n m */ + + /* + t[1] = j * p - n * l; + t[2] = j * o - n * k; + t[3] = i * p - m * l; + t[4] = i * o - m * k; + */ + x0 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 1, 1), glmm_shuff1(r2, 2, 3, 2, 3), + _mm_mul_ps(glmm_shuff1(r2, 0, 0, 1, 1), + glmm_shuff1(r3, 2, 3, 2, 3))); + /* + t[0] = k * p - o * l; + t[0] = k * p - o * l; + t[5] = i * n - m * j; + t[5] = i * n - m * j; + */ + x1 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 2, 2), glmm_shuff1(r2, 1, 1, 3, 3), + _mm_mul_ps(glmm_shuff1(r2, 0, 0, 2, 2), + glmm_shuff1(r3, 1, 1, 3, 3))); + + /* + a * (f * t[0] - g * t[1] + h * t[2]) + - b * (e * t[0] - g * t[3] + h * t[4]) + + c * (e * t[1] - f * t[3] + h * t[5]) + - d * (e * t[2] - f * t[4] + g * t[5]) + */ + x2 = glmm_fnmadd(glmm_shuff1(r1, 1, 1, 2, 2), glmm_shuff1(x0, 3, 2, 2, 0), + _mm_mul_ps(glmm_shuff1(r1, 0, 0, 0, 1), + _mm_shuffle_ps(x1, x0, _MM_SHUFFLE(1, 0, 0, 0)))); + x2 = glmm_fmadd(glmm_shuff1(r1, 2, 3, 3, 3), + _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 2, 3, 1)), + x2); + + x2 = _mm_xor_ps(x2, _mm_set_ps(-0.f, 0.f, -0.f, 0.f)); + + return glmm_hadd(_mm_mul_ps(x2, r0)); +} + +f_inline +void +glm_mat4_inv_fast_sse2(mat4 mat, mat4 dest) { + __m128 r0, r1, r2, r3, + v0, v1, v2, v3, + t0, t1, t2, t3, t4, t5, + x0, x1, x2, x3, x4, x5, x6, x7, x8, x9; + + x8 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); + x9 = glmm_shuff1(x8, 2, 1, 2, 1); + + /* 127 <- 0 */ + r0 = glmm_load(mat[0]); /* d c b a */ + r1 = glmm_load(mat[1]); /* h g f e */ + r2 = glmm_load(mat[2]); /* l k j i */ + r3 = glmm_load(mat[3]); /* p o n m */ + + x0 = _mm_movehl_ps(r3, r2); /* p o l k */ + x3 = _mm_movelh_ps(r2, r3); /* n m j i */ + x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */ + x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */ + x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */ + x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */ + + x6 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(0, 0, 0, 0)); /* e e i i */ + x5 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(1, 1, 1, 1)); /* f f j j */ + x3 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(2, 2, 2, 2)); /* g g k k */ + x0 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(3, 3, 3, 3)); /* h h l l */ + + t0 = _mm_mul_ps(x3, x1); + t1 = _mm_mul_ps(x5, x1); + t2 = _mm_mul_ps(x5, x2); + t3 = _mm_mul_ps(x6, x1); + t4 = _mm_mul_ps(x6, x2); + t5 = _mm_mul_ps(x6, x4); + + /* t1[0] = k * p - o * l; + t1[0] = k * p - o * l; + t2[0] = g * p - o * h; + t3[0] = g * l - k * h; */ + t0 = glmm_fnmadd(x2, x0, t0); + + /* t1[1] = j * p - n * l; + t1[1] = j * p - n * l; + t2[1] = f * p - n * h; + t3[1] = f * l - j * h; */ + t1 = glmm_fnmadd(x4, x0, t1); + + /* t1[2] = j * o - n * k + t1[2] = j * o - n * k; + t2[2] = f * o - n * g; + t3[2] = f * k - j * g; */ + t2 = glmm_fnmadd(x4, x3, t2); + + /* t1[3] = i * p - m * l; + t1[3] = i * p - m * l; + t2[3] = e * p - m * h; + t3[3] = e * l - i * h; */ + t3 = glmm_fnmadd(x7, x0, t3); + + /* t1[4] = i * o - m * k; + t1[4] = i * o - m * k; + t2[4] = e * o - m * g; + t3[4] = e * k - i * g; */ + t4 = glmm_fnmadd(x7, x3, t4); + + /* t1[5] = i * n - m * j; + t1[5] = i * n - m * j; + t2[5] = e * n - m * f; + t3[5] = e * j - i * f; */ + t5 = glmm_fnmadd(x7, x5, t5); + + x4 = _mm_movelh_ps(r0, r1); /* f e b a */ + x5 = _mm_movehl_ps(r1, r0); /* h g d c */ + + x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */ + x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */ + x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */ + x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */ + + v2 = _mm_mul_ps(x0, t1); + v1 = _mm_mul_ps(x0, t0); + v3 = _mm_mul_ps(x0, t2); + v0 = _mm_mul_ps(x1, t0); + + v2 = glmm_fnmadd(x1, t3, v2); + v3 = glmm_fnmadd(x1, t4, v3); + v0 = glmm_fnmadd(x2, t1, v0); + v1 = glmm_fnmadd(x2, t3, v1); + + v3 = glmm_fmadd(x2, t5, v3); + v0 = glmm_fmadd(x3, t2, v0); + v2 = glmm_fmadd(x3, t5, v2); + v1 = glmm_fmadd(x3, t4, v1); + + /* + dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2]; + dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]); + dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2]; + dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */ + v0 = _mm_xor_ps(v0, x8); + + /* + dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5]; + dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]); + dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5]; + dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/ + v2 = _mm_xor_ps(v2, x8); + + /* + dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]); + dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4]; + dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]); + dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */ + v1 = _mm_xor_ps(v1, x9); + + /* + dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]); + dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5]; + dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]); + dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */ + v3 = _mm_xor_ps(v3, x9); + + /* determinant */ + x0 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(0, 0, 0, 0)); + x1 = _mm_shuffle_ps(v2, v3, _MM_SHUFFLE(0, 0, 0, 0)); + x0 = _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 0, 2, 0)); + + x0 = _mm_rcp_ps(glmm_vhadd(_mm_mul_ps(x0, r0))); + + glmm_store(dest[0], _mm_mul_ps(v0, x0)); + glmm_store(dest[1], _mm_mul_ps(v1, x0)); + glmm_store(dest[2], _mm_mul_ps(v2, x0)); + glmm_store(dest[3], _mm_mul_ps(v3, x0)); +} + +f_inline +void +glm_mat4_inv_sse2(mat4 mat, mat4 dest) { + __m128 r0, r1, r2, r3, + v0, v1, v2, v3, + t0, t1, t2, t3, t4, t5, + x0, x1, x2, x3, x4, x5, x6, x7, x8, x9; + + x8 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); + x9 = glmm_shuff1(x8, 2, 1, 2, 1); + + /* 127 <- 0 */ + r0 = glmm_load(mat[0]); /* d c b a */ + r1 = glmm_load(mat[1]); /* h g f e */ + r2 = glmm_load(mat[2]); /* l k j i */ + r3 = glmm_load(mat[3]); /* p o n m */ + + x0 = _mm_movehl_ps(r3, r2); /* p o l k */ + x3 = _mm_movelh_ps(r2, r3); /* n m j i */ + x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */ + x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */ + x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */ + x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */ + + x6 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(0, 0, 0, 0)); /* e e i i */ + x5 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(1, 1, 1, 1)); /* f f j j */ + x3 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(2, 2, 2, 2)); /* g g k k */ + x0 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(3, 3, 3, 3)); /* h h l l */ + + t0 = _mm_mul_ps(x3, x1); + t1 = _mm_mul_ps(x5, x1); + t2 = _mm_mul_ps(x5, x2); + t3 = _mm_mul_ps(x6, x1); + t4 = _mm_mul_ps(x6, x2); + t5 = _mm_mul_ps(x6, x4); + + /* t1[0] = k * p - o * l; + t1[0] = k * p - o * l; + t2[0] = g * p - o * h; + t3[0] = g * l - k * h; */ + t0 = glmm_fnmadd(x2, x0, t0); + + /* t1[1] = j * p - n * l; + t1[1] = j * p - n * l; + t2[1] = f * p - n * h; + t3[1] = f * l - j * h; */ + t1 = glmm_fnmadd(x4, x0, t1); + + /* t1[2] = j * o - n * k + t1[2] = j * o - n * k; + t2[2] = f * o - n * g; + t3[2] = f * k - j * g; */ + t2 = glmm_fnmadd(x4, x3, t2); + + /* t1[3] = i * p - m * l; + t1[3] = i * p - m * l; + t2[3] = e * p - m * h; + t3[3] = e * l - i * h; */ + t3 = glmm_fnmadd(x7, x0, t3); + + /* t1[4] = i * o - m * k; + t1[4] = i * o - m * k; + t2[4] = e * o - m * g; + t3[4] = e * k - i * g; */ + t4 = glmm_fnmadd(x7, x3, t4); + + /* t1[5] = i * n - m * j; + t1[5] = i * n - m * j; + t2[5] = e * n - m * f; + t3[5] = e * j - i * f; */ + t5 = glmm_fnmadd(x7, x5, t5); + + x4 = _mm_movelh_ps(r0, r1); /* f e b a */ + x5 = _mm_movehl_ps(r1, r0); /* h g d c */ + + x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */ + x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */ + x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */ + x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */ + + v2 = _mm_mul_ps(x0, t1); + v1 = _mm_mul_ps(x0, t0); + v3 = _mm_mul_ps(x0, t2); + v0 = _mm_mul_ps(x1, t0); + + v2 = glmm_fnmadd(x1, t3, v2); + v3 = glmm_fnmadd(x1, t4, v3); + v0 = glmm_fnmadd(x2, t1, v0); + v1 = glmm_fnmadd(x2, t3, v1); + + v3 = glmm_fmadd(x2, t5, v3); + v0 = glmm_fmadd(x3, t2, v0); + v2 = glmm_fmadd(x3, t5, v2); + v1 = glmm_fmadd(x3, t4, v1); + + /* + dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2]; + dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]); + dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2]; + dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */ + v0 = _mm_xor_ps(v0, x8); + + /* + dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5]; + dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]); + dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5]; + dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/ + v2 = _mm_xor_ps(v2, x8); + + /* + dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]); + dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4]; + dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]); + dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */ + v1 = _mm_xor_ps(v1, x9); + + /* + dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]); + dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5]; + dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]); + dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */ + v3 = _mm_xor_ps(v3, x9); + + /* determinant */ + x0 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(0, 0, 0, 0)); + x1 = _mm_shuffle_ps(v2, v3, _MM_SHUFFLE(0, 0, 0, 0)); + x0 = _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 0, 2, 0)); + + x0 = _mm_div_ps(_mm_set1_ps(1.0f), glmm_vhadd(_mm_mul_ps(x0, r0))); + + glmm_store(dest[0], _mm_mul_ps(v0, x0)); + glmm_store(dest[1], _mm_mul_ps(v1, x0)); + glmm_store(dest[2], _mm_mul_ps(v2, x0)); + glmm_store(dest[3], _mm_mul_ps(v3, x0)); +} + +#endif +#endif + + +#define GLM_MAT4_IDENTITY_INIT {{1.0f, 0.0f, 0.0f, 0.0f}, \ + {0.0f, 1.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 1.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f, 1.0f}} + +#define GLM_MAT4_ZERO_INIT {{0.0f, 0.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f, 0.0f}} + +/* for C only */ +#define GLM_MAT4_IDENTITY ((mat4)GLM_MAT4_IDENTITY_INIT) +#define GLM_MAT4_ZERO ((mat4)GLM_MAT4_ZERO_INIT) + +/*! + * @brief copy all members of [mat] to [dest] + * + * matrix may not be aligned, u stands for unaligned, this may be useful when + * copying a matrix from external source e.g. asset importer... + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat4_ucopy(mat4 mat, mat4 dest) { + dest[0][0] = mat[0][0]; dest[1][0] = mat[1][0]; + dest[0][1] = mat[0][1]; dest[1][1] = mat[1][1]; + dest[0][2] = mat[0][2]; dest[1][2] = mat[1][2]; + dest[0][3] = mat[0][3]; dest[1][3] = mat[1][3]; + + dest[2][0] = mat[2][0]; dest[3][0] = mat[3][0]; + dest[2][1] = mat[2][1]; dest[3][1] = mat[3][1]; + dest[2][2] = mat[2][2]; dest[3][2] = mat[3][2]; + dest[2][3] = mat[2][3]; dest[3][3] = mat[3][3]; +} + +/*! + * @brief copy all members of [mat] to [dest] + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat4_copy(mat4 mat, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(dest[0], glmm_load(mat[0])); + glmm_store(dest[1], glmm_load(mat[1])); + glmm_store(dest[2], glmm_load(mat[2])); + glmm_store(dest[3], glmm_load(mat[3])); +#elif defined(CGLM_NEON_FP) + vst1q_f32(dest[0], vld1q_f32(mat[0])); + vst1q_f32(dest[1], vld1q_f32(mat[1])); + vst1q_f32(dest[2], vld1q_f32(mat[2])); + vst1q_f32(dest[3], vld1q_f32(mat[3])); +#else + glm_mat4_ucopy(mat, dest); +#endif +} + +/*! + * @brief make given matrix identity. It is identical with below, + * but it is more easy to do that with this func especially for members + * e.g. glm_mat4_identity(aStruct->aMatrix); + * + * @code + * glm_mat4_copy(GLM_MAT4_IDENTITY, mat); // C only + * + * // or + * mat4 mat = GLM_MAT4_IDENTITY_INIT; + * @endcode + * + * @param[in, out] mat destination + */ +f_inline +void +glm_mat4_identity(mat4 mat) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + glm_mat4_copy(t, mat); +} + +/*! + * @brief make given matrix array's each element identity matrix + * + * @param[in, out] mat matrix array (must be aligned (16/32) + * if alignment is not disabled) + * + * @param[in] count count of matrices + */ +f_inline +void +glm_mat4_identity_array(mat4 * __restrict mat, uint count) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + uint i; + + for (i = 0; i < count; i++) { + glm_mat4_copy(t, mat[i]); + } +} + +/*! + * @brief make given matrix zero. + * + * @param[in, out] mat matrix + */ +f_inline +void +glm_mat4_zero(mat4 mat) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_128 x0; + x0 = _mm_setzero_ps(); + glmm_store(mat[0], x0); + glmm_store(mat[1], x0); + glmm_store(mat[2], x0); + glmm_store(mat[3], x0); +#elif defined(CGLM_NEON_FP) + glmm_128 x0; + x0 = vdupq_n_f32(0.0f); + vst1q_f32(mat[0], x0); + vst1q_f32(mat[1], x0); + vst1q_f32(mat[2], x0); + vst1q_f32(mat[3], x0); +#else + f_align(16) mat4 t = GLM_MAT4_ZERO_INIT; + glm_mat4_copy(t, mat); +#endif +} + +/*! + * @brief copy upper-left of mat4 to mat3 + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat4_pick3(mat4 mat, mat3 dest) { + dest[0][0] = mat[0][0]; + dest[0][1] = mat[0][1]; + dest[0][2] = mat[0][2]; + + dest[1][0] = mat[1][0]; + dest[1][1] = mat[1][1]; + dest[1][2] = mat[1][2]; + + dest[2][0] = mat[2][0]; + dest[2][1] = mat[2][1]; + dest[2][2] = mat[2][2]; +} + +/*! + * @brief copy upper-left of mat4 to mat3 (transposed) + * + * the postfix t stands for transpose + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat4_pick3t(mat4 mat, mat3 dest) { + dest[0][0] = mat[0][0]; + dest[0][1] = mat[1][0]; + dest[0][2] = mat[2][0]; + + dest[1][0] = mat[0][1]; + dest[1][1] = mat[1][1]; + dest[1][2] = mat[2][1]; + + dest[2][0] = mat[0][2]; + dest[2][1] = mat[1][2]; + dest[2][2] = mat[2][2]; +} + +/*! + * @brief copy mat3 to mat4's upper-left + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat4_ins3(mat3 mat, mat4 dest) { + dest[0][0] = mat[0][0]; + dest[0][1] = mat[0][1]; + dest[0][2] = mat[0][2]; + + dest[1][0] = mat[1][0]; + dest[1][1] = mat[1][1]; + dest[1][2] = mat[1][2]; + + dest[2][0] = mat[2][0]; + dest[2][1] = mat[2][1]; + dest[2][2] = mat[2][2]; +} + +/*! + * @brief multiply m1 and m2 to dest + * + * m1, m2 and dest matrices can be same matrix, it is possible to write this: + * + * @code + * mat4 m = GLM_MAT4_IDENTITY_INIT; + * glm_mat4_mul(m, m, m); + * @endcode + * + * @param[in] m1 left matrix + * @param[in] m2 right matrix + * @param[out] dest destination matrix + */ +f_inline +void +glm_mat4_mul(mat4 m1, mat4 m2, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_mul_sse2(m1, m2, dest); +#elif defined(CGLM_NEON_FP) + glm_mat4_mul_neon(m1, m2, dest); +#else + float a00 = m1[0][0], a01 = m1[0][1], a02 = m1[0][2], a03 = m1[0][3], + a10 = m1[1][0], a11 = m1[1][1], a12 = m1[1][2], a13 = m1[1][3], + a20 = m1[2][0], a21 = m1[2][1], a22 = m1[2][2], a23 = m1[2][3], + a30 = m1[3][0], a31 = m1[3][1], a32 = m1[3][2], a33 = m1[3][3], + + b00 = m2[0][0], b01 = m2[0][1], b02 = m2[0][2], b03 = m2[0][3], + b10 = m2[1][0], b11 = m2[1][1], b12 = m2[1][2], b13 = m2[1][3], + b20 = m2[2][0], b21 = m2[2][1], b22 = m2[2][2], b23 = m2[2][3], + b30 = m2[3][0], b31 = m2[3][1], b32 = m2[3][2], b33 = m2[3][3]; + + dest[0][0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; + dest[0][1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; + dest[0][2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; + dest[0][3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; + dest[1][0] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; + dest[1][1] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; + dest[1][2] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; + dest[1][3] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; + dest[2][0] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; + dest[2][1] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; + dest[2][2] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; + dest[2][3] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; + dest[3][0] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; + dest[3][1] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; + dest[3][2] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; + dest[3][3] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; +#endif +} + +/*! + * @brief mupliply N mat4 matrices and store result in dest + * + * this function lets you multiply multiple (more than two or more...) matrices + *

multiplication will be done in loop, this may reduce instructions + * size but if len is too small then compiler may unroll whole loop, + * usage: + * @code + * mat m1, m2, m3, m4, res; + * + * glm_mat4_mulN((mat4 *[]){&m1, &m2, &m3, &m4}, 4, res); + * @endcode + * + * @warning matrices parameter is pointer array not mat4 array! + * + * @param[in] matrices mat4 * array + * @param[in] len matrices count + * @param[out] dest result + */ +f_inline +void +glm_mat4_mulN(mat4 * __restrict matrices[], uint len, mat4 dest) { + uint i; + + // sys_assert(len > 1); + + glm_mat4_mul(*matrices[0], *matrices[1], dest); + + for (i = 2; i < len; i++) + glm_mat4_mul(dest, *matrices[i], dest); +} + +/*! + * @brief multiply mat4 with vec4 (column vector) and store in dest vector + * + * @param[in] m mat4 (left) + * @param[in] v vec4 (right, column vector) + * @param[out] dest vec4 (result, column vector) + */ +f_inline +void +glm_mat4_mulv(mat4 m, vec4 v, vec4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_mulv_sse2(m, v, dest); +#elif defined(CGLM_NEON_FP) + glm_mat4_mulv_neon(m, v, dest); +#else + vec4 res; + res[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3]; + res[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3]; + res[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3]; + res[3] = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3]; + glm_vec4_copy(res, dest); +#endif +} + +/*! + * @brief trace of matrix + * + * sum of the elements on the main diagonal from upper left to the lower right + * + * @param[in] m matrix + */ +f_inline +float +glm_mat4_trace(mat4 m) { + return m[0][0] + m[1][1] + m[2][2] + m[3][3]; +} + +/*! + * @brief trace of matrix (rotation part) + * + * sum of the elements on the main diagonal from upper left to the lower right + * + * @param[in] m matrix + */ +f_inline +float +glm_mat4_trace3(mat4 m) { + return m[0][0] + m[1][1] + m[2][2]; +} + +/*! + * @brief convert mat4's rotation part to quaternion + * + * @param[in] m affine matrix + * @param[out] dest destination quaternion + */ +f_inline +void +glm_mat4_quat(mat4 m, vec4 dest) { + float trace, r, rinv; + + /* it seems using like m12 instead of m[1][2] causes extra instructions */ + + trace = m[0][0] + m[1][1] + m[2][2]; + if (trace >= 0.0f) { + r = sqrtf(1.0f + trace); + rinv = 0.5f / r; + + dest[0] = rinv * (m[1][2] - m[2][1]); + dest[1] = rinv * (m[2][0] - m[0][2]); + dest[2] = rinv * (m[0][1] - m[1][0]); + dest[3] = r * 0.5f; + } else if (m[0][0] >= m[1][1] && m[0][0] >= m[2][2]) { + r = sqrtf(1.0f - m[1][1] - m[2][2] + m[0][0]); + rinv = 0.5f / r; + + dest[0] = r * 0.5f; + dest[1] = rinv * (m[0][1] + m[1][0]); + dest[2] = rinv * (m[0][2] + m[2][0]); + dest[3] = rinv * (m[1][2] - m[2][1]); + } else if (m[1][1] >= m[2][2]) { + r = sqrtf(1.0f - m[0][0] - m[2][2] + m[1][1]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][1] + m[1][0]); + dest[1] = r * 0.5f; + dest[2] = rinv * (m[1][2] + m[2][1]); + dest[3] = rinv * (m[2][0] - m[0][2]); + } else { + r = sqrtf(1.0f - m[0][0] - m[1][1] + m[2][2]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][2] + m[2][0]); + dest[1] = rinv * (m[1][2] + m[2][1]); + dest[2] = r * 0.5f; + dest[3] = rinv * (m[0][1] - m[1][0]); + } +} + +/*! + * @brief multiply vector with mat4 + * + * actually the result is vec4, after multiplication the last component + * is trimmed. if you need it don't use this func. + * + * @param[in] m mat4(affine transform) + * @param[in] v vec3 + * @param[in] last 4th item to make it vec4 + * @param[out] dest result vector (vec3) + */ +f_inline +void +glm_mat4_mulv3(mat4 m, vec3 v, float last, vec3 dest) { + vec4 res; + glm_vec4(v, last, res); + glm_mat4_mulv(m, res, res); + glm_vec3(res, dest); +} + +/*! + * @brief transpose mat4 and store in dest + * + * source matrix will not be transposed unless dest is m + * + * @param[in] m matrix + * @param[out] dest result + */ +f_inline +void +glm_mat4_transpose_to(mat4 m, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_transp_sse2(m, dest); +#elif defined(CGLM_NEON_FP) + glm_mat4_transp_neon(m, dest); +#else + dest[0][0] = m[0][0]; dest[1][0] = m[0][1]; + dest[0][1] = m[1][0]; dest[1][1] = m[1][1]; + dest[0][2] = m[2][0]; dest[1][2] = m[2][1]; + dest[0][3] = m[3][0]; dest[1][3] = m[3][1]; + dest[2][0] = m[0][2]; dest[3][0] = m[0][3]; + dest[2][1] = m[1][2]; dest[3][1] = m[1][3]; + dest[2][2] = m[2][2]; dest[3][2] = m[2][3]; + dest[2][3] = m[3][2]; dest[3][3] = m[3][3]; +#endif +} + +/*! + * @brief tranpose mat4 and store result in same matrix + * + * @param[in, out] m source and dest + */ +f_inline +void +glm_mat4_transpose(mat4 m) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_transp_sse2(m, m); +#elif defined(CGLM_NEON_FP) + glm_mat4_transp_neon(m, m); +#else + mat4 d; + glm_mat4_transpose_to(m, d); + glm_mat4_ucopy(d, m); +#endif +} + +/*! + * @brief scale (multiply with scalar) matrix without simd optimization + * + * multiply matrix with scalar + * + * @param[in, out] m matrix + * @param[in] s scalar + */ +f_inline +void +glm_mat4_scale_p(mat4 m, float s) { + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; m[0][3] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; m[1][3] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; m[2][3] *= s; + m[3][0] *= s; m[3][1] *= s; m[3][2] *= s; m[3][3] *= s; +} + +/*! + * @brief scale (multiply with scalar) matrix + * + * multiply matrix with scalar + * + * @param[in, out] m matrix + * @param[in] s scalar + */ +f_inline +void +glm_mat4_scale(mat4 m, float s) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_scale_sse2(m, s); +#elif defined(CGLM_NEON_FP) + glm_mat4_scale_neon(m, s); +#else + glm_mat4_scale_p(m, s); +#endif +} + +/*! + * @brief mat4 determinant + * + * @param[in] mat matrix + * + * @return determinant + */ +f_inline +float +glm_mat4_det(mat4 mat) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + return glm_mat4_det_sse2(mat); +#elif defined(CGLM_NEON_FP) + return glm_mat4_det_neon(mat); +#else + /* [square] det(A) = det(At) */ + float t[6]; + float a = mat[0][0], b = mat[0][1], c = mat[0][2], d = mat[0][3], + e = mat[1][0], f = mat[1][1], g = mat[1][2], h = mat[1][3], + i = mat[2][0], j = mat[2][1], k = mat[2][2], l = mat[2][3], + m = mat[3][0], n = mat[3][1], o = mat[3][2], p = mat[3][3]; + + t[0] = k * p - o * l; + t[1] = j * p - n * l; + t[2] = j * o - n * k; + t[3] = i * p - m * l; + t[4] = i * o - m * k; + t[5] = i * n - m * j; + + return a * (f * t[0] - g * t[1] + h * t[2]) + - b * (e * t[0] - g * t[3] + h * t[4]) + + c * (e * t[1] - f * t[3] + h * t[5]) + - d * (e * t[2] - f * t[4] + g * t[5]); +#endif +} + +/*! + * @brief inverse mat4 and store in dest + * + * @param[in] mat matrix + * @param[out] dest inverse matrix + */ +f_inline +void +glm_mat4_inv(mat4 mat, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_inv_sse2(mat, dest); +#elif defined(CGLM_NEON_FP) + glm_mat4_inv_neon(mat, dest); +#else + float t[6]; + float det; + float a = mat[0][0], b = mat[0][1], c = mat[0][2], d = mat[0][3], + e = mat[1][0], f = mat[1][1], g = mat[1][2], h = mat[1][3], + i = mat[2][0], j = mat[2][1], k = mat[2][2], l = mat[2][3], + m = mat[3][0], n = mat[3][1], o = mat[3][2], p = mat[3][3]; + + t[0] = k * p - o * l; t[1] = j * p - n * l; t[2] = j * o - n * k; + t[3] = i * p - m * l; t[4] = i * o - m * k; t[5] = i * n - m * j; + + dest[0][0] = f * t[0] - g * t[1] + h * t[2]; + dest[1][0] =-(e * t[0] - g * t[3] + h * t[4]); + dest[2][0] = e * t[1] - f * t[3] + h * t[5]; + dest[3][0] =-(e * t[2] - f * t[4] + g * t[5]); + + dest[0][1] =-(b * t[0] - c * t[1] + d * t[2]); + dest[1][1] = a * t[0] - c * t[3] + d * t[4]; + dest[2][1] =-(a * t[1] - b * t[3] + d * t[5]); + dest[3][1] = a * t[2] - b * t[4] + c * t[5]; + + t[0] = g * p - o * h; t[1] = f * p - n * h; t[2] = f * o - n * g; + t[3] = e * p - m * h; t[4] = e * o - m * g; t[5] = e * n - m * f; + + dest[0][2] = b * t[0] - c * t[1] + d * t[2]; + dest[1][2] =-(a * t[0] - c * t[3] + d * t[4]); + dest[2][2] = a * t[1] - b * t[3] + d * t[5]; + dest[3][2] =-(a * t[2] - b * t[4] + c * t[5]); + + t[0] = g * l - k * h; t[1] = f * l - j * h; t[2] = f * k - j * g; + t[3] = e * l - i * h; t[4] = e * k - i * g; t[5] = e * j - i * f; + + dest[0][3] =-(b * t[0] - c * t[1] + d * t[2]); + dest[1][3] = a * t[0] - c * t[3] + d * t[4]; + dest[2][3] =-(a * t[1] - b * t[3] + d * t[5]); + dest[3][3] = a * t[2] - b * t[4] + c * t[5]; + + det = 1.0f / (a * dest[0][0] + b * dest[1][0] + + c * dest[2][0] + d * dest[3][0]); + + glm_mat4_scale_p(dest, det); +#endif +} + +/*! + * @brief inverse mat4 and store in dest + * + * this func uses reciprocal approximation without extra corrections + * e.g Newton-Raphson. this should work faster than normal, + * to get more precise use glm_mat4_inv version. + * + * NOTE: You will lose precision, glm_mat4_inv is more accurate + * + * @param[in] mat matrix + * @param[out] dest inverse matrix + */ +f_inline +void +glm_mat4_inv_fast(mat4 mat, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat4_inv_fast_sse2(mat, dest); +#else + glm_mat4_inv(mat, dest); +#endif +} + +/*! + * @brief swap two matrix columns + * + * @param[in,out] mat matrix + * @param[in] col1 col1 + * @param[in] col2 col2 + */ +f_inline +void +glm_mat4_swap_col(mat4 mat, int col1, int col2) { + f_align(16) vec4 tmp; + glm_vec4_copy(mat[col1], tmp); + glm_vec4_copy(mat[col2], mat[col1]); + glm_vec4_copy(tmp, mat[col2]); +} + +/*! + * @brief swap two matrix rows + * + * @param[in,out] mat matrix + * @param[in] row1 row1 + * @param[in] row2 row2 + */ +f_inline +void +glm_mat4_swap_row(mat4 mat, int row1, int row2) { + f_align(16) vec4 tmp; + tmp[0] = mat[0][row1]; + tmp[1] = mat[1][row1]; + tmp[2] = mat[2][row1]; + tmp[3] = mat[3][row1]; + + mat[0][row1] = mat[0][row2]; + mat[1][row1] = mat[1][row2]; + mat[2][row1] = mat[2][row2]; + mat[3][row1] = mat[3][row2]; + + mat[0][row2] = tmp[0]; + mat[1][row2] = tmp[1]; + mat[2][row2] = tmp[2]; + mat[3][row2] = tmp[3]; +} + +/*! + * @brief helper for R (row vector) * M (matrix) * C (column vector) + * + * rmc stands for Row * Matrix * Column + * + * the result is scalar because R * M = Matrix1x4 (row vector), + * then Matrix1x4 * Vec4 (column vector) = Matrix1x1 (Scalar) + * + * @param[in] r row vector or matrix1x4 + * @param[in] m matrix4x4 + * @param[in] c column vector or matrix4x1 + * + * @return scalar value e.g. B(s) + */ +f_inline +float +glm_mat4_rmc(vec4 r, mat4 m, vec4 c) { + vec4 tmp; + glm_mat4_mulv(m, c, tmp); + return glm_vec4_dot(r, tmp); +} + + + + + + + + + + +#ifdef CGLM_SIMD +#if defined( __SSE__ ) || defined( __SSE2__ ) + +f_inline +void +glm_mat3_mul_sse2(mat3 m1, mat3 m2, mat3 dest) { + __m128 l0, l1, l2, r0, r1, r2, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9; + + l0 = _mm_loadu_ps(m1[0]); + l1 = _mm_loadu_ps(&m1[1][1]); + + r0 = _mm_loadu_ps(m2[0]); + r1 = _mm_loadu_ps(&m2[1][1]); + + x8 = glmm_shuff1(l0, 0, 2, 1, 0); /* a00 a02 a01 a00 */ + x1 = glmm_shuff1(r0, 3, 0, 0, 0); /* b10 b00 b00 b00 */ + x2 = _mm_shuffle_ps(l0, l1, _MM_SHUFFLE(1, 0, 3, 3)); /* a12 a11 a10 a10 */ + x3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(2, 0, 3, 1)); /* b20 b11 b10 b01 */ + x0 = _mm_mul_ps(x8, x1); + + x6 = glmm_shuff1(l0, 1, 0, 2, 1); /* a01 a00 a02 a01 */ + x7 = glmm_shuff1(x3, 3, 3, 1, 1); /* b20 b20 b10 b10 */ + l2 = _mm_load_ss(&m1[2][2]); + r2 = _mm_load_ss(&m2[2][2]); + x1 = _mm_mul_ps(x6, x7); + l2 = glmm_shuff1(l2, 0, 0, 1, 0); /* a22 a22 0.f a22 */ + r2 = glmm_shuff1(r2, 0, 0, 1, 0); /* b22 b22 0.f b22 */ + + x4 = glmm_shuff1(x2, 0, 3, 2, 0); /* a10 a12 a11 a10 */ + x5 = glmm_shuff1(x2, 2, 0, 3, 2); /* a11 a10 a12 a11 */ + x6 = glmm_shuff1(x3, 2, 0, 0, 0); /* b11 b01 b01 b01 */ + x2 = glmm_shuff1(r1, 3, 3, 0, 0); /* b21 b21 b11 b11 */ + + x8 = _mm_unpackhi_ps(x8, x4); /* a10 a00 a12 a02 */ + x9 = _mm_unpackhi_ps(x7, x2); /* b21 b20 b21 b20 */ + + x0 = glmm_fmadd(x4, x6, x0); + x1 = glmm_fmadd(x5, x2, x1); + + x2 = _mm_movehl_ps(l2, l1); /* a22 a22 a21 a20 */ + x3 = glmm_shuff1(x2, 0, 2, 1, 0); /* a20 a22 a21 a20 */ + x2 = glmm_shuff1(x2, 1, 0, 2, 1); /* a21 a20 a22 a21 */ + x4 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 1, 2, 2)); /* b12 b12 b02 b02 */ + + x5 = glmm_shuff1(x4, 3, 0, 0, 0); /* b12 b02 b02 b02 */ + x4 = _mm_movehl_ps(r2, x4); /* b22 b22 b12 b12 */ + x0 = glmm_fmadd(x3, x5, x0); + x1 = glmm_fmadd(x2, x4, x1); + + /* + Dot Product : dest[2][2] = a02 * b20 + + a12 * b21 + + a22 * b22 + + 0 * 00 */ + x2 = _mm_movelh_ps(x8, l2); /* 0.f a22 a12 a02 */ + x3 = _mm_movelh_ps(x9, r2); /* 0.f b22 b21 b20 */ + x2 = glmm_vdots(x2, x3); + + _mm_storeu_ps(&dest[0][0], x0); + _mm_storeu_ps(&dest[1][1], x1); + _mm_store_ss (&dest[2][2], x2); +} + +#endif +#endif + +#define GLM_MAT3_IDENTITY_INIT {{1.0f, 0.0f, 0.0f}, \ + {0.0f, 1.0f, 0.0f}, \ + {0.0f, 0.0f, 1.0f}} +#define GLM_MAT3_ZERO_INIT {{0.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f}, \ + {0.0f, 0.0f, 0.0f}} + + +/* for C only */ +#define GLM_MAT3_IDENTITY ((mat3)GLM_MAT3_IDENTITY_INIT) +#define GLM_MAT3_ZERO ((mat3)GLM_MAT3_ZERO_INIT) + +/*! + * @brief copy all members of [mat] to [dest] + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat3_copy(mat3 mat, mat3 dest) { + dest[0][0] = mat[0][0]; + dest[0][1] = mat[0][1]; + dest[0][2] = mat[0][2]; + + dest[1][0] = mat[1][0]; + dest[1][1] = mat[1][1]; + dest[1][2] = mat[1][2]; + + dest[2][0] = mat[2][0]; + dest[2][1] = mat[2][1]; + dest[2][2] = mat[2][2]; +} + +/*! + * @brief make given matrix identity. It is identical with below, + * but it is more easy to do that with this func especially for members + * e.g. glm_mat3_identity(aStruct->aMatrix); + * + * @code + * glm_mat3_copy(GLM_MAT3_IDENTITY, mat); // C only + * + * // or + * mat3 mat = GLM_MAT3_IDENTITY_INIT; + * @endcode + * + * @param[in, out] mat destination + */ +f_inline +void +glm_mat3_identity(mat3 mat) { + f_align(16) mat3 t = GLM_MAT3_IDENTITY_INIT; + glm_mat3_copy(t, mat); +} + +/*! + * @brief make given matrix array's each element identity matrix + * + * @param[in, out] mat matrix array (must be aligned (16/32) + * if alignment is not disabled) + * + * @param[in] count count of matrices + */ +f_inline +void +glm_mat3_identity_array(mat3 * __restrict mat, uint count) { + f_align(16) mat3 t = GLM_MAT3_IDENTITY_INIT; + uint i; + + for (i = 0; i < count; i++) { + glm_mat3_copy(t, mat[i]); + } +} + +/*! + * @brief make given matrix zero. + * + * @param[in, out] mat matrix + */ +f_inline +void +glm_mat3_zero(mat3 mat) { + f_align(16) mat3 t = GLM_MAT3_ZERO_INIT; + glm_mat3_copy(t, mat); +} + +/*! + * @brief multiply m1 and m2 to dest + * + * m1, m2 and dest matrices can be same matrix, it is possible to write this: + * + * @code + * mat3 m = GLM_MAT3_IDENTITY_INIT; + * glm_mat3_mul(m, m, m); + * @endcode + * + * @param[in] m1 left matrix + * @param[in] m2 right matrix + * @param[out] dest destination matrix + */ +f_inline +void +glm_mat3_mul(mat3 m1, mat3 m2, mat3 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat3_mul_sse2(m1, m2, dest); +#else + float a00 = m1[0][0], a01 = m1[0][1], a02 = m1[0][2], + a10 = m1[1][0], a11 = m1[1][1], a12 = m1[1][2], + a20 = m1[2][0], a21 = m1[2][1], a22 = m1[2][2], + + b00 = m2[0][0], b01 = m2[0][1], b02 = m2[0][2], + b10 = m2[1][0], b11 = m2[1][1], b12 = m2[1][2], + b20 = m2[2][0], b21 = m2[2][1], b22 = m2[2][2]; + + dest[0][0] = a00 * b00 + a10 * b01 + a20 * b02; + dest[0][1] = a01 * b00 + a11 * b01 + a21 * b02; + dest[0][2] = a02 * b00 + a12 * b01 + a22 * b02; + dest[1][0] = a00 * b10 + a10 * b11 + a20 * b12; + dest[1][1] = a01 * b10 + a11 * b11 + a21 * b12; + dest[1][2] = a02 * b10 + a12 * b11 + a22 * b12; + dest[2][0] = a00 * b20 + a10 * b21 + a20 * b22; + dest[2][1] = a01 * b20 + a11 * b21 + a21 * b22; + dest[2][2] = a02 * b20 + a12 * b21 + a22 * b22; +#endif +} + +/*! + * @brief transpose mat3 and store in dest + * + * source matrix will not be transposed unless dest is m + * + * @param[in] m matrix + * @param[out] dest result + */ +f_inline +void +glm_mat3_transpose_to(mat3 m, mat3 dest) { + dest[0][0] = m[0][0]; + dest[0][1] = m[1][0]; + dest[0][2] = m[2][0]; + dest[1][0] = m[0][1]; + dest[1][1] = m[1][1]; + dest[1][2] = m[2][1]; + dest[2][0] = m[0][2]; + dest[2][1] = m[1][2]; + dest[2][2] = m[2][2]; +} + +/*! + * @brief tranpose mat3 and store result in same matrix + * + * @param[in, out] m source and dest + */ +f_inline +void +glm_mat3_transpose(mat3 m) { + f_align(16) mat3 tmp; + + tmp[0][1] = m[1][0]; + tmp[0][2] = m[2][0]; + tmp[1][0] = m[0][1]; + tmp[1][2] = m[2][1]; + tmp[2][0] = m[0][2]; + tmp[2][1] = m[1][2]; + + m[0][1] = tmp[0][1]; + m[0][2] = tmp[0][2]; + m[1][0] = tmp[1][0]; + m[1][2] = tmp[1][2]; + m[2][0] = tmp[2][0]; + m[2][1] = tmp[2][1]; +} + +/*! + * @brief multiply mat3 with vec3 (column vector) and store in dest vector + * + * @param[in] m mat3 (left) + * @param[in] v vec3 (right, column vector) + * @param[out] dest vec3 (result, column vector) + */ +f_inline +void +glm_mat3_mulv(mat3 m, vec3 v, vec3 dest) { + vec3 res; + res[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2]; + res[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2]; + res[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2]; + glm_vec3_copy(res, dest); +} + +/*! + * @brief trace of matrix + * + * sum of the elements on the main diagonal from upper left to the lower right + * + * @param[in] m matrix + */ +f_inline +float +glm_mat3_trace(mat3 m) { + return m[0][0] + m[1][1] + m[2][2]; +} + +/*! + * @brief convert mat3 to quaternion + * + * @param[in] m rotation matrix + * @param[out] dest destination quaternion + */ +f_inline +void +glm_mat3_quat(mat3 m, vec4 dest) { + float trace, r, rinv; + + /* it seems using like m12 instead of m[1][2] causes extra instructions */ + + trace = m[0][0] + m[1][1] + m[2][2]; + if (trace >= 0.0f) { + r = sqrtf(1.0f + trace); + rinv = 0.5f / r; + + dest[0] = rinv * (m[1][2] - m[2][1]); + dest[1] = rinv * (m[2][0] - m[0][2]); + dest[2] = rinv * (m[0][1] - m[1][0]); + dest[3] = r * 0.5f; + } else if (m[0][0] >= m[1][1] && m[0][0] >= m[2][2]) { + r = sqrtf(1.0f - m[1][1] - m[2][2] + m[0][0]); + rinv = 0.5f / r; + + dest[0] = r * 0.5f; + dest[1] = rinv * (m[0][1] + m[1][0]); + dest[2] = rinv * (m[0][2] + m[2][0]); + dest[3] = rinv * (m[1][2] - m[2][1]); + } else if (m[1][1] >= m[2][2]) { + r = sqrtf(1.0f - m[0][0] - m[2][2] + m[1][1]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][1] + m[1][0]); + dest[1] = r * 0.5f; + dest[2] = rinv * (m[1][2] + m[2][1]); + dest[3] = rinv * (m[2][0] - m[0][2]); + } else { + r = sqrtf(1.0f - m[0][0] - m[1][1] + m[2][2]); + rinv = 0.5f / r; + + dest[0] = rinv * (m[0][2] + m[2][0]); + dest[1] = rinv * (m[1][2] + m[2][1]); + dest[2] = r * 0.5f; + dest[3] = rinv * (m[0][1] - m[1][0]); + } +} + +/*! + * @brief scale (multiply with scalar) matrix + * + * multiply matrix with scalar + * + * @param[in, out] m matrix + * @param[in] s scalar + */ +f_inline +void +glm_mat3_scale(mat3 m, float s) { + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; +} + +/*! + * @brief mat3 determinant + * + * @param[in] mat matrix + * + * @return determinant + */ +f_inline +float +glm_mat3_det(mat3 mat) { + float a = mat[0][0], b = mat[0][1], c = mat[0][2], + d = mat[1][0], e = mat[1][1], f = mat[1][2], + g = mat[2][0], h = mat[2][1], i = mat[2][2]; + + return a * (e * i - h * f) - d * (b * i - c * h) + g * (b * f - c * e); +} + +/*! + * @brief inverse mat3 and store in dest + * + * @param[in] mat matrix + * @param[out] dest inverse matrix + */ +f_inline +void +glm_mat3_inv(mat3 mat, mat3 dest) { + float det; + float a = mat[0][0], b = mat[0][1], c = mat[0][2], + d = mat[1][0], e = mat[1][1], f = mat[1][2], + g = mat[2][0], h = mat[2][1], i = mat[2][2]; + + dest[0][0] = e * i - f * h; + dest[0][1] = -(b * i - h * c); + dest[0][2] = b * f - e * c; + dest[1][0] = -(d * i - g * f); + dest[1][1] = a * i - c * g; + dest[1][2] = -(a * f - d * c); + dest[2][0] = d * h - g * e; + dest[2][1] = -(a * h - g * b); + dest[2][2] = a * e - b * d; + + det = 1.0f / (a * dest[0][0] + b * dest[1][0] + c * dest[2][0]); + + glm_mat3_scale(dest, det); +} + +/*! + * @brief swap two matrix columns + * + * @param[in,out] mat matrix + * @param[in] col1 col1 + * @param[in] col2 col2 + */ +f_inline +void +glm_mat3_swap_col(mat3 mat, int col1, int col2) { + vec3 tmp; + glm_vec3_copy(mat[col1], tmp); + glm_vec3_copy(mat[col2], mat[col1]); + glm_vec3_copy(tmp, mat[col2]); +} + +/*! + * @brief swap two matrix rows + * + * @param[in,out] mat matrix + * @param[in] row1 row1 + * @param[in] row2 row2 + */ +f_inline +void +glm_mat3_swap_row(mat3 mat, int row1, int row2) { + vec3 tmp; + tmp[0] = mat[0][row1]; + tmp[1] = mat[1][row1]; + tmp[2] = mat[2][row1]; + + mat[0][row1] = mat[0][row2]; + mat[1][row1] = mat[1][row2]; + mat[2][row1] = mat[2][row2]; + + mat[0][row2] = tmp[0]; + mat[1][row2] = tmp[1]; + mat[2][row2] = tmp[2]; +} + +/*! + * @brief helper for R (row vector) * M (matrix) * C (column vector) + * + * rmc stands for Row * Matrix * Column + * + * the result is scalar because R * M = Matrix1x3 (row vector), + * then Matrix1x3 * Vec3 (column vector) = Matrix1x1 (Scalar) + * + * @param[in] r row vector or matrix1x3 + * @param[in] m matrix3x3 + * @param[in] c column vector or matrix3x1 + * + * @return scalar value e.g. Matrix1x1 + */ +f_inline +float +glm_mat3_rmc(vec3 r, mat3 m, vec3 c) { + vec3 tmp; + glm_mat3_mulv(m, c, tmp); + return glm_vec3_dot(r, tmp); +} + + + + + + + + + + +#ifdef CGLM_SIMD +#if defined( __SSE__ ) || defined( __SSE2__ ) + +f_inline +void +glm_mat2_mul_sse2(mat2 m1, mat2 m2, mat2 dest) { + __m128 x0, x1, x2, x3, x4; + + x1 = glmm_load(m1[0]); /* d c b a */ + x2 = glmm_load(m2[0]); /* h g f e */ + + x3 = glmm_shuff1(x2, 2, 2, 0, 0); + x4 = glmm_shuff1(x2, 3, 3, 1, 1); + x0 = _mm_movelh_ps(x1, x1); + x2 = _mm_movehl_ps(x1, x1); + + /* + dest[0][0] = a * e + c * f; + dest[0][1] = b * e + d * f; + dest[1][0] = a * g + c * h; + dest[1][1] = b * g + d * h; + */ + x0 = glmm_fmadd(x0, x3, _mm_mul_ps(x2, x4)); + + glmm_store(dest[0], x0); +} + +f_inline +void +glm_mat2_transp_sse2(mat2 m, mat2 dest) { + /* d c b a */ + /* d b c a */ + glmm_store(dest[0], glmm_shuff1(glmm_load(m[0]), 3, 1, 2, 0)); +} + +#endif +#endif + +#define GLM_MAT2_IDENTITY_INIT {{1.0f, 0.0f}, {0.0f, 1.0f}} +#define GLM_MAT2_ZERO_INIT {{0.0f, 0.0f}, {0.0f, 0.0f}} + +/* for C only */ +#define GLM_MAT2_IDENTITY ((mat2)GLM_MAT2_IDENTITY_INIT) +#define GLM_MAT2_ZERO ((mat2)GLM_MAT2_ZERO_INIT) + +/*! + * @brief copy all members of [mat] to [dest] + * + * @param[in] mat source + * @param[out] dest destination + */ +f_inline +void +glm_mat2_copy(mat2 mat, mat2 dest) { + glm_vec4_ucopy(mat[0], dest[0]); +} + +/*! + * @brief make given matrix identity. It is identical with below, + * but it is more easy to do that with this func especially for members + * e.g. glm_mat2_identity(aStruct->aMatrix); + * + * @code + * glm_mat2_copy(GLM_MAT2_IDENTITY, mat); // C only + * + * // or + * mat2 mat = GLM_MAT2_IDENTITY_INIT; + * @endcode + * + * @param[in, out] mat destination + */ +f_inline +void +glm_mat2_identity(mat2 mat) { + f_align(16) mat2 t = GLM_MAT2_IDENTITY_INIT; + glm_mat2_copy(t, mat); +} + +/*! + * @brief make given matrix array's each element identity matrix + * + * @param[in, out] mat matrix array (must be aligned (16) + * if alignment is not disabled) + * + * @param[in] count count of matrices + */ +f_inline +void +glm_mat2_identity_array(mat2 * __restrict mat, uint count) { + f_align(16) mat2 t = GLM_MAT2_IDENTITY_INIT; + uint i; + + for (i = 0; i < count; i++) { + glm_mat2_copy(t, mat[i]); + } +} + +/*! + * @brief make given matrix zero. + * + * @param[in, out] mat matrix + */ +f_inline +void +glm_mat2_zero(mat2 mat) { + f_align(16) mat2 t = GLM_MAT2_ZERO_INIT; + glm_mat2_copy(t, mat); +} + +/*! + * @brief multiply m1 and m2 to dest + * + * m1, m2 and dest matrices can be same matrix, it is possible to write this: + * + * @code + * mat2 m = GLM_MAT2_IDENTITY_INIT; + * glm_mat2_mul(m, m, m); + * @endcode + * + * @param[in] m1 left matrix + * @param[in] m2 right matrix + * @param[out] dest destination matrix + */ +f_inline +void +glm_mat2_mul(mat2 m1, mat2 m2, mat2 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat2_mul_sse2(m1, m2, dest); +#elif defined(CGLM_NEON_FP) + glm_mat2_mul_neon(m1, m2, dest); +#else + float a00 = m1[0][0], a01 = m1[0][1], + a10 = m1[1][0], a11 = m1[1][1], + b00 = m2[0][0], b01 = m2[0][1], + b10 = m2[1][0], b11 = m2[1][1]; + + dest[0][0] = a00 * b00 + a10 * b01; + dest[0][1] = a01 * b00 + a11 * b01; + dest[1][0] = a00 * b10 + a10 * b11; + dest[1][1] = a01 * b10 + a11 * b11; +#endif +} + +/*! + * @brief transpose mat2 and store in dest + * + * source matrix will not be transposed unless dest is m + * + * @param[in] m matrix + * @param[out] dest result + */ +f_inline +void +glm_mat2_transpose_to(mat2 m, mat2 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mat2_transp_sse2(m, dest); +#else + dest[0][0] = m[0][0]; + dest[0][1] = m[1][0]; + dest[1][0] = m[0][1]; + dest[1][1] = m[1][1]; +#endif +} + +/*! + * @brief tranpose mat2 and store result in same matrix + * + * @param[in, out] m source and dest + */ +f_inline +void +glm_mat2_transpose(mat2 m) { + float tmp; + tmp = m[0][1]; + m[0][1] = m[1][0]; + m[1][0] = tmp; +} + +/*! + * @brief multiply mat2 with vec2 (column vector) and store in dest vector + * + * @param[in] m mat2 (left) + * @param[in] v vec2 (right, column vector) + * @param[out] dest vec2 (result, column vector) + */ +f_inline +void +glm_mat2_mulv(mat2 m, vec2 v, vec2 dest) { + dest[0] = m[0][0] * v[0] + m[1][0] * v[1]; + dest[1] = m[0][1] * v[0] + m[1][1] * v[1]; +} + +/*! + * @brief trace of matrix + * + * sum of the elements on the main diagonal from upper left to the lower right + * + * @param[in] m matrix + */ +f_inline +float +glm_mat2_trace(mat2 m) { + return m[0][0] + m[1][1]; +} + +/*! + * @brief scale (multiply with scalar) matrix + * + * multiply matrix with scalar + * + * @param[in, out] m matrix + * @param[in] s scalar + */ +f_inline +void +glm_mat2_scale(mat2 m, float s) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glmm_store(m[0], _mm_mul_ps(_mm_loadu_ps(m[0]), _mm_set1_ps(s))); +#elif defined(CGLM_NEON_FP) + vst1q_f32(m[0], vmulq_f32(vld1q_f32(m[0]), vdupq_n_f32(s))); +#else + m[0][0] = m[0][0] * s; + m[0][1] = m[0][1] * s; + m[1][0] = m[1][0] * s; + m[1][1] = m[1][1] * s; +#endif +} + +/*! + * @brief mat2 determinant + * + * @param[in] mat matrix + * + * @return determinant + */ +f_inline +float +glm_mat2_det(mat2 mat) { + return mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1]; +} + +/*! + * @brief inverse mat2 and store in dest + * + * @param[in] mat matrix + * @param[out] dest inverse matrix + */ +f_inline +void +glm_mat2_inv(mat2 mat, mat2 dest) { + float det; + float a = mat[0][0], b = mat[0][1], + c = mat[1][0], d = mat[1][1]; + + det = 1.0f / (a * d - b * c); + + dest[0][0] = d * det; + dest[0][1] = -b * det; + dest[1][0] = -c * det; + dest[1][1] = a * det; +} + +/*! + * @brief swap two matrix columns + * + * @param[in,out] mat matrix + * @param[in] col1 col1 + * @param[in] col2 col2 + */ +f_inline +void +glm_mat2_swap_col(mat2 mat, int col1, int col2) { + float a, b; + + a = mat[col1][0]; + b = mat[col1][1]; + + mat[col1][0] = mat[col2][0]; + mat[col1][1] = mat[col2][1]; + + mat[col2][0] = a; + mat[col2][1] = b; +} + +/*! + * @brief swap two matrix rows + * + * @param[in,out] mat matrix + * @param[in] row1 row1 + * @param[in] row2 row2 + */ +f_inline +void +glm_mat2_swap_row(mat2 mat, int row1, int row2) { + float a, b; + + a = mat[0][row1]; + b = mat[1][row1]; + + mat[0][row1] = mat[0][row2]; + mat[1][row1] = mat[1][row2]; + + mat[0][row2] = a; + mat[1][row2] = b; +} + +/*! + * @brief helper for R (row vector) * M (matrix) * C (column vector) + * + * rmc stands for Row * Matrix * Column + * + * the result is scalar because R * M = Matrix1x2 (row vector), + * then Matrix1x2 * Vec2 (column vector) = Matrix1x1 (Scalar) + * + * @param[in] r row vector or matrix1x2 + * @param[in] m matrix2x2 + * @param[in] c column vector or matrix2x1 + * + * @return scalar value e.g. Matrix1x1 + */ +f_inline +float +glm_mat2_rmc(vec2 r, mat2 m, vec2 c) { + vec2 tmp; + glm_mat2_mulv(m, c, tmp); + return glm_vec2_dot(r, tmp); +} + + + + + + + + + +#ifdef CGLM_SIMD +#if defined( __SSE__ ) || defined( __SSE2__ ) + +f_inline +void +glm_mul_sse2(mat4 m1, mat4 m2, mat4 dest) { + /* D = R * L (Column-Major) */ + glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3; + + l = glmm_load(m1[0]); + r0 = glmm_load(m2[0]); + r1 = glmm_load(m2[1]); + r2 = glmm_load(m2[2]); + r3 = glmm_load(m2[3]); + + v0 = _mm_mul_ps(glmm_splat_x(r0), l); + v1 = _mm_mul_ps(glmm_splat_x(r1), l); + v2 = _mm_mul_ps(glmm_splat_x(r2), l); + v3 = _mm_mul_ps(glmm_splat_x(r3), l); + + l = glmm_load(m1[1]); + v0 = glmm_fmadd(glmm_splat_y(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_y(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_y(r2), l, v2); + v3 = glmm_fmadd(glmm_splat_y(r3), l, v3); + + l = glmm_load(m1[2]); + v0 = glmm_fmadd(glmm_splat_z(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_z(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_z(r2), l, v2); + v3 = glmm_fmadd(glmm_splat_z(r3), l, v3); + + l = glmm_load(m1[3]); + v3 = glmm_fmadd(glmm_splat_w(r3), l, v3); + + glmm_store(dest[0], v0); + glmm_store(dest[1], v1); + glmm_store(dest[2], v2); + glmm_store(dest[3], v3); +} + +f_inline +void +glm_mul_rot_sse2(mat4 m1, mat4 m2, mat4 dest) { + /* D = R * L (Column-Major) */ + + glmm_128 l, r0, r1, r2, v0, v1, v2; + + l = glmm_load(m1[0]); + r0 = glmm_load(m2[0]); + r1 = glmm_load(m2[1]); + r2 = glmm_load(m2[2]); + + v0 = _mm_mul_ps(glmm_splat_x(r0), l); + v1 = _mm_mul_ps(glmm_splat_x(r1), l); + v2 = _mm_mul_ps(glmm_splat_x(r2), l); + + l = glmm_load(m1[1]); + v0 = glmm_fmadd(glmm_splat_y(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_y(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_y(r2), l, v2); + + l = glmm_load(m1[2]); + v0 = glmm_fmadd(glmm_splat_z(r0), l, v0); + v1 = glmm_fmadd(glmm_splat_z(r1), l, v1); + v2 = glmm_fmadd(glmm_splat_z(r2), l, v2); + + glmm_store(dest[0], v0); + glmm_store(dest[1], v1); + glmm_store(dest[2], v2); + glmm_store(dest[3], glmm_load(m1[3])); +} + +f_inline +void +glm_inv_tr_sse2(mat4 mat) { + __m128 r0, r1, r2, r3, x0, x1, x2, x3, x4, x5; + + r0 = glmm_load(mat[0]); + r1 = glmm_load(mat[1]); + r2 = glmm_load(mat[2]); + r3 = glmm_load(mat[3]); + x1 = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f); + + _MM_TRANSPOSE4_PS(r0, r1, r2, x1); + + x2 = glmm_shuff1(r3, 0, 0, 0, 0); + x3 = glmm_shuff1(r3, 1, 1, 1, 1); + x4 = glmm_shuff1(r3, 2, 2, 2, 2); + x5 = _mm_set1_ps(-0.f); + + x0 = glmm_fmadd(r0, x2, glmm_fmadd(r1, x3, _mm_mul_ps(r2, x4))); + x0 = _mm_xor_ps(x0, x5); + + x0 = _mm_add_ps(x0, x1); + + glmm_store(mat[0], r0); + glmm_store(mat[1], r1); + glmm_store(mat[2], r2); + glmm_store(mat[3], x0); +} + +#endif +#endif + +/*! + * @brief this is similar to glm_mat4_mul but specialized to affine transform + * + * Matrix format should be: + * R R R X + * R R R Y + * R R R Z + * 0 0 0 W + * + * this reduces some multiplications. It should be faster than mat4_mul. + * if you are not sure about matrix format then DON'T use this! use mat4_mul + * + * @param[in] m1 affine matrix 1 + * @param[in] m2 affine matrix 2 + * @param[out] dest result matrix + */ +f_inline +void +glm_mul(mat4 m1, mat4 m2, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mul_sse2(m1, m2, dest); +#elif defined(CGLM_NEON_FP) + glm_mul_neon(m1, m2, dest); +#else + float a00 = m1[0][0], a01 = m1[0][1], a02 = m1[0][2], a03 = m1[0][3], + a10 = m1[1][0], a11 = m1[1][1], a12 = m1[1][2], a13 = m1[1][3], + a20 = m1[2][0], a21 = m1[2][1], a22 = m1[2][2], a23 = m1[2][3], + a30 = m1[3][0], a31 = m1[3][1], a32 = m1[3][2], a33 = m1[3][3], + + b00 = m2[0][0], b01 = m2[0][1], b02 = m2[0][2], + b10 = m2[1][0], b11 = m2[1][1], b12 = m2[1][2], + b20 = m2[2][0], b21 = m2[2][1], b22 = m2[2][2], + b30 = m2[3][0], b31 = m2[3][1], b32 = m2[3][2], b33 = m2[3][3]; + + dest[0][0] = a00 * b00 + a10 * b01 + a20 * b02; + dest[0][1] = a01 * b00 + a11 * b01 + a21 * b02; + dest[0][2] = a02 * b00 + a12 * b01 + a22 * b02; + dest[0][3] = a03 * b00 + a13 * b01 + a23 * b02; + + dest[1][0] = a00 * b10 + a10 * b11 + a20 * b12; + dest[1][1] = a01 * b10 + a11 * b11 + a21 * b12; + dest[1][2] = a02 * b10 + a12 * b11 + a22 * b12; + dest[1][3] = a03 * b10 + a13 * b11 + a23 * b12; + + dest[2][0] = a00 * b20 + a10 * b21 + a20 * b22; + dest[2][1] = a01 * b20 + a11 * b21 + a21 * b22; + dest[2][2] = a02 * b20 + a12 * b21 + a22 * b22; + dest[2][3] = a03 * b20 + a13 * b21 + a23 * b22; + + dest[3][0] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; + dest[3][1] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; + dest[3][2] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; + dest[3][3] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; +#endif +} + +/*! + * @brief this is similar to glm_mat4_mul but specialized to affine transform + * + * Right Matrix format should be: + * R R R 0 + * R R R 0 + * R R R 0 + * 0 0 0 1 + * + * this reduces some multiplications. It should be faster than mat4_mul. + * if you are not sure about matrix format then DON'T use this! use mat4_mul + * + * @param[in] m1 affine matrix 1 + * @param[in] m2 affine matrix 2 + * @param[out] dest result matrix + */ +f_inline +void +glm_mul_rot(mat4 m1, mat4 m2, mat4 dest) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_mul_rot_sse2(m1, m2, dest); +#elif defined(CGLM_NEON_FP) + glm_mul_rot_neon(m1, m2, dest); +#else + float a00 = m1[0][0], a01 = m1[0][1], a02 = m1[0][2], a03 = m1[0][3], + a10 = m1[1][0], a11 = m1[1][1], a12 = m1[1][2], a13 = m1[1][3], + a20 = m1[2][0], a21 = m1[2][1], a22 = m1[2][2], a23 = m1[2][3], + a30 = m1[3][0], a31 = m1[3][1], a32 = m1[3][2], a33 = m1[3][3], + + b00 = m2[0][0], b01 = m2[0][1], b02 = m2[0][2], + b10 = m2[1][0], b11 = m2[1][1], b12 = m2[1][2], + b20 = m2[2][0], b21 = m2[2][1], b22 = m2[2][2]; + + dest[0][0] = a00 * b00 + a10 * b01 + a20 * b02; + dest[0][1] = a01 * b00 + a11 * b01 + a21 * b02; + dest[0][2] = a02 * b00 + a12 * b01 + a22 * b02; + dest[0][3] = a03 * b00 + a13 * b01 + a23 * b02; + + dest[1][0] = a00 * b10 + a10 * b11 + a20 * b12; + dest[1][1] = a01 * b10 + a11 * b11 + a21 * b12; + dest[1][2] = a02 * b10 + a12 * b11 + a22 * b12; + dest[1][3] = a03 * b10 + a13 * b11 + a23 * b12; + + dest[2][0] = a00 * b20 + a10 * b21 + a20 * b22; + dest[2][1] = a01 * b20 + a11 * b21 + a21 * b22; + dest[2][2] = a02 * b20 + a12 * b21 + a22 * b22; + dest[2][3] = a03 * b20 + a13 * b21 + a23 * b22; + + dest[3][0] = a30; + dest[3][1] = a31; + dest[3][2] = a32; + dest[3][3] = a33; +#endif +} + +/*! + * @brief inverse orthonormal rotation + translation matrix (ridig-body) + * + * @code + * X = | R T | X' = | R' -R'T | + * | 0 1 | | 0 1 | + * @endcode + * + * @param[in,out] mat matrix + */ +f_inline +void +glm_inv_tr(mat4 mat) { +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_inv_tr_sse2(mat); +#elif defined(CGLM_NEON_FP) + glm_inv_tr_neon(mat); +#else + f_align(16) mat3 r; + f_align(8) vec3 t; + + /* rotate */ + glm_mat4_pick3t(mat, r); + glm_mat4_ins3(r, mat); + + /* translate */ + glm_mat3_mulv(r, mat[3], t); + glm_vec3_negate(t); + glm_vec3_copy(t, mat[3]); +#endif +} + + + + + + + + +/*! + * @brief creates NEW translate transform matrix by v vector + * + * @param[out] m affine transfrom + * @param[in] v translate vector [x, y, z] + */ +f_inline +void +glm_translate_make(mat4 m, vec3 v) { + glm_mat4_identity(m); + glm_vec3_copy(v, m[3]); +} + +/*! + * @brief scale existing transform matrix by v vector + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] v scale vector [x, y, z] + * @param[out] dest scaled matrix + */ +f_inline +void +glm_scale_to(mat4 m, vec3 v, mat4 dest) { + glm_vec4_scale(m[0], v[0], dest[0]); + glm_vec4_scale(m[1], v[1], dest[1]); + glm_vec4_scale(m[2], v[2], dest[2]); + + glm_vec4_copy(m[3], dest[3]); +} + +/*! + * @brief creates NEW scale matrix by v vector + * + * @param[out] m affine transfrom + * @param[in] v scale vector [x, y, z] + */ +f_inline +void +glm_scale_make(mat4 m, vec3 v) { + glm_mat4_identity(m); + m[0][0] = v[0]; + m[1][1] = v[1]; + m[2][2] = v[2]; +} + +/*! + * @brief scales existing transform matrix by v vector + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] v scale vector [x, y, z] + */ +f_inline +void +glm_scale(mat4 m, vec3 v) { + glm_scale_to(m, v, m); +} + +/*! + * @brief applies uniform scale to existing transform matrix v = [s, s, s] + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] s scale factor + */ +f_inline +void +glm_scale_uni(mat4 m, float s) { + f_align(8) vec3 v = { s, s, s }; + glm_scale_to(m, v, m); +} + +/*! + * @brief creates NEW rotation matrix by angle and axis + * + * axis will be normalized so you don't need to normalize it + * + * @param[out] m affine transfrom + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotate_make(mat4 m, float angle, vec3 axis) { + f_align(8) vec3 axisn, v, vs; + float c; + + c = cosf(angle); + + glm_vec3_normalize_to(axis, axisn); + glm_vec3_scale(axisn, 1.0f - c, v); + glm_vec3_scale(axisn, sinf(angle), vs); + + glm_vec3_scale(axisn, v[0], m[0]); + glm_vec3_scale(axisn, v[1], m[1]); + glm_vec3_scale(axisn, v[2], m[2]); + + m[0][0] += c; m[1][0] -= vs[2]; m[2][0] += vs[1]; + m[0][1] += vs[2]; m[1][1] += c; m[2][1] -= vs[0]; + m[0][2] -= vs[1]; m[1][2] += vs[0]; m[2][2] += c; + + m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + +/*! + * @brief decompose scale vector + * + * @param[in] m affine transform + * @param[out] s scale vector (Sx, Sy, Sz) + */ +f_inline +void +glm_decompose_scalev(mat4 m, vec3 s) { + s[0] = glm_vec3_norm(m[0]); + s[1] = glm_vec3_norm(m[1]); + s[2] = glm_vec3_norm(m[2]); +} + +/*! + * @brief returns true if matrix is uniform scaled. This is helpful for + * creating normal matrix. + * + * @param[in] m m + * + * @return boolean + */ +f_inline +byte +glm_uniscaled(mat4 m) { + f_align(8) vec3 s; + glm_decompose_scalev(m, s); + return glm_vec3_eq_all(s); +} + +/*! + * @brief decompose rotation matrix (mat4) and scale vector [Sx, Sy, Sz] + * DON'T pass projected matrix here + * + * @param[in] m affine transform + * @param[out] r rotation matrix + * @param[out] s scale matrix + */ +f_inline +void +glm_decompose_rs(mat4 m, mat4 r, vec3 s) { + f_align(16) vec4 t = {0.0f, 0.0f, 0.0f, 1.0f}; + f_align(8) vec3 v; + + glm_vec4_copy(m[0], r[0]); + glm_vec4_copy(m[1], r[1]); + glm_vec4_copy(m[2], r[2]); + glm_vec4_copy(t, r[3]); + + s[0] = glm_vec3_norm(m[0]); + s[1] = glm_vec3_norm(m[1]); + s[2] = glm_vec3_norm(m[2]); + + glm_vec4_scale(r[0], 1.0f/s[0], r[0]); + glm_vec4_scale(r[1], 1.0f/s[1], r[1]); + glm_vec4_scale(r[2], 1.0f/s[2], r[2]); + + /* Note from Apple Open Source (assume that the matrix is orthonormal): + check for a coordinate system flip. If the determinant + is -1, then negate the matrix and the scaling factors. */ + glm_vec3_cross(m[0], m[1], v); + if (glm_vec3_dot(v, m[2]) < 0.0f) { + glm_vec4_negate(r[0]); + glm_vec4_negate(r[1]); + glm_vec4_negate(r[2]); + glm_vec3_negate(s); + } +} + +/*! + * @brief decompose affine transform, TODO: extract shear factors. + * DON'T pass projected matrix here + * + * @param[in] m affine transfrom + * @param[out] t translation vector + * @param[out] r rotation matrix (mat4) + * @param[out] s scaling vector [X, Y, Z] + */ +f_inline +void +glm_decompose(mat4 m, vec4 t, mat4 r, vec3 s) { + glm_vec4_copy(m[3], t); + glm_decompose_rs(m, r, s); +} + + + + + + + + + + + + +/*! + * @brief translate existing transform matrix by v vector + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] v translate vector [x, y, z] + */ +f_inline +void +glm_translate(mat4 m, vec3 v) { +#if defined(CGLM_SIMD) + glmm_128 m0, m1, m2, m3; + + m0 = glmm_load(m[0]); + m1 = glmm_load(m[1]); + m2 = glmm_load(m[2]); + m3 = glmm_load(m[3]); + + glmm_store(m[3], + glmm_fmadd(m0, glmm_set1(v[0]), + glmm_fmadd(m1, glmm_set1(v[1]), + glmm_fmadd(m2, glmm_set1(v[2]), m3)))); +#else + glm_vec4_muladds(m[0], v[0], m[3]); + glm_vec4_muladds(m[1], v[1], m[3]); + glm_vec4_muladds(m[2], v[2], m[3]); +#endif +} + +/*! + * @brief translate existing transform matrix by v vector + * and store result in dest + * + * source matrix will remain same + * + * @param[in] m affine transfrom + * @param[in] v translate vector [x, y, z] + * @param[out] dest translated matrix + */ +f_inline +void +glm_translate_to(mat4 m, vec3 v, mat4 dest) { + glm_mat4_copy(m, dest); + glm_translate(dest, v); +} + +/*! + * @brief translate existing transform matrix by x factor + * + * @param[in, out] m affine transfrom + * @param[in] x x factor + */ +f_inline +void +glm_translate_x(mat4 m, float x) { +#if defined(CGLM_SIMD) + glmm_store(m[3], glmm_fmadd(glmm_load(m[0]), glmm_set1(x), glmm_load(m[3]))); +#else + vec4 v1; + glm_vec4_scale(m[0], x, v1); + glm_vec4_add(v1, m[3], m[3]); +#endif +} + +/*! + * @brief translate existing transform matrix by y factor + * + * @param[in, out] m affine transfrom + * @param[in] y y factor + */ +f_inline +void +glm_translate_y(mat4 m, float y) { +#if defined(CGLM_SIMD) + glmm_store(m[3], glmm_fmadd(glmm_load(m[1]), glmm_set1(y), glmm_load(m[3]))); +#else + vec4 v1; + glm_vec4_scale(m[1], y, v1); + glm_vec4_add(v1, m[3], m[3]); +#endif +} + +/*! + * @brief translate existing transform matrix by z factor + * + * @param[in, out] m affine transfrom + * @param[in] z z factor + */ +f_inline +void +glm_translate_z(mat4 m, float z) { +#if defined(CGLM_SIMD) + glmm_store(m[3], glmm_fmadd(glmm_load(m[2]), glmm_set1(z), glmm_load(m[3]))); +#else + vec4 v1; + glm_vec4_scale(m[2], z, v1); + glm_vec4_add(v1, m[3], m[3]); +#endif +} + +/*! + * @brief rotate existing transform matrix around X axis by angle + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotate_x(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[1][1] = c; + t[1][2] = s; + t[2][1] = -s; + t[2][2] = c; + + glm_mul_rot(m, t, dest); +} + +/*! + * @brief rotate existing transform matrix around Y axis by angle + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotate_y(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[0][0] = c; + t[0][2] = -s; + t[2][0] = s; + t[2][2] = c; + + glm_mul_rot(m, t, dest); +} + +/*! + * @brief rotate existing transform matrix around Z axis by angle + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotate_z(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[0][0] = c; + t[0][1] = s; + t[1][0] = -s; + t[1][1] = c; + + glm_mul_rot(m, t, dest); +} + +/*! + * @brief rotate existing transform matrix around given axis by angle + * + * @param[in, out] m affine transfrom + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotate(mat4 m, float angle, vec3 axis) { + f_align(16) mat4 rot; + glm_rotate_make(rot, angle, axis); + glm_mul_rot(m, rot, m); +} + +/*! + * @brief rotate existing transform + * around given axis by angle at given pivot point (rotation center) + * + * @param[in, out] m affine transfrom + * @param[in] pivot rotation center + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotate_at(mat4 m, vec3 pivot, float angle, vec3 axis) { + f_align(8) vec3 pivotInv; + + glm_vec3_negate_to(pivot, pivotInv); + + glm_translate(m, pivot); + glm_rotate(m, angle, axis); + glm_translate(m, pivotInv); +} + +/*! + * @brief creates NEW rotation matrix by angle and axis at given point + * + * this creates rotation matrix, it assumes you don't have a matrix + * + * this should work faster than glm_rotate_at because it reduces + * one glm_translate. + * + * @param[out] m affine transfrom + * @param[in] pivot rotation center + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotate_atm(mat4 m, vec3 pivot, float angle, vec3 axis) { + f_align(8) vec3 pivotInv; + + glm_vec3_negate_to(pivot, pivotInv); + + glm_translate_make(m, pivot); + glm_rotate(m, angle, axis); + glm_translate(m, pivotInv); +} + +/*! + * @brief rotate existing transform matrix around given axis by angle around self (doesn't affected by position) + * + * @param[in, out] m affine transfrom + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_spin(mat4 m, float angle, vec3 axis) { + f_align(16) mat4 rot; + glm_rotate_atm(rot, m[3], angle, axis); + glm_mat4_mul(m, rot, m); +} + + + + + + + + + +/*! + * @brief translate existing transform matrix by v vector + * and stores result in same matrix + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] v translate vector [x, y, z] + */ +f_inline +void +glm_translated(mat4 m, vec3 v) { + glm_vec3_add(m[3], v, m[3]); +} + +/*! + * @brief translate existing transform matrix by v vector + * and store result in dest + * + * source matrix will remain same + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in] m affine transfrom + * @param[in] v translate vector [x, y, z] + * @param[out] dest translated matrix + */ +f_inline +void +glm_translated_to(mat4 m, vec3 v, mat4 dest) { + glm_mat4_copy(m, dest); + glm_translated(dest, v); +} + +/*! + * @brief translate existing transform matrix by x factor + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] x x factor + */ +f_inline +void +glm_translated_x(mat4 m, float x) { + m[3][0] += x; +} + +/*! + * @brief translate existing transform matrix by y factor + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] y y factor + */ +f_inline +void +glm_translated_y(mat4 m, float y) { + m[3][1] += y; +} + +/*! + * @brief translate existing transform matrix by z factor + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] z z factor + */ +f_inline +void +glm_translated_z(mat4 m, float z) { + m[3][2] += z; +} + +/*! + * @brief rotate existing transform matrix around X axis by angle + * and store result in dest + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotated_x(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[1][1] = c; + t[1][2] = s; + t[2][1] = -s; + t[2][2] = c; + + glm_mul_rot(t, m, dest); +} + +/*! + * @brief rotate existing transform matrix around Y axis by angle + * and store result in dest + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotated_y(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[0][0] = c; + t[0][2] = -s; + t[2][0] = s; + t[2][2] = c; + + glm_mul_rot(t, m, dest); +} + +/*! + * @brief rotate existing transform matrix around Z axis by angle + * and store result in dest + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest rotated matrix + */ +f_inline +void +glm_rotated_z(mat4 m, float angle, mat4 dest) { + f_align(16) mat4 t = GLM_MAT4_IDENTITY_INIT; + float c, s; + + c = cosf(angle); + s = sinf(angle); + + t[0][0] = c; + t[0][1] = s; + t[1][0] = -s; + t[1][1] = c; + + glm_mul_rot(t, m, dest); +} + +/*! + * @brief rotate existing transform matrix around given axis by angle + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotated(mat4 m, float angle, vec3 axis) { + f_align(16) mat4 rot; + glm_rotate_make(rot, angle, axis); + glm_mul_rot(rot, m, m); +} + +/*! + * @brief rotate existing transform + * around given axis by angle at given pivot point (rotation center) + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] pivot rotation center + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_rotated_at(mat4 m, vec3 pivot, float angle, vec3 axis) { + f_align(8) vec3 pivotInv; + + glm_vec3_negate_to(pivot, pivotInv); + + glm_translated(m, pivot); + glm_rotated(m, angle, axis); + glm_translated(m, pivotInv); +} + +/*! + * @brief rotate existing transform matrix around given axis by angle around self (doesn't affected by position) + * + * this is POST transform, applies to existing transform as last transfrom + * + * @param[in, out] m affine transfrom + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_spinned(mat4 m, float angle, vec3 axis) { + f_align(16) mat4 rot; + glm_rotate_atm(rot, m[3], angle, axis); + glm_mat4_mul(rot, m, m); +} + + + + + + + + + +/* + Plane equation: Ax + By + Cz + D = 0; + + It stored in vec4 as [A, B, C, D]. (A, B, C) is normal and D is distance +*/ + +/* + Functions: + f_inline void glm_plane_normalize(vec4 plane); + */ + +/*! + * @brief normalizes a plane + * + * @param[in, out] plane plane to normalize + */ +f_inline +void +glm_plane_normalize(vec4 plane) { + float norm; + + if ((norm = glm_vec3_norm(plane)) == 0.0f) { + glm_vec4_zero(plane); + return; + } + + glm_vec4_scale(plane, 1.0f / norm, plane); +} + + + + + + + + + + +/*! + * @brief returns field of view angle along the Y-axis (in radians) + * + * if you need to degrees, use glm_deg to convert it or use this: + * fovy_deg = glm_deg(glm_persp_fovy(projMatrix)) + * + * @param[in] proj perspective projection matrix + */ +f_inline +float +glm_persp_fovy(mat4 proj) { + return 2.0f * atanf(1.0f / proj[1][1]); +} + +/*! + * @brief returns aspect ratio of perspective projection + * + * @param[in] proj perspective projection matrix + */ +f_inline +float +glm_persp_aspect(mat4 proj) { + return proj[1][1] / proj[0][0]; +} + + + + + + + + + +/*! + * @brief set up orthographic projection matrix + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] left viewport.left + * @param[in] right viewport.right + * @param[in] bottom viewport.bottom + * @param[in] top viewport.top + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping plane + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_rh_no(float left, float right, + float bottom, float top, + float nearZ, float farZ, + mat4 dest) { + float rl, tb, fn; + + glm_mat4_zero(dest); + + rl = 1.0f / (right - left); + tb = 1.0f / (top - bottom); + fn =-1.0f / (farZ - nearZ); + + dest[0][0] = 2.0f * rl; + dest[1][1] = 2.0f * tb; + dest[2][2] = 2.0f * fn; + dest[3][0] =-(right + left) * rl; + dest[3][1] =-(top + bottom) * tb; + dest[3][2] = (farZ + nearZ) * fn; + dest[3][3] = 1.0f; +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb_rh_no(vec3 box[2], mat4 dest) { + glm_ortho_rh_no(box[0][0], box[1][0], + box[0][1], box[1][1], + -box[1][2], -box[0][2], + dest); +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[in] padding padding + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb_p_rh_no(vec3 box[2], float padding, mat4 dest) { + glm_ortho_rh_no(box[0][0] - padding, box[1][0] + padding, + box[0][1] - padding, box[1][1] + padding, + -(box[1][2] + padding), -(box[0][2] - padding), + dest); +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[in] padding padding for near and far + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb_pz_rh_no(vec3 box[2], float padding, mat4 dest) { + glm_ortho_rh_no(box[0][0], box[1][0], + box[0][1], box[1][1], + -(box[1][2] + padding), -(box[0][2] - padding), + dest); +} + +/*! + * @brief set up unit orthographic projection matrix + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] aspect aspect ration ( width / height ) + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_default_rh_no(float aspect, mat4 dest) { + if (aspect >= 1.0f) { + glm_ortho_rh_no(-aspect, aspect, -1.0f, 1.0f, -100.0f, 100.0f, dest); + return; + } + + aspect = 1.0f / aspect; + + glm_ortho_rh_no(-1.0f, 1.0f, -aspect, aspect, -100.0f, 100.0f, dest); +} + +/*! + * @brief set up orthographic projection matrix with given CUBE size + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[in] size cube size + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_default_s_rh_no(float aspect, float size, mat4 dest) { + if (aspect >= 1.0f) { + glm_ortho_rh_no(-size * aspect, + size * aspect, + -size, + size, + -size - 100.0f, + size + 100.0f, + dest); + return; + } + + glm_ortho_rh_no(-size, + size, + -size / aspect, + size / aspect, + -size - 100.0f, + size + 100.0f, + dest); +} + + + + + + + + + +/*! + * @brief set up perspective peprojection matrix + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] left viewport.left + * @param[in] right viewport.right + * @param[in] bottom viewport.bottom + * @param[in] top viewport.top + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping plane + * @param[out] dest result matrix + */ +f_inline +void +glm_frustum_rh_no(float left, float right, + float bottom, float top, + float nearZ, float farZ, + mat4 dest) { + float rl, tb, fn, nv; + + glm_mat4_zero(dest); + + rl = 1.0f / (right - left); + tb = 1.0f / (top - bottom); + fn =-1.0f / (farZ - nearZ); + nv = 2.0f * nearZ; + + dest[0][0] = nv * rl; + dest[1][1] = nv * tb; + dest[2][0] = (right + left) * rl; + dest[2][1] = (top + bottom) * tb; + dest[2][2] = (farZ + nearZ) * fn; + dest[2][3] =-1.0f; + dest[3][2] = farZ * nv * fn; +} + +/*! + * @brief set up perspective projection matrix + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] fovy field of view angle + * @param[in] aspect aspect ratio ( width / height ) + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping planes + * @param[out] dest result matrix + */ +f_inline +void +glm_perspective_rh_no(float fovy, + float aspect, + float nearZ, + float farZ, + mat4 dest) { + float f, fn; + + glm_mat4_zero(dest); + + f = 1.0f / tanf(fovy * 0.5f); + fn = 1.0f / (nearZ - farZ); + + dest[0][0] = f / aspect; + dest[1][1] = f; + dest[2][2] = (nearZ + farZ) * fn; + dest[2][3] =-1.0f; + dest[3][2] = 2.0f * nearZ * farZ * fn; + +} + +/*! + * @brief set up perspective projection matrix with default near/far + * and angle values with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[out] dest result matrix + */ +f_inline +void +glm_perspective_default_rh_no(float aspect, mat4 dest) { + glm_perspective_rh_no(GLM_PI_4f, aspect, 0.01f, 100.0f, dest); +} + +/*! + * @brief resize perspective matrix by aspect ratio ( width / height ) + * this makes very easy to resize proj matrix when window /viewport + * resized with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[in, out] proj perspective projection matrix + */ +f_inline +void +glm_perspective_resize_rh_no(float aspect, mat4 proj) { + if (proj[0][0] == 0.0f) + return; + + proj[0][0] = proj[1][1] / aspect; +} + +/*! + * @brief extend perspective projection matrix's far distance + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * this function does not guarantee far >= near, be aware of that! + * + * @param[in, out] proj projection matrix to extend + * @param[in] deltaFar distance from existing far (negative to shink) + */ +f_inline +void +glm_persp_move_far_rh_no(mat4 proj, float deltaFar) { + float fn, farZ, nearZ, p22, p32; + + p22 = proj[2][2]; + p32 = proj[3][2]; + + nearZ = p32 / (p22 - 1.0f); + farZ = p32 / (p22 + 1.0f) + deltaFar; + fn = 1.0f / (nearZ - farZ); + + proj[2][2] = (farZ + nearZ) * fn; + proj[3][2] = 2.0f * nearZ * farZ * fn; +} + +/*! + * @brief decomposes frustum values of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + * @param[out] farZ far + * @param[out] top top + * @param[out] bottom bottom + * @param[out] left left + * @param[out] right right + */ +f_inline +void +glm_persp_decomp_rh_no(mat4 proj, + float * __restrict nearZ, float * __restrict farZ, + float * __restrict top, float * __restrict bottom, + float * __restrict left, float * __restrict right) { + float m00, m11, m20, m21, m22, m32, n, f; + float n_m11, n_m00; + + m00 = proj[0][0]; + m11 = proj[1][1]; + m20 = proj[2][0]; + m21 = proj[2][1]; + m22 = proj[2][2]; + m32 = proj[3][2]; + + n = m32 / (m22 - 1.0f); + f = m32 / (m22 + 1.0f); + + n_m11 = n / m11; + n_m00 = n / m00; + + *nearZ = n; + *farZ = f; + *bottom = n_m11 * (m21 - 1.0f); + *top = n_m11 * (m21 + 1.0f); + *left = n_m00 * (m20 - 1.0f); + *right = n_m00 * (m20 + 1.0f); +} + +/*! + * @brief decomposes frustum values of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * this makes easy to get all values at once + * + * @param[in] proj perspective projection matrix + * @param[out] dest array + */ +f_inline +void +glm_persp_decompv_rh_no(mat4 proj, float dest[6]) { + glm_persp_decomp_rh_no(proj, &dest[0], &dest[1], &dest[2], + &dest[3], &dest[4], &dest[5]); +} + +/*! + * @brief decomposes left and right values of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * x stands for x axis (left / right axis) + * + * @param[in] proj perspective projection matrix + * @param[out] left left + * @param[out] right right + */ +f_inline +void +glm_persp_decomp_x_rh_no(mat4 proj, + float * __restrict left, + float * __restrict right) { + float nearZ, m20, m00, m22; + + m00 = proj[0][0]; + m20 = proj[2][0]; + m22 = proj[2][2]; + + nearZ = proj[3][2] / (m22 - 1.0f); + *left = nearZ * (m20 - 1.0f) / m00; + *right = nearZ * (m20 + 1.0f) / m00; +} + +/*! + * @brief decomposes top and bottom values of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * y stands for y axis (top / botom axis) + * + * @param[in] proj perspective projection matrix + * @param[out] top top + * @param[out] bottom bottom + */ +f_inline +void +glm_persp_decomp_y_rh_no(mat4 proj, + float * __restrict top, + float * __restrict bottom) { + float nearZ, m21, m11, m22; + + m21 = proj[2][1]; + m11 = proj[1][1]; + m22 = proj[2][2]; + + nearZ = proj[3][2] / (m22 - 1.0f); + *bottom = nearZ * (m21 - 1.0f) / m11; + *top = nearZ * (m21 + 1.0f) / m11; +} + +/*! + * @brief decomposes near and far values of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * z stands for z axis (near / far axis) + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + * @param[out] farZ far + */ +f_inline +void +glm_persp_decomp_z_rh_no(mat4 proj, + float * __restrict nearZ, + float * __restrict farZ) { + float m32, m22; + + m32 = proj[3][2]; + m22 = proj[2][2]; + + *nearZ = m32 / (m22 - 1.0f); + *farZ = m32 / (m22 + 1.0f); +} + +/*! + * @brief decomposes far value of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] proj perspective projection matrix + * @param[out] farZ far + */ +f_inline +void +glm_persp_decomp_far_rh_no(mat4 proj, float * __restrict farZ) { + *farZ = proj[3][2] / (proj[2][2] + 1.0f); +} + +/*! + * @brief decomposes near value of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + */ +f_inline +void +glm_persp_decomp_near_rh_no(mat4 proj, float * __restrict nearZ) { + *nearZ = proj[3][2] / (proj[2][2] - 1.0f); +} + +/*! + * @brief returns sizes of near and far planes of perspective projection + * with a right-hand coordinate system and a + * clip-space of [-1, 1]. + * + * @param[in] proj perspective projection matrix + * @param[in] fovy fovy (see brief) + * @param[out] dest sizes order: [Wnear, Hnear, Wfar, Hfar] + */ +f_inline +void +glm_persp_sizes_rh_no(mat4 proj, float fovy, vec4 dest) { + float t, a, nearZ, farZ; + + t = 2.0f * tanf(fovy * 0.5f); + a = glm_persp_aspect(proj); + + glm_persp_decomp_z_rh_no(proj, &nearZ, &farZ); + + dest[1] = t * nearZ; + dest[3] = t * farZ; + dest[0] = a * dest[1]; + dest[2] = a * dest[3]; +} + +/*! + * @brief returns field of view angle along the Y-axis (in radians) + * with a right-hand coordinate system and a clip-space of [-1, 1]. + * + * if you need to degrees, use glm_deg to convert it or use this: + * fovy_deg = glm_deg(glm_persp_fovy(projMatrix)) + * + * @param[in] proj perspective projection matrix + */ +f_inline +float +glm_persp_fovy_rh_no(mat4 proj) { + return glm_persp_fovy(proj); +} + +/*! + * @brief returns aspect ratio of perspective projection + * with a right-hand coordinate system and a clip-space of [-1, 1]. + * + * @param[in] proj perspective projection matrix + */ +f_inline +float +glm_persp_aspect_rh_no(mat4 proj) { + return glm_persp_aspect(proj); +} + + + + + + + + + + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] center center vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_lookat_rh(vec3 eye, vec3 center, vec3 up, mat4 dest) { + f_align(8) vec3 f, u, s; + + glm_vec3_sub(center, eye, f); + glm_vec3_normalize(f); + + glm_vec3_crossn(f, up, s); + glm_vec3_cross(s, f, u); + + dest[0][0] = s[0]; + dest[0][1] = u[0]; + dest[0][2] =-f[0]; + dest[1][0] = s[1]; + dest[1][1] = u[1]; + dest[1][2] =-f[1]; + dest[2][0] = s[2]; + dest[2][1] = u[2]; + dest[2][2] =-f[2]; + dest[3][0] =-glm_vec3_dot(s, eye); + dest[3][1] =-glm_vec3_dot(u, eye); + dest[3][2] = glm_vec3_dot(f, eye); + dest[0][3] = dest[1][3] = dest[2][3] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * convenient wrapper for lookat: if you only have direction not target self + * then this might be useful. Because you need to get target from direction. + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look_rh(vec3 eye, vec3 dir, vec3 up, mat4 dest) { + f_align(8) vec3 target; + glm_vec3_add(eye, dir, target); + glm_lookat_rh(eye, target, up, dest); +} + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * convenient wrapper for look: if you only have direction and if you don't + * care what UP vector is then this might be useful to create view matrix + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look_anyup_rh(vec3 eye, vec3 dir, mat4 dest) { + f_align(8) vec3 up; + glm_vec3_ortho(dir, up); + glm_look_rh(eye, dir, up, dest); +} + + + + + + + + + + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] center center vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_lookat_rh_no(vec3 eye, vec3 center, vec3 up, mat4 dest) { + glm_lookat_rh(eye, center, up, dest); +} + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * convenient wrapper for lookat: if you only have direction not target self + * then this might be useful. Because you need to get target from direction. + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look_rh_no(vec3 eye, vec3 dir, vec3 up, mat4 dest) { + glm_look_rh(eye, dir, up, dest); +} + +/*! + * @brief set up view matrix with right handed coordinate system. + * + * convenient wrapper for look: if you only have direction and if you don't + * care what UP vector is then this might be useful to create view matrix + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look_anyup_rh_no(vec3 eye, vec3 dir, mat4 dest) { + glm_look_anyup_rh(eye, dir, dest); +} + + + + + + + + + + +/*! + * @brief set up perspective peprojection matrix + * + * @param[in] left viewport.left + * @param[in] right viewport.right + * @param[in] bottom viewport.bottom + * @param[in] top viewport.top + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping plane + * @param[out] dest result matrix + */ +f_inline +void +glm_frustum(float left, float right, + float bottom, float top, + float nearZ, float farZ, + mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_frustum_lh_zo(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_frustum_lh_no(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_frustum_rh_zo(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_frustum_rh_no(left, right, bottom, top, nearZ, farZ, dest); +#endif +} + +/*! + * @brief set up orthographic projection matrix + * + * @param[in] left viewport.left + * @param[in] right viewport.right + * @param[in] bottom viewport.bottom + * @param[in] top viewport.top + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping plane + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho(float left, float right, + float bottom, float top, + float nearZ, float farZ, + mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_lh_zo(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_lh_no(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_rh_zo(left, right, bottom, top, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_rh_no(left, right, bottom, top, nearZ, farZ, dest); +#endif +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb(vec3 box[2], mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_aabb_lh_zo(box, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_aabb_lh_no(box, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_aabb_rh_zo(box, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_aabb_rh_no(box, dest); +#endif +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[in] padding padding + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb_p(vec3 box[2], float padding, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_aabb_p_lh_zo(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_aabb_p_lh_no(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_aabb_p_rh_zo(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_aabb_p_rh_no(box, padding, dest); +#endif +} + +/*! + * @brief set up orthographic projection matrix using bounding box + * + * bounding box (AABB) must be in view space + * + * @param[in] box AABB + * @param[in] padding padding for near and far + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_aabb_pz(vec3 box[2], float padding, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_aabb_pz_lh_zo(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_aabb_pz_lh_no(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_aabb_pz_rh_zo(box, padding, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_aabb_pz_rh_no(box, padding, dest); +#endif +} + +/*! + * @brief set up unit orthographic projection matrix + * + * @param[in] aspect aspect ration ( width / height ) + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_default(float aspect, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_default_lh_zo(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_default_lh_no(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_default_rh_zo(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_default_rh_no(aspect, dest); +#endif +} + +/*! + * @brief set up orthographic projection matrix with given CUBE size + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[in] size cube size + * @param[out] dest result matrix + */ +f_inline +void +glm_ortho_default_s(float aspect, float size, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_ortho_default_s_lh_zo(aspect, size, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_ortho_default_s_lh_no(aspect, size, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_ortho_default_s_rh_zo(aspect, size, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_ortho_default_s_rh_no(aspect, size, dest); +#endif +} + +/*! + * @brief set up perspective projection matrix + * + * @param[in] fovy field of view angle + * @param[in] aspect aspect ratio ( width / height ) + * @param[in] nearZ near clipping plane + * @param[in] farZ far clipping planes + * @param[out] dest result matrix + */ +f_inline +void +glm_perspective(float fovy, float aspect, float nearZ, float farZ, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_perspective_lh_zo(fovy, aspect, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_perspective_lh_no(fovy, aspect, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_perspective_rh_zo(fovy, aspect, nearZ, farZ, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_perspective_rh_no(fovy, aspect, nearZ, farZ, dest); +#endif +} + +/*! + * @brief extend perspective projection matrix's far distance + * + * this function does not guarantee far >= near, be aware of that! + * + * @param[in, out] proj projection matrix to extend + * @param[in] deltaFar distance from existing far (negative to shink) + */ +f_inline +void +glm_persp_move_far(mat4 proj, float deltaFar) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_move_far_lh_zo(proj, deltaFar); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_move_far_lh_no(proj, deltaFar); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_move_far_rh_zo(proj, deltaFar); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_move_far_rh_no(proj, deltaFar); +#endif +} + +/*! + * @brief set up perspective projection matrix with default near/far + * and angle values + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[out] dest result matrix + */ +f_inline +void +glm_perspective_default(float aspect, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_perspective_default_lh_zo(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_perspective_default_lh_no(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_perspective_default_rh_zo(aspect, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_perspective_default_rh_no(aspect, dest); +#endif +} + +/*! + * @brief resize perspective matrix by aspect ratio ( width / height ) + * this makes very easy to resize proj matrix when window /viewport + * reized + * + * @param[in] aspect aspect ratio ( width / height ) + * @param[in, out] proj perspective projection matrix + */ +f_inline +void +glm_perspective_resize(float aspect, mat4 proj) { + if (proj[0][0] == 0.0f) + return; + + proj[0][0] = proj[1][1] / aspect; +} + +/*! + * @brief set up view matrix + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] center center vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_lookat(vec3 eye, vec3 center, vec3 up, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_LH_BIT + glm_lookat_lh(eye, center, up, dest); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_RH_BIT + glm_lookat_rh(eye, center, up, dest); +#endif +} + +/*! + * @brief set up view matrix + * + * convenient wrapper for lookat: if you only have direction not target self + * then this might be useful. Because you need to get target from direction. + * + * NOTE: The UP vector must not be parallel to the line of sight from + * the eye point to the reference point + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[in] up up vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look(vec3 eye, vec3 dir, vec3 up, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_LH_BIT + glm_look_lh(eye, dir, up, dest); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_RH_BIT + glm_look_rh(eye, dir, up, dest); +#endif +} + +/*! + * @brief set up view matrix + * + * convenient wrapper for look: if you only have direction and if you don't + * care what UP vector is then this might be useful to create view matrix + * + * @param[in] eye eye vector + * @param[in] dir direction vector + * @param[out] dest result matrix + */ +f_inline +void +glm_look_anyup(vec3 eye, vec3 dir, mat4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_LH_BIT + glm_look_anyup_lh(eye, dir, dest); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_RH_BIT + glm_look_anyup_rh(eye, dir, dest); +#endif +} + +/*! + * @brief decomposes frustum values of perspective projection. + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + * @param[out] farZ far + * @param[out] top top + * @param[out] bottom bottom + * @param[out] left left + * @param[out] right right + */ +f_inline +void +glm_persp_decomp(mat4 proj, + float * __restrict nearZ, float * __restrict farZ, + float * __restrict top, float * __restrict bottom, + float * __restrict left, float * __restrict right) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_lh_zo(proj, nearZ, farZ, top, bottom, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_lh_no(proj, nearZ, farZ, top, bottom, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_rh_zo(proj, nearZ, farZ, top, bottom, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_rh_no(proj, nearZ, farZ, top, bottom, left, right); +#endif +} + +/*! + * @brief decomposes frustum values of perspective projection. + * this makes easy to get all values at once + * + * @param[in] proj perspective projection matrix + * @param[out] dest array + */ +f_inline +void +glm_persp_decompv(mat4 proj, float dest[6]) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decompv_lh_zo(proj, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decompv_lh_no(proj, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decompv_rh_zo(proj, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decompv_rh_no(proj, dest); +#endif +} + +/*! + * @brief decomposes left and right values of perspective projection. + * x stands for x axis (left / right axis) + * + * @param[in] proj perspective projection matrix + * @param[out] left left + * @param[out] right right + */ +f_inline +void +glm_persp_decomp_x(mat4 proj, + float * __restrict left, + float * __restrict right) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_x_lh_zo(proj, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_x_lh_no(proj, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_x_rh_zo(proj, left, right); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_x_rh_no(proj, left, right); +#endif +} + +/*! + * @brief decomposes top and bottom values of perspective projection. + * y stands for y axis (top / botom axis) + * + * @param[in] proj perspective projection matrix + * @param[out] top top + * @param[out] bottom bottom + */ +f_inline +void +glm_persp_decomp_y(mat4 proj, + float * __restrict top, + float * __restrict bottom) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_y_lh_zo(proj, top, bottom); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_y_lh_no(proj, top, bottom); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_y_rh_zo(proj, top, bottom); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_y_rh_no(proj, top, bottom); +#endif +} + +/*! + * @brief decomposes near and far values of perspective projection. + * z stands for z axis (near / far axis) + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + * @param[out] farZ far + */ +f_inline +void +glm_persp_decomp_z(mat4 proj, float * __restrict nearZ, float * __restrict farZ) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_z_lh_zo(proj, nearZ, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_z_lh_no(proj, nearZ, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_z_rh_zo(proj, nearZ, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_z_rh_no(proj, nearZ, farZ); +#endif +} + +/*! + * @brief decomposes far value of perspective projection. + * + * @param[in] proj perspective projection matrix + * @param[out] farZ far + */ +f_inline +void +glm_persp_decomp_far(mat4 proj, float * __restrict farZ) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_far_lh_zo(proj, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_far_lh_no(proj, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_far_rh_zo(proj, farZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_far_rh_no(proj, farZ); +#endif +} + +/*! + * @brief decomposes near value of perspective projection. + * + * @param[in] proj perspective projection matrix + * @param[out] nearZ near + */ +f_inline +void +glm_persp_decomp_near(mat4 proj, float * __restrict nearZ) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_decomp_near_lh_zo(proj, nearZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_decomp_near_lh_no(proj, nearZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_decomp_near_rh_zo(proj, nearZ); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_decomp_near_rh_no(proj, nearZ); +#endif +} + +/*! + * @brief returns sizes of near and far planes of perspective projection + * + * @param[in] proj perspective projection matrix + * @param[in] fovy fovy (see brief) + * @param[out] dest sizes order: [Wnear, Hnear, Wfar, Hfar] + */ +f_inline +void +glm_persp_sizes(mat4 proj, float fovy, vec4 dest) { +#if CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_ZO + glm_persp_sizes_lh_zo(proj, fovy, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_LH_NO + glm_persp_sizes_lh_no(proj, fovy, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_ZO + glm_persp_sizes_rh_zo(proj, fovy, dest); +#elif CGLM_CONFIG_CLIP_CONTROL == CGLM_CLIP_CONTROL_RH_NO + glm_persp_sizes_rh_no(proj, fovy, dest); +#endif +} + + + + + + + + + + +#define GLM_LBN 0 /* left bottom near */ +#define GLM_LTN 1 /* left top near */ +#define GLM_RTN 2 /* right top near */ +#define GLM_RBN 3 /* right bottom near */ + +#define GLM_LBF 4 /* left bottom far */ +#define GLM_LTF 5 /* left top far */ +#define GLM_RTF 6 /* right top far */ +#define GLM_RBF 7 /* right bottom far */ + +#define GLM_LEFT 0 +#define GLM_RIGHT 1 +#define GLM_BOTTOM 2 +#define GLM_TOP 3 +#define GLM_NEAR 4 +#define GLM_FAR 5 + +/* you can override clip space coords + but you have to provide all with same name + e.g.: define GLM_CSCOORD_LBN {0.0f, 0.0f, 1.0f, 1.0f} */ +#ifndef GLM_CUSTOM_CLIPSPACE + +/* near */ +#define GLM_CSCOORD_LBN {-1.0f, -1.0f, -1.0f, 1.0f} +#define GLM_CSCOORD_LTN {-1.0f, 1.0f, -1.0f, 1.0f} +#define GLM_CSCOORD_RTN { 1.0f, 1.0f, -1.0f, 1.0f} +#define GLM_CSCOORD_RBN { 1.0f, -1.0f, -1.0f, 1.0f} + +/* far */ +#define GLM_CSCOORD_LBF {-1.0f, -1.0f, 1.0f, 1.0f} +#define GLM_CSCOORD_LTF {-1.0f, 1.0f, 1.0f, 1.0f} +#define GLM_CSCOORD_RTF { 1.0f, 1.0f, 1.0f, 1.0f} +#define GLM_CSCOORD_RBF { 1.0f, -1.0f, 1.0f, 1.0f} + +#endif + +/*! + * @brief extracts view frustum planes + * + * planes' space: + * 1- if m = proj: View Space + * 2- if m = viewProj: World Space + * 3- if m = MVP: Object Space + * + * You probably want to extract planes in world space so use viewProj as m + * Computing viewProj: + * glm_mat4_mul(proj, view, viewProj); + * + * Exracted planes order: [left, right, bottom, top, near, far] + * + * @param[in] m matrix (see brief) + * @param[out] dest extracted view frustum planes (see brief) + */ +f_inline +void +glm_frustum_planes(mat4 m, vec4 dest[6]) { + mat4 t; + + glm_mat4_transpose_to(m, t); + + glm_vec4_add(t[3], t[0], dest[0]); /* left */ + glm_vec4_sub(t[3], t[0], dest[1]); /* right */ + glm_vec4_add(t[3], t[1], dest[2]); /* bottom */ + glm_vec4_sub(t[3], t[1], dest[3]); /* top */ + glm_vec4_add(t[3], t[2], dest[4]); /* near */ + glm_vec4_sub(t[3], t[2], dest[5]); /* far */ + + glm_plane_normalize(dest[0]); + glm_plane_normalize(dest[1]); + glm_plane_normalize(dest[2]); + glm_plane_normalize(dest[3]); + glm_plane_normalize(dest[4]); + glm_plane_normalize(dest[5]); +} + +/*! + * @brief extracts view frustum corners using clip-space coordinates + * + * corners' space: + * 1- if m = invViewProj: World Space + * 2- if m = invMVP: Object Space + * + * You probably want to extract corners in world space so use invViewProj + * Computing invViewProj: + * glm_mat4_mul(proj, view, viewProj); + * ... + * glm_mat4_inv(viewProj, invViewProj); + * + * if you have a near coord at i index, you can get it's far coord by i + 4 + * + * Find center coordinates: + * for (j = 0; j < 4; j++) { + * glm_vec3_center(corners[i], corners[i + 4], centerCorners[i]); + * } + * + * @param[in] invMat matrix (see brief) + * @param[out] dest exracted view frustum corners (see brief) + */ +f_inline +void +glm_frustum_corners(mat4 invMat, vec4 dest[8]) { + vec4 c[8]; + + /* indexOf(nearCoord) = indexOf(farCoord) + 4 */ + vec4 csCoords[8] = { + GLM_CSCOORD_LBN, + GLM_CSCOORD_LTN, + GLM_CSCOORD_RTN, + GLM_CSCOORD_RBN, + + GLM_CSCOORD_LBF, + GLM_CSCOORD_LTF, + GLM_CSCOORD_RTF, + GLM_CSCOORD_RBF + }; + + glm_mat4_mulv(invMat, csCoords[0], c[0]); + glm_mat4_mulv(invMat, csCoords[1], c[1]); + glm_mat4_mulv(invMat, csCoords[2], c[2]); + glm_mat4_mulv(invMat, csCoords[3], c[3]); + glm_mat4_mulv(invMat, csCoords[4], c[4]); + glm_mat4_mulv(invMat, csCoords[5], c[5]); + glm_mat4_mulv(invMat, csCoords[6], c[6]); + glm_mat4_mulv(invMat, csCoords[7], c[7]); + + glm_vec4_scale(c[0], 1.0f / c[0][3], dest[0]); + glm_vec4_scale(c[1], 1.0f / c[1][3], dest[1]); + glm_vec4_scale(c[2], 1.0f / c[2][3], dest[2]); + glm_vec4_scale(c[3], 1.0f / c[3][3], dest[3]); + glm_vec4_scale(c[4], 1.0f / c[4][3], dest[4]); + glm_vec4_scale(c[5], 1.0f / c[5][3], dest[5]); + glm_vec4_scale(c[6], 1.0f / c[6][3], dest[6]); + glm_vec4_scale(c[7], 1.0f / c[7][3], dest[7]); +} + +/*! + * @brief finds center of view frustum + * + * @param[in] corners view frustum corners + * @param[out] dest view frustum center + */ +f_inline +void +glm_frustum_center(vec4 corners[8], vec4 dest) { + vec4 center; + + glm_vec4_copy(corners[0], center); + + glm_vec4_add(corners[1], center, center); + glm_vec4_add(corners[2], center, center); + glm_vec4_add(corners[3], center, center); + glm_vec4_add(corners[4], center, center); + glm_vec4_add(corners[5], center, center); + glm_vec4_add(corners[6], center, center); + glm_vec4_add(corners[7], center, center); + + glm_vec4_scale(center, 0.125f, dest); +} + +/*! + * @brief finds bounding box of frustum relative to given matrix e.g. view mat + * + * @param[in] corners view frustum corners + * @param[in] m matrix to convert existing conners + * @param[out] box bounding box as array [min, max] + */ +f_inline +void +glm_frustum_box(vec4 corners[8], mat4 m, vec3 box[2]) { + vec4 v; + vec3 min, max; + int i; + + glm_vec3_broadcast(FLT_MAX, min); + glm_vec3_broadcast(-FLT_MAX, max); + + for (i = 0; i < 8; i++) { + glm_mat4_mulv(m, corners[i], v); + + min[0] = glm_min(min[0], v[0]); + min[1] = glm_min(min[1], v[1]); + min[2] = glm_min(min[2], v[2]); + + max[0] = glm_max(max[0], v[0]); + max[1] = glm_max(max[1], v[1]); + max[2] = glm_max(max[2], v[2]); + } + + glm_vec3_copy(min, box[0]); + glm_vec3_copy(max, box[1]); +} + +/*! + * @brief finds planes corners which is between near and far planes (parallel) + * + * this will be helpful if you want to split a frustum e.g. CSM/PSSM. This will + * find planes' corners but you will need to one more plane. + * Actually you have it, it is near, far or created previously with this func ;) + * + * @param[in] corners view frustum corners + * @param[in] splitDist split distance + * @param[in] farDist far distance (zFar) + * @param[out] planeCorners plane corners [LB, LT, RT, RB] + */ +f_inline +void +glm_frustum_corners_at(vec4 corners[8], + float splitDist, + float farDist, + vec4 planeCorners[4]) { + vec4 corner; + float dist, sc; + + /* because distance and scale is same for all */ + dist = glm_vec3_distance(corners[GLM_RTF], corners[GLM_RTN]); + sc = dist * (splitDist / farDist); + + /* left bottom */ + glm_vec4_sub(corners[GLM_LBF], corners[GLM_LBN], corner); + glm_vec4_scale_as(corner, sc, corner); + glm_vec4_add(corners[GLM_LBN], corner, planeCorners[0]); + + /* left top */ + glm_vec4_sub(corners[GLM_LTF], corners[GLM_LTN], corner); + glm_vec4_scale_as(corner, sc, corner); + glm_vec4_add(corners[GLM_LTN], corner, planeCorners[1]); + + /* right top */ + glm_vec4_sub(corners[GLM_RTF], corners[GLM_RTN], corner); + glm_vec4_scale_as(corner, sc, corner); + glm_vec4_add(corners[GLM_RTN], corner, planeCorners[2]); + + /* right bottom */ + glm_vec4_sub(corners[GLM_RBF], corners[GLM_RBN], corner); + glm_vec4_scale_as(corner, sc, corner); + glm_vec4_add(corners[GLM_RBN], corner, planeCorners[3]); +} + + + + + + + + + + + + +#ifdef CGLM_SIMD +#if defined( __SSE__ ) || defined( __SSE2__ ) + +f_inline +void +glm_quat_mul_sse2(vec4 p, vec4 q, vec4 dest) { + /* + + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i + + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j + + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k + a1 a2 − b1 b2 − c1 c2 − d1 d2 + */ + + __m128 xp, xq, x1, x2, x3, r, x, y, z; + + xp = glmm_load(p); /* 3 2 1 0 */ + xq = glmm_load(q); + x1 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); /* TODO: _mm_set1_ss() + shuff ? */ + r = _mm_mul_ps(glmm_splat_w(xp), xq); + + x2 = _mm_unpackhi_ps(x1, x1); + x3 = glmm_shuff1(x1, 3, 2, 0, 1); + x = glmm_splat_x(xp); + y = glmm_splat_y(xp); + z = glmm_splat_z(xp); + + x = _mm_xor_ps(x, x1); + y = _mm_xor_ps(y, x2); + z = _mm_xor_ps(z, x3); + + x1 = glmm_shuff1(xq, 0, 1, 2, 3); + x2 = glmm_shuff1(xq, 1, 0, 3, 2); + x3 = glmm_shuff1(xq, 2, 3, 0, 1); + + r = glmm_fmadd(x, x1, r); + r = glmm_fmadd(y, x2, r); + r = glmm_fmadd(z, x3, r); + + glmm_store(dest, r); +} + +#endif +#endif + +f_inline void glm_quat_normalize(vec4 q); + +/* + * IMPORTANT: + * ---------------------------------------------------------------------------- + * cglm stores quat as [x, y, z, w] since v0.3.6 + * + * it was [w, x, y, z] before v0.3.6 it has been changed to [x, y, z, w] + * with v0.3.6 version. + * ---------------------------------------------------------------------------- + */ + +#define GLM_QUAT_IDENTITY_INIT {0.0f, 0.0f, 0.0f, 1.0f} +#define GLM_QUAT_IDENTITY ((vec4)GLM_QUAT_IDENTITY_INIT) + +/*! + * @brief makes given quat to identity + * + * @param[in, out] q quaternion + */ +f_inline +void +glm_quat_identity(vec4 q) { + f_align(16) vec4 v = GLM_QUAT_IDENTITY_INIT; + glm_vec4_copy(v, q); +} + +/*! + * @brief make given quaternion array's each element identity quaternion + * + * @param[in, out] q quat array (must be aligned (16) + * if alignment is not disabled) + * + * @param[in] count count of quaternions + */ +f_inline +void +glm_quat_identity_array(vec4 * __restrict q, uint count) { + f_align(16) vec4 v = GLM_QUAT_IDENTITY_INIT; + uint i; + + for (i = 0; i < count; i++) { + glm_vec4_copy(v, q[i]); + } +} + +/*! + * @brief inits quaterion with raw values + * + * @param[out] q quaternion + * @param[in] x x + * @param[in] y y + * @param[in] z z + * @param[in] w w (real part) + */ +f_inline +void +glm_quat_init(vec4 q, float x, float y, float z, float w) { + q[0] = x; + q[1] = y; + q[2] = z; + q[3] = w; +} + +/*! + * @brief creates NEW quaternion with axis vector + * + * @param[out] q quaternion + * @param[in] angle angle (radians) + * @param[in] axis axis + */ +f_inline +void +glm_quatv(vec4 q, float angle, vec3 axis) { + f_align(8) vec3 k; + float a, c, s; + + a = angle * 0.5f; + c = cosf(a); + s = sinf(a); + + glm_normalize_to(axis, k); + + q[0] = s * k[0]; + q[1] = s * k[1]; + q[2] = s * k[2]; + q[3] = c; +} + +/*! + * @brief creates NEW quaternion with individual axis components + * + * @param[out] q quaternion + * @param[in] angle angle (radians) + * @param[in] x axis.x + * @param[in] y axis.y + * @param[in] z axis.z + */ +f_inline +void +glm_quat(vec4 q, float angle, float x, float y, float z) { + f_align(8) vec3 axis = {x, y, z}; + glm_quatv(q, angle, axis); +} + +/*! + * @brief copy quaternion to another one + * + * @param[in] q quaternion + * @param[out] dest destination + */ +f_inline +void +glm_quat_copy(vec4 q, vec4 dest) { + glm_vec4_copy(q, dest); +} + +/*! + * @brief compute quaternion rotating vector A to vector B + * + * @param[in] a vec3 (must have unit length) + * @param[in] b vec3 (must have unit length) + * @param[out] dest quaternion (of unit length) + */ +f_inline +void +glm_quat_from_vecs(vec3 a, vec3 b, vec4 dest) { + f_align(8) vec3 axis; + float cos_theta; + float cos_half_theta; + + cos_theta = glm_vec3_dot(a, b); + if (cos_theta >= 1.f - GLM_FLT_EPSILON) { /* a ∥ b */ + glm_quat_identity(dest); + return; + } + if (cos_theta < -1.f + GLM_FLT_EPSILON) { /* angle(a, b) = π */ + glm_vec3_ortho(a, axis); + cos_half_theta = 0.f; /* cos π/2 */ + } else { + glm_vec3_cross(a, b, axis); + cos_half_theta = 1.0f + cos_theta; /* cos 0 + cos θ */ + } + + glm_quat_init(dest, axis[0], axis[1], axis[2], cos_half_theta); + glm_quat_normalize(dest); +} + +/*! + * @brief returns norm (magnitude) of quaternion + * + * @param[in] q quaternion + */ +f_inline +float +glm_quat_norm(vec4 q) { + return glm_vec4_norm(q); +} + +/*! + * @brief normalize quaternion and store result in dest + * + * @param[in] q quaternion to normalze + * @param[out] dest destination quaternion + */ +f_inline +void +glm_quat_normalize_to(vec4 q, vec4 dest) { +#if defined( __SSE2__ ) || defined( __SSE2__ ) + __m128 xdot, x0; + float dot; + + x0 = glmm_load(q); + xdot = glmm_vdot(x0, x0); + dot = _mm_cvtss_f32(xdot); + + if (dot <= 0.0f) { + glm_quat_identity(dest); + return; + } + + glmm_store(dest, _mm_div_ps(x0, _mm_sqrt_ps(xdot))); +#else + float dot; + + dot = glm_vec4_norm2(q); + + if (dot <= 0.0f) { + glm_quat_identity(dest); + return; + } + + glm_vec4_scale(q, 1.0f / sqrtf(dot), dest); +#endif +} + +/*! + * @brief normalize quaternion + * + * @param[in, out] q quaternion + */ +f_inline +void +glm_quat_normalize(vec4 q) { + glm_quat_normalize_to(q, q); +} + +/*! + * @brief dot product of two quaternion + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + */ +f_inline +float +glm_quat_dot(vec4 p, vec4 q) { + return glm_vec4_dot(p, q); +} + +/*! + * @brief conjugate of quaternion + * + * @param[in] q quaternion + * @param[out] dest conjugate + */ +f_inline +void +glm_quat_conjugate(vec4 q, vec4 dest) { + glm_vec4_negate_to(q, dest); + dest[3] = -dest[3]; +} + +/*! + * @brief inverse of non-zero quaternion + * + * @param[in] q quaternion + * @param[out] dest inverse quaternion + */ +f_inline +void +glm_quat_inv(vec4 q, vec4 dest) { + f_align(16) vec4 conj; + glm_quat_conjugate(q, conj); + glm_vec4_scale(conj, 1.0f / glm_vec4_norm2(q), dest); +} + +/*! + * @brief add (componentwise) two quaternions and store result in dest + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_add(vec4 p, vec4 q, vec4 dest) { + glm_vec4_add(p, q, dest); +} + +/*! + * @brief subtract (componentwise) two quaternions and store result in dest + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_sub(vec4 p, vec4 q, vec4 dest) { + glm_vec4_sub(p, q, dest); +} + +/*! + * @brief returns real part of quaternion + * + * @param[in] q quaternion + */ +f_inline +float +glm_quat_real(vec4 q) { + return q[3]; +} + +/*! + * @brief returns imaginary part of quaternion + * + * @param[in] q quaternion + * @param[out] dest imag + */ +f_inline +void +glm_quat_imag(vec4 q, vec3 dest) { + dest[0] = q[0]; + dest[1] = q[1]; + dest[2] = q[2]; +} + +/*! + * @brief returns normalized imaginary part of quaternion + * + * @param[in] q quaternion + */ +f_inline +void +glm_quat_imagn(vec4 q, vec3 dest) { + glm_normalize_to(q, dest); +} + +/*! + * @brief returns length of imaginary part of quaternion + * + * @param[in] q quaternion + */ +f_inline +float +glm_quat_imaglen(vec4 q) { + return glm_vec3_norm(q); +} + +/*! + * @brief returns angle of quaternion + * + * @param[in] q quaternion + */ +f_inline +float +glm_quat_angle(vec4 q) { + /* + sin(theta / 2) = length(x*x + y*y + z*z) + cos(theta / 2) = w + theta = 2 * atan(sin(theta / 2) / cos(theta / 2)) + */ + return 2.0f * atan2f(glm_quat_imaglen(q), glm_quat_real(q)); +} + +/*! + * @brief axis of quaternion + * + * @param[in] q quaternion + * @param[out] dest axis of quaternion + */ +f_inline +void +glm_quat_axis(vec4 q, vec3 dest) { + glm_quat_imagn(q, dest); +} + +/*! + * @brief multiplies two quaternion and stores result in dest + * this is also called Hamilton Product + * + * According to WikiPedia: + * The product of two rotation quaternions [clarification needed] will be + * equivalent to the rotation q followed by the rotation p + * + * @param[in] p quaternion 1 + * @param[in] q quaternion 2 + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_mul(vec4 p, vec4 q, vec4 dest) { + /* + + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i + + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j + + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k + a1 a2 − b1 b2 − c1 c2 − d1 d2 + */ +#if defined( __SSE__ ) || defined( __SSE2__ ) + glm_quat_mul_sse2(p, q, dest); +#elif defined(CGLM_NEON_FP) + glm_quat_mul_neon(p, q, dest); +#else + dest[0] = p[3] * q[0] + p[0] * q[3] + p[1] * q[2] - p[2] * q[1]; + dest[1] = p[3] * q[1] - p[0] * q[2] + p[1] * q[3] + p[2] * q[0]; + dest[2] = p[3] * q[2] + p[0] * q[1] - p[1] * q[0] + p[2] * q[3]; + dest[3] = p[3] * q[3] - p[0] * q[0] - p[1] * q[1] - p[2] * q[2]; +#endif +} + +/*! + * @brief convert quaternion to mat4 + * + * @param[in] q quaternion + * @param[out] dest result matrix + */ +f_inline +void +glm_quat_mat4(vec4 q, mat4 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[0][1] = xy + wz; + dest[1][2] = yz + wx; + dest[2][0] = xz + wy; + + dest[1][0] = xy - wz; + dest[2][1] = yz - wx; + dest[0][2] = xz - wy; + + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief convert quaternion to mat4 (transposed) + * + * @param[in] q quaternion + * @param[out] dest result matrix as transposed + */ +f_inline +void +glm_quat_mat4t(vec4 q, mat4 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[1][0] = xy + wz; + dest[2][1] = yz + wx; + dest[0][2] = xz + wy; + + dest[0][1] = xy - wz; + dest[1][2] = yz - wx; + dest[2][0] = xz - wy; + + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief convert quaternion to mat3 + * + * @param[in] q quaternion + * @param[out] dest result matrix + */ +f_inline +void +glm_quat_mat3(vec4 q, mat3 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[0][1] = xy + wz; + dest[1][2] = yz + wx; + dest[2][0] = xz + wy; + + dest[1][0] = xy - wz; + dest[2][1] = yz - wx; + dest[0][2] = xz - wy; +} + +/*! + * @brief convert quaternion to mat3 (transposed) + * + * @param[in] q quaternion + * @param[out] dest result matrix + */ +f_inline +void +glm_quat_mat3t(vec4 q, mat3 dest) { + float w, x, y, z, + xx, yy, zz, + xy, yz, xz, + wx, wy, wz, norm, s; + + norm = glm_quat_norm(q); + s = norm > 0.0f ? 2.0f / norm : 0.0f; + + x = q[0]; + y = q[1]; + z = q[2]; + w = q[3]; + + xx = s * x * x; xy = s * x * y; wx = s * w * x; + yy = s * y * y; yz = s * y * z; wy = s * w * y; + zz = s * z * z; xz = s * x * z; wz = s * w * z; + + dest[0][0] = 1.0f - yy - zz; + dest[1][1] = 1.0f - xx - zz; + dest[2][2] = 1.0f - xx - yy; + + dest[1][0] = xy + wz; + dest[2][1] = yz + wx; + dest[0][2] = xz + wy; + + dest[0][1] = xy - wz; + dest[1][2] = yz - wx; + dest[2][0] = xz - wy; +} + +/*! + * @brief interpolates between two quaternions + * using linear interpolation (LERP) + * + * @param[in] from from + * @param[in] to to + * @param[in] t interpolant (amount) + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_lerp(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerp(from, to, t, dest); +} + +/*! + * @brief interpolates between two quaternions + * using linear interpolation (LERP) + * + * @param[in] from from + * @param[in] to to + * @param[in] t interpolant (amount) clamped between 0 and 1 + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_lerpc(vec4 from, vec4 to, float t, vec4 dest) { + glm_vec4_lerpc(from, to, t, dest); +} + +/*! + * @brief interpolates between two quaternions + * taking the shortest rotation path using + * normalized linear interpolation (NLERP) + * + * @param[in] from from + * @param[in] to to + * @param[in] t interpolant (amount) + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_nlerp(vec4 from, vec4 to, float t, vec4 dest) { + vec4 target; + float dot; + + dot = glm_vec4_dot(from, to); + + glm_vec4_scale(to, (dot >= 0) ? 1.0f : -1.0f, target); + glm_quat_lerp(from, target, t, dest); + glm_quat_normalize(dest); +} + +/*! + * @brief interpolates between two quaternions + * using spherical linear interpolation (SLERP) + * + * @param[in] from from + * @param[in] to to + * @param[in] t amout + * @param[out] dest result quaternion + */ +f_inline +void +glm_quat_slerp(vec4 from, vec4 to, float t, vec4 dest) { + f_align(16) vec4 q1, q2; + float cosTheta, sinTheta, angle; + + cosTheta = glm_quat_dot(from, to); + glm_quat_copy(from, q1); + + if (fabsf(cosTheta) >= 1.0f) { + glm_quat_copy(q1, dest); + return; + } + + if (cosTheta < 0.0f) { + glm_vec4_negate(q1); + cosTheta = -cosTheta; + } + + sinTheta = sqrtf(1.0f - cosTheta * cosTheta); + + /* LERP to avoid zero division */ + if (fabsf(sinTheta) < 0.001f) { + glm_quat_lerp(from, to, t, dest); + return; + } + + /* SLERP */ + angle = acosf(cosTheta); + glm_vec4_scale(q1, sinf((1.0f - t) * angle), q1); + glm_vec4_scale(to, sinf(t * angle), q2); + + glm_vec4_add(q1, q2, q1); + glm_vec4_scale(q1, 1.0f / sinTheta, dest); +} + +/*! + * @brief creates view matrix using quaternion as camera orientation + * + * @param[in] eye eye + * @param[in] ori orientation in world space as quaternion + * @param[out] dest view matrix + */ +f_inline +void +glm_quat_look(vec3 eye, vec4 ori, mat4 dest) { + /* orientation */ + glm_quat_mat4t(ori, dest); + + /* translate */ + glm_mat4_mulv3(dest, eye, 1.0f, dest[3]); + glm_vec3_negate(dest[3]); +} + +/*! + * @brief creates look rotation quaternion + * + * @param[in] dir direction to look + * @param[in] up up vector + * @param[out] dest destination quaternion + */ +f_inline +void +glm_quat_for(vec3 dir, vec3 up, vec4 dest) { + f_align(16) mat3 m; + + glm_vec3_normalize_to(dir, m[2]); + + /* No need to negate in LH, but we use RH here */ + glm_vec3_negate(m[2]); + + glm_vec3_crossn(up, m[2], m[0]); + glm_vec3_cross(m[2], m[0], m[1]); + + glm_mat3_quat(m, dest); +} + +/*! + * @brief creates look rotation quaternion using source and + * destination positions p suffix stands for position + * + * @param[in] from source point + * @param[in] to destination point + * @param[in] up up vector + * @param[out] dest destination quaternion + */ +f_inline +void +glm_quat_forp(vec3 from, vec3 to, vec3 up, vec4 dest) { + f_align(8) vec3 dir; + glm_vec3_sub(to, from, dir); + glm_quat_for(dir, up, dest); +} + +/*! + * @brief rotate vector using using quaternion + * + * @param[in] q quaternion + * @param[in] v vector to rotate + * @param[out] dest rotated vector + */ +f_inline +void +glm_quat_rotatev(vec4 q, vec3 v, vec3 dest) { + f_align(16) vec4 p; + f_align(8) vec3 u, v1, v2; + float s; + + glm_quat_normalize_to(q, p); + glm_quat_imag(p, u); + s = glm_quat_real(p); + + glm_vec3_scale(u, 2.0f * glm_vec3_dot(u, v), v1); + glm_vec3_scale(v, s * s - glm_vec3_dot(u, u), v2); + glm_vec3_add(v1, v2, v1); + + glm_vec3_cross(u, v, v2); + glm_vec3_scale(v2, 2.0f * s, v2); + + glm_vec3_add(v1, v2, dest); +} + +/*! + * @brief rotate existing transform matrix using quaternion + * + * @param[in] m existing transform matrix + * @param[in] q quaternion + * @param[out] dest rotated matrix/transform + */ +f_inline +void +glm_quat_rotate(mat4 m, vec4 q, mat4 dest) { + f_align(16) mat4 rot; + glm_quat_mat4(q, rot); + glm_mul_rot(m, rot, dest); +} + +/*! + * @brief rotate existing transform matrix using quaternion at pivot point + * + * @param[in, out] m existing transform matrix + * @param[in] q quaternion + * @param[out] pivot pivot + */ +f_inline +void +glm_quat_rotate_at(mat4 m, vec4 q, vec3 pivot) { + f_align(8) vec3 pivotInv; + + glm_vec3_negate_to(pivot, pivotInv); + + glm_translate(m, pivot); + glm_quat_rotate(m, q, m); + glm_translate(m, pivotInv); +} + +/*! + * @brief rotate NEW transform matrix using quaternion at pivot point + * + * this creates rotation matrix, it assumes you don't have a matrix + * + * this should work faster than glm_quat_rotate_at because it reduces + * one glm_translate. + * + * @param[out] m existing transform matrix + * @param[in] q quaternion + * @param[in] pivot pivot + */ +f_inline +void +glm_quat_rotate_atm(mat4 m, vec4 q, vec3 pivot) { + f_align(8) vec3 pivotInv; + + glm_vec3_negate_to(pivot, pivotInv); + + glm_translate_make(m, pivot); + glm_quat_rotate(m, q, m); + glm_translate(m, pivotInv); +} + + + + + + + + + + +/*! + * if you have axis order like vec3 orderVec = [0, 1, 2] or [0, 2, 1]... + * vector then you can convert it to this enum by doing this: + * @code + * glm_euler_seq order; + * order = orderVec[0] | orderVec[1] << 2 | orderVec[2] << 4; + * @endcode + * you may need to explicit cast if required + */ +typedef enum glm_euler_seq { + GLM_EULER_XYZ = 0 << 0 | 1 << 2 | 2 << 4, + GLM_EULER_XZY = 0 << 0 | 2 << 2 | 1 << 4, + GLM_EULER_YZX = 1 << 0 | 2 << 2 | 0 << 4, + GLM_EULER_YXZ = 1 << 0 | 0 << 2 | 2 << 4, + GLM_EULER_ZXY = 2 << 0 | 0 << 2 | 1 << 4, + GLM_EULER_ZYX = 2 << 0 | 1 << 2 | 0 << 4 +} glm_euler_seq; + +f_inline +glm_euler_seq +glm_euler_order(int ord[3]) { + return (glm_euler_seq)(ord[0] << 0 | ord[1] << 2 | ord[2] << 4); +} + +/*! + * @brief extract euler angles (in radians) using xyz order + * + * @param[in] m affine transform + * @param[out] dest angles vector [x, y, z] + */ +f_inline +void +glm_euler_angles(mat4 m, vec3 dest) { + float m00, m01, m10, m11, m20, m21, m22; + float thetaX, thetaY, thetaZ; + + m00 = m[0][0]; m10 = m[1][0]; m20 = m[2][0]; + m01 = m[0][1]; m11 = m[1][1]; m21 = m[2][1]; + m22 = m[2][2]; + + if (m20 < 1.0f) { + if (m20 > -1.0f) { + thetaY = asinf(m20); + thetaX = atan2f(-m21, m22); + thetaZ = atan2f(-m10, m00); + } else { /* m20 == -1 */ + /* Not a unique solution */ + thetaY = -GLM_PI_2f; + thetaX = -atan2f(m01, m11); + thetaZ = 0.0f; + } + } else { /* m20 == +1 */ + thetaY = GLM_PI_2f; + thetaX = atan2f(m01, m11); + thetaZ = 0.0f; + } + + dest[0] = thetaX; + dest[1] = thetaY; + dest[2] = thetaZ; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_xyz(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, czsx, cxcz, sysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + czsx = cz * sx; + cxcz = cx * cz; + sysz = sy * sz; + + dest[0][0] = cy * cz; + dest[0][1] = czsx * sy + cx * sz; + dest[0][2] = -cxcz * sy + sx * sz; + dest[1][0] = -cy * sz; + dest[1][1] = cxcz - sx * sysz; + dest[1][2] = czsx + cx * sysz; + dest[2][0] = sy; + dest[2][1] = -cy * sx; + dest[2][2] = cx * cy; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler(vec3 angles, mat4 dest) { + glm_euler_xyz(angles, dest); +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_xzy(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, sxsy, cysx, cxsy, cxcy; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + sxsy = sx * sy; + cysx = cy * sx; + cxsy = cx * sy; + cxcy = cx * cy; + + dest[0][0] = cy * cz; + dest[0][1] = sxsy + cxcy * sz; + dest[0][2] = -cxsy + cysx * sz; + dest[1][0] = -sz; + dest[1][1] = cx * cz; + dest[1][2] = cz * sx; + dest[2][0] = cz * sy; + dest[2][1] = -cysx + cxsy * sz; + dest[2][2] = cxcy + sxsy * sz; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_yxz(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, cycz, sysz, czsy, cysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + cycz = cy * cz; + sysz = sy * sz; + czsy = cz * sy; + cysz = cy * sz; + + dest[0][0] = cycz + sx * sysz; + dest[0][1] = cx * sz; + dest[0][2] = -czsy + cysz * sx; + dest[1][0] = -cysz + czsy * sx; + dest[1][1] = cx * cz; + dest[1][2] = cycz * sx + sysz; + dest[2][0] = cx * sy; + dest[2][1] = -sx; + dest[2][2] = cx * cy; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_yzx(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, sxsy, cxcy, cysx, cxsy; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + sxsy = sx * sy; + cxcy = cx * cy; + cysx = cy * sx; + cxsy = cx * sy; + + dest[0][0] = cy * cz; + dest[0][1] = sz; + dest[0][2] = -cz * sy; + dest[1][0] = sxsy - cxcy * sz; + dest[1][1] = cx * cz; + dest[1][2] = cysx + cxsy * sz; + dest[2][0] = cxsy + cysx * sz; + dest[2][1] = -cz * sx; + dest[2][2] = cxcy - sxsy * sz; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_zxy(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, cycz, sxsy, cysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + cycz = cy * cz; + sxsy = sx * sy; + cysz = cy * sz; + + dest[0][0] = cycz - sxsy * sz; + dest[0][1] = cz * sxsy + cysz; + dest[0][2] = -cx * sy; + dest[1][0] = -cx * sz; + dest[1][1] = cx * cz; + dest[1][2] = sx; + dest[2][0] = cz * sy + cysz * sx; + dest[2][1] = -cycz * sx + sy * sz; + dest[2][2] = cx * cy; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_zyx(vec3 angles, mat4 dest) { + float cx, cy, cz, + sx, sy, sz, czsx, cxcz, sysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + czsx = cz * sx; + cxcz = cx * cz; + sysz = sy * sz; + + dest[0][0] = cy * cz; + dest[0][1] = cy * sz; + dest[0][2] = -sy; + dest[1][0] = czsx * sy - cx * sz; + dest[1][1] = cxcz + sx * sysz; + dest[1][2] = cy * sx; + dest[2][0] = cxcz * sy + sx * sz; + dest[2][1] = -czsx + cx * sysz; + dest[2][2] = cx * cy; + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + +/*! + * @brief build rotation matrix from euler angles + * + * @param[in] angles angles as vector [Xangle, Yangle, Zangle] + * @param[in] ord euler order + * @param[out] dest rotation matrix + */ +f_inline +void +glm_euler_by_order(vec3 angles, glm_euler_seq ord, mat4 dest) { + float cx, cy, cz, + sx, sy, sz; + + float cycz, cysz, cysx, cxcy, + czsy, cxcz, czsx, cxsz, + sysz; + + sx = sinf(angles[0]); cx = cosf(angles[0]); + sy = sinf(angles[1]); cy = cosf(angles[1]); + sz = sinf(angles[2]); cz = cosf(angles[2]); + + cycz = cy * cz; cysz = cy * sz; + cysx = cy * sx; cxcy = cx * cy; + czsy = cz * sy; cxcz = cx * cz; + czsx = cz * sx; cxsz = cx * sz; + sysz = sy * sz; + + switch (ord) { + case GLM_EULER_XZY: + dest[0][0] = cycz; + dest[0][1] = sx * sy + cx * cysz; + dest[0][2] = -cx * sy + cysx * sz; + dest[1][0] = -sz; + dest[1][1] = cxcz; + dest[1][2] = czsx; + dest[2][0] = czsy; + dest[2][1] = -cysx + cx * sysz; + dest[2][2] = cxcy + sx * sysz; + break; + case GLM_EULER_XYZ: + dest[0][0] = cycz; + dest[0][1] = czsx * sy + cxsz; + dest[0][2] = -cx * czsy + sx * sz; + dest[1][0] = -cysz; + dest[1][1] = cxcz - sx * sysz; + dest[1][2] = czsx + cx * sysz; + dest[2][0] = sy; + dest[2][1] = -cysx; + dest[2][2] = cxcy; + break; + case GLM_EULER_YXZ: + dest[0][0] = cycz + sx * sysz; + dest[0][1] = cxsz; + dest[0][2] = -czsy + cysx * sz; + dest[1][0] = czsx * sy - cysz; + dest[1][1] = cxcz; + dest[1][2] = cycz * sx + sysz; + dest[2][0] = cx * sy; + dest[2][1] = -sx; + dest[2][2] = cxcy; + break; + case GLM_EULER_YZX: + dest[0][0] = cycz; + dest[0][1] = sz; + dest[0][2] = -czsy; + dest[1][0] = sx * sy - cx * cysz; + dest[1][1] = cxcz; + dest[1][2] = cysx + cx * sysz; + dest[2][0] = cx * sy + cysx * sz; + dest[2][1] = -czsx; + dest[2][2] = cxcy - sx * sysz; + break; + case GLM_EULER_ZXY: + dest[0][0] = cycz - sx * sysz; + dest[0][1] = czsx * sy + cysz; + dest[0][2] = -cx * sy; + dest[1][0] = -cxsz; + dest[1][1] = cxcz; + dest[1][2] = sx; + dest[2][0] = czsy + cysx * sz; + dest[2][1] = -cycz * sx + sysz; + dest[2][2] = cxcy; + break; + case GLM_EULER_ZYX: + dest[0][0] = cycz; + dest[0][1] = cysz; + dest[0][2] = -sy; + dest[1][0] = czsx * sy - cxsz; + dest[1][1] = cxcz + sx * sysz; + dest[1][2] = cysx; + dest[2][0] = cx * czsy + sx * sz; + dest[2][1] = -czsx + cx * sysz; + dest[2][2] = cxcy; + break; + } + + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][0] = 0.0f; + dest[3][1] = 0.0f; + dest[3][2] = 0.0f; + dest[3][3] = 1.0f; +} + + + + + + + + + +/*! + * @brief apply transform to Axis-Aligned Bounding Box + * + * @param[in] box bounding box + * @param[in] m transform matrix + * @param[out] dest transformed bounding box + */ +f_inline +void +glm_aabb_transform(vec3 box[2], mat4 m, vec3 dest[2]) { + vec3 v[2], xa, xb, ya, yb, za, zb; + + glm_vec3_scale(m[0], box[0][0], xa); + glm_vec3_scale(m[0], box[1][0], xb); + + glm_vec3_scale(m[1], box[0][1], ya); + glm_vec3_scale(m[1], box[1][1], yb); + + glm_vec3_scale(m[2], box[0][2], za); + glm_vec3_scale(m[2], box[1][2], zb); + + /* translation + min(xa, xb) + min(ya, yb) + min(za, zb) */ + glm_vec3(m[3], v[0]); + glm_vec3_minadd(xa, xb, v[0]); + glm_vec3_minadd(ya, yb, v[0]); + glm_vec3_minadd(za, zb, v[0]); + + /* translation + max(xa, xb) + max(ya, yb) + max(za, zb) */ + glm_vec3(m[3], v[1]); + glm_vec3_maxadd(xa, xb, v[1]); + glm_vec3_maxadd(ya, yb, v[1]); + glm_vec3_maxadd(za, zb, v[1]); + + glm_vec3_copy(v[0], dest[0]); + glm_vec3_copy(v[1], dest[1]); +} + +/*! + * @brief merges two AABB bounding box and creates new one + * + * two box must be in same space, if one of box is in different space then + * you should consider to convert it's space by glm_box_space + * + * @param[in] box1 bounding box 1 + * @param[in] box2 bounding box 2 + * @param[out] dest merged bounding box + */ +f_inline +void +glm_aabb_merge(vec3 box1[2], vec3 box2[2], vec3 dest[2]) { + dest[0][0] = glm_min(box1[0][0], box2[0][0]); + dest[0][1] = glm_min(box1[0][1], box2[0][1]); + dest[0][2] = glm_min(box1[0][2], box2[0][2]); + + dest[1][0] = glm_max(box1[1][0], box2[1][0]); + dest[1][1] = glm_max(box1[1][1], box2[1][1]); + dest[1][2] = glm_max(box1[1][2], box2[1][2]); +} + +/*! + * @brief crops a bounding box with another one. + * + * this could be useful for gettng a bbox which fits with view frustum and + * object bounding boxes. In this case you crop view frustum box with objects + * box + * + * @param[in] box bounding box 1 + * @param[in] cropBox crop box + * @param[out] dest cropped bounding box + */ +f_inline +void +glm_aabb_crop(vec3 box[2], vec3 cropBox[2], vec3 dest[2]) { + dest[0][0] = glm_max(box[0][0], cropBox[0][0]); + dest[0][1] = glm_max(box[0][1], cropBox[0][1]); + dest[0][2] = glm_max(box[0][2], cropBox[0][2]); + + dest[1][0] = glm_min(box[1][0], cropBox[1][0]); + dest[1][1] = glm_min(box[1][1], cropBox[1][1]); + dest[1][2] = glm_min(box[1][2], cropBox[1][2]); +} + +/*! + * @brief crops a bounding box with another one. + * + * this could be useful for gettng a bbox which fits with view frustum and + * object bounding boxes. In this case you crop view frustum box with objects + * box + * + * @param[in] box bounding box + * @param[in] cropBox crop box + * @param[in] clampBox miniumum box + * @param[out] dest cropped bounding box + */ +f_inline +void +glm_aabb_crop_until(vec3 box[2], + vec3 cropBox[2], + vec3 clampBox[2], + vec3 dest[2]) { + glm_aabb_crop(box, cropBox, dest); + glm_aabb_merge(clampBox, dest, dest); +} + +/*! + * @brief check if AABB intersects with frustum planes + * + * this could be useful for frustum culling using AABB. + * + * OPTIMIZATION HINT: + * if planes order is similar to LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR + * then this method should run even faster because it would only use two + * planes if object is not inside the two planes + * fortunately cglm extracts planes as this order! just pass what you got! + * + * @param[in] box bounding box + * @param[in] planes frustum planes + */ +f_inline +byte +glm_aabb_frustum(vec3 box[2], vec4 planes[6]) { + float *p, dp; + int i; + + for (i = 0; i < 6; i++) { + p = planes[i]; + dp = p[0] * box[p[0] > 0.0f][0] + + p[1] * box[p[1] > 0.0f][1] + + p[2] * box[p[2] > 0.0f][2]; + + if (dp < -p[3]) + return 0; + } + + return 1; +} + +/*! + * @brief invalidate AABB min and max values + * + * @param[in, out] box bounding box + */ +f_inline +void +glm_aabb_invalidate(vec3 box[2]) { + glm_vec3_broadcast(FLT_MAX, box[0]); + glm_vec3_broadcast(-FLT_MAX, box[1]); +} + +/*! + * @brief check if AABB is valid or not + * + * @param[in] box bounding box + */ +f_inline +byte +glm_aabb_isvalid(vec3 box[2]) { + return glm_vec3_max(box[0]) != FLT_MAX + && glm_vec3_min(box[1]) != -FLT_MAX; +} + +/*! + * @brief distance between of min and max + * + * @param[in] box bounding box + */ +f_inline +float +glm_aabb_size(vec3 box[2]) { + return glm_vec3_distance(box[0], box[1]); +} + +/*! + * @brief radius of sphere which surrounds AABB + * + * @param[in] box bounding box + */ +f_inline +float +glm_aabb_radius(vec3 box[2]) { + return glm_aabb_size(box) * 0.5f; +} + +/*! + * @brief computes center point of AABB + * + * @param[in] box bounding box + * @param[out] dest center of bounding box + */ +f_inline +void +glm_aabb_center(vec3 box[2], vec3 dest) { + glm_vec3_center(box[0], box[1], dest); +} + +/*! + * @brief check if two AABB intersects + * + * @param[in] box bounding box + * @param[in] other other bounding box + */ +f_inline +byte +glm_aabb_aabb(vec3 box[2], vec3 other[2]) { + return (box[0][0] <= other[1][0] && box[1][0] >= other[0][0]) + && (box[0][1] <= other[1][1] && box[1][1] >= other[0][1]) + && (box[0][2] <= other[1][2] && box[1][2] >= other[0][2]); +} + +/*! + * @brief check if AABB intersects with sphere + * + * https://github.com/erich666/GraphicsGems/blob/master/gems/BoxSphere.c + * Solid Box - Solid Sphere test. + * + * Sphere Representation in cglm: [center.x, center.y, center.z, radii] + * + * @param[in] box solid bounding box + * @param[in] s solid sphere + */ +f_inline +byte +glm_aabb_sphere(vec3 box[2], vec4 s) { + float dmin; + int a, b, c; + + a = (s[0] < box[0][0]) + (s[0] > box[1][0]); + b = (s[1] < box[0][1]) + (s[1] > box[1][1]); + c = (s[2] < box[0][2]) + (s[2] > box[1][2]); + + dmin = glm_pow2((s[0] - box[!(a - 1)][0]) * (a != 0)) + + glm_pow2((s[1] - box[!(b - 1)][1]) * (b != 0)) + + glm_pow2((s[2] - box[!(c - 1)][2]) * (c != 0)); + + return dmin <= glm_pow2(s[3]); +} + +/*! + * @brief check if point is inside of AABB + * + * @param[in] box bounding box + * @param[in] point point + */ +f_inline +byte +glm_aabb_point(vec3 box[2], vec3 point) { + return (point[0] >= box[0][0] && point[0] <= box[1][0]) + && (point[1] >= box[0][1] && point[1] <= box[1][1]) + && (point[2] >= box[0][2] && point[2] <= box[1][2]); +} + +/*! + * @brief check if AABB contains other AABB + * + * @param[in] box bounding box + * @param[in] other other bounding box + */ +f_inline +byte +glm_aabb_contains(vec3 box[2], vec3 other[2]) { + return (box[0][0] <= other[0][0] && box[1][0] >= other[1][0]) + && (box[0][1] <= other[0][1] && box[1][1] >= other[1][1]) + && (box[0][2] <= other[0][2] && box[1][2] >= other[1][2]); +} + + + + + + + + + + +/*! + * @brief averages the color channels into one value + * + * @param[in] rgb RGB color + */ +f_inline +float +glm_luminance(vec3 rgb) { + vec3 l = {0.212671f, 0.715160f, 0.072169f}; + return glm_dot(rgb, l); +} + + + + + + + + + +/*! + * @brief maps the specified viewport coordinates into specified space [1] + * the matrix should contain projection matrix. + * + * if you don't have ( and don't want to have ) an inverse matrix then use + * glm_unproject version. You may use existing inverse of matrix in somewhere + * else, this is why glm_unprojecti exists to save save inversion cost + * + * [1] space: + * 1- if m = invProj: View Space + * 2- if m = invViewProj: World Space + * 3- if m = invMVP: Object Space + * + * You probably want to map the coordinates into object space + * so use invMVP as m + * + * Computing viewProj: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * glm_mat4_inv(viewProj, invMVP); + * + * @param[in] pos point/position in viewport coordinates + * @param[in] invMat matrix (see brief) + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest unprojected coordinates + */ +f_inline +void +glm_unprojecti_no(vec3 pos, mat4 invMat, vec4 vp, vec3 dest) { + vec4 v; + + v[0] = 2.0f * (pos[0] - vp[0]) / vp[2] - 1.0f; + v[1] = 2.0f * (pos[1] - vp[1]) / vp[3] - 1.0f; + v[2] = 2.0f * pos[2] - 1.0f; + v[3] = 1.0f; + + glm_mat4_mulv(invMat, v, v); + glm_vec4_scale(v, 1.0f / v[3], v); + glm_vec3(v, dest); +} + +/*! + * @brief map object coordinates to window coordinates + * + * Computing MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] pos object coordinates + * @param[in] m MVP matrix + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest projected coordinates + */ +f_inline +void +glm_project_no(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + f_align(16) vec4 pos4; + + glm_vec4(pos, 1.0f, pos4); + + glm_mat4_mulv(m, pos4, pos4); + glm_vec4_scale(pos4, 1.0f / pos4[3], pos4); /* pos = pos / pos.w */ + glm_vec4_scale(pos4, 0.5f, pos4); + glm_vec4_adds(pos4, 0.5f, pos4); + + dest[0] = pos4[0] * vp[2] + vp[0]; + dest[1] = pos4[1] * vp[3] + vp[1]; + dest[2] = pos4[2]; +} + +/*! + * @brief map object's z coordinate to window coordinates + * + * Computing MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] v object coordinates + * @param[in] m MVP matrix + * + * @returns projected z coordinate + */ +f_inline +float +glm_project_z_no(vec3 v, mat4 m) { + float z, w; + + z = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]; + w = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3]; + + return 0.5f * (z / w) + 0.5f; +} + + + + + + + + + +/*! + * @brief maps the specified viewport coordinates into specified space [1] + * the matrix should contain projection matrix. + * + * if you don't have ( and don't want to have ) an inverse matrix then use + * glm_unproject version. You may use existing inverse of matrix in somewhere + * else, this is why glm_unprojecti exists to save save inversion cost + * + * [1] space: + * 1- if m = invProj: View Space + * 2- if m = invViewProj: World Space + * 3- if m = invMVP: Object Space + * + * You probably want to map the coordinates into object space + * so use invMVP as m + * + * Computing viewProj: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * glm_mat4_inv(viewProj, invMVP); + * + * @param[in] pos point/position in viewport coordinates + * @param[in] invMat matrix (see brief) + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest unprojected coordinates + */ +f_inline +void +glm_unprojecti(vec3 pos, mat4 invMat, vec4 vp, vec3 dest) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_ZO_BIT + glm_unprojecti_zo(pos, invMat, vp, dest); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_NO_BIT + glm_unprojecti_no(pos, invMat, vp, dest); +#endif +} + +/*! + * @brief maps the specified viewport coordinates into specified space [1] + * the matrix should contain projection matrix. + * + * this is same as glm_unprojecti except this function get inverse matrix for + * you. + * + * [1] space: + * 1- if m = proj: View Space + * 2- if m = viewProj: World Space + * 3- if m = MVP: Object Space + * + * You probably want to map the coordinates into object space + * so use MVP as m + * + * Computing viewProj and MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] pos point/position in viewport coordinates + * @param[in] m matrix (see brief) + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest unprojected coordinates + */ +f_inline +void +glm_unproject(vec3 pos, mat4 m, vec4 vp, vec3 dest) { + mat4 inv; + glm_mat4_inv(m, inv); + glm_unprojecti(pos, inv, vp, dest); +} + +/*! + * @brief map object coordinates to window coordinates + * + * Computing MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] pos object coordinates + * @param[in] m MVP matrix + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest projected coordinates + */ +f_inline +void +glm_project(vec3 pos, mat4 m, vec4 vp, vec3 dest) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_ZO_BIT + glm_project_zo(pos, m, vp, dest); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_NO_BIT + glm_project_no(pos, m, vp, dest); +#endif +} + +/*! + * @brief map object's z coordinate to window coordinates + * + * Computing MVP: + * glm_mat4_mul(proj, view, viewProj); + * glm_mat4_mul(viewProj, model, MVP); + * + * @param[in] v object coordinates + * @param[in] m MVP matrix + * + * @returns projected z coordinate + */ +f_inline +float +glm_project_z(vec3 v, mat4 m) { +#if CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_ZO_BIT + return glm_project_z_zo(v, m); +#elif CGLM_CONFIG_CLIP_CONTROL & CGLM_CLIP_CONTROL_NO_BIT + return glm_project_z_no(v, m); +#endif +} + +/*! + * @brief define a picking region + * + * @param[in] center center [x, y] of a picking region in window coordinates + * @param[in] size size [width, height] of the picking region in window coordinates + * @param[in] vp viewport as [x, y, width, height] + * @param[out] dest projected coordinates + */ +f_inline +void +glm_pickmatrix(vec2 center, vec2 size, vec4 vp, mat4 dest) { + mat4 res; + vec3 v; + + if (size[0] <= 0.0f || size[1] <= 0.0f) + return; + + /* Translate and scale the picked region to the entire window */ + v[0] = (vp[2] - 2.0f * (center[0] - vp[0])) / size[0]; + v[1] = (vp[3] - 2.0f * (center[1] - vp[1])) / size[1]; + v[2] = 0.0f; + + glm_translate_make(res, v); + + v[0] = vp[2] / size[0]; + v[1] = vp[3] / size[1]; + v[2] = 1.0f; + + glm_scale(res, v); + + glm_mat4_copy(res, dest); +} + + + + + + + + + + + +/* + Sphere Representation in cglm: [center.x, center.y, center.z, radii] + + You could use this representation or you can convert it to vec4 before call + any function + */ + +/*! + * @brief helper for getting sphere radius + * + * @param[in] s sphere + * + * @return returns radii + */ +f_inline +float +glm_sphere_radii(vec4 s) { + return s[3]; +} + +/*! + * @brief apply transform to sphere, it is just wrapper for glm_mat4_mulv3 + * + * @param[in] s sphere + * @param[in] m transform matrix + * @param[out] dest transformed sphere + */ +f_inline +void +glm_sphere_transform(vec4 s, mat4 m, vec4 dest) { + glm_mat4_mulv3(m, s, 1.0f, dest); + dest[3] = s[3]; +} + +/*! + * @brief merges two spheres and creates a new one + * + * two sphere must be in same space, for instance if one in world space then + * the other must be in world space too, not in local space. + * + * @param[in] s1 sphere 1 + * @param[in] s2 sphere 2 + * @param[out] dest merged/extended sphere + */ +f_inline +void +glm_sphere_merge(vec4 s1, vec4 s2, vec4 dest) { + float dist, radii; + + dist = glm_vec3_distance(s1, s2); + radii = dist + s1[3] + s2[3]; + + radii = glm_max(radii, s1[3]); + radii = glm_max(radii, s2[3]); + + glm_vec3_center(s1, s2, dest); + dest[3] = radii; +} + +/*! + * @brief check if two sphere intersects + * + * @param[in] s1 sphere + * @param[in] s2 other sphere + */ +f_inline +byte +glm_sphere_sphere(vec4 s1, vec4 s2) { + return glm_vec3_distance2(s1, s2) <= glm_pow2(s1[3] + s2[3]); +} + +/*! + * @brief check if sphere intersects with point + * + * @param[in] s sphere + * @param[in] point point + */ +f_inline +byte +glm_sphere_point(vec4 s, vec3 point) { + float rr; + rr = s[3] * s[3]; + return glm_vec3_distance2(point, s) <= rr; +} + + + + + + + + + + + +f_inline +float +glm_ease_linear(float t) { + return t; +} + +f_inline +float +glm_ease_sine_in(float t) { + return sinf((t - 1.0f) * GLM_PI_2f) + 1.0f; +} + +f_inline +float +glm_ease_sine_out(float t) { + return sinf(t * GLM_PI_2f); +} + +f_inline +float +glm_ease_sine_inout(float t) { + return 0.5f * (1.0f - cosf(t * GLM_PIf)); +} + +f_inline +float +glm_ease_quad_in(float t) { + return t * t; +} + +f_inline +float +glm_ease_quad_out(float t) { + return -(t * (t - 2.0f)); +} + +f_inline +float +glm_ease_quad_inout(float t) { + float tt; + + tt = t * t; + if (t < 0.5f) + return 2.0f * tt; + + return (-2.0f * tt) + (4.0f * t) - 1.0f; +} + +f_inline +float +glm_ease_cubic_in(float t) { + return t * t * t; +} + +f_inline +float +glm_ease_cubic_out(float t) { + float f; + f = t - 1.0f; + return f * f * f + 1.0f; +} + +f_inline +float +glm_ease_cubic_inout(float t) { + float f; + + if (t < 0.5f) + return 4.0f * t * t * t; + + f = 2.0f * t - 2.0f; + + return 0.5f * f * f * f + 1.0f; +} + +f_inline +float +glm_ease_quart_in(float t) { + float f; + f = t * t; + return f * f; +} + +f_inline +float +glm_ease_quart_out(float t) { + float f; + + f = t - 1.0f; + + return f * f * f * (1.0f - t) + 1.0f; +} + +f_inline +float +glm_ease_quart_inout(float t) { + float f, g; + + if (t < 0.5f) { + f = t * t; + return 8.0f * f * f; + } + + f = t - 1.0f; + g = f * f; + + return -8.0f * g * g + 1.0f; +} + +f_inline +float +glm_ease_quint_in(float t) { + float f; + f = t * t; + return f * f * t; +} + +f_inline +float +glm_ease_quint_out(float t) { + float f, g; + + f = t - 1.0f; + g = f * f; + + return g * g * f + 1.0f; +} + +f_inline +float +glm_ease_quint_inout(float t) { + float f, g; + + if (t < 0.5f) { + f = t * t; + return 16.0f * f * f * t; + } + + f = 2.0f * t - 2.0f; + g = f * f; + + return 0.5f * g * g * f + 1.0f; +} + +f_inline +float +glm_ease_exp_in(float t) { + if (t == 0.0f) + return t; + + return powf(2.0f, 10.0f * (t - 1.0f)); +} + +f_inline +float +glm_ease_exp_out(float t) { + if (t == 1.0f) + return t; + + return 1.0f - powf(2.0f, -10.0f * t); +} + +f_inline +float +glm_ease_exp_inout(float t) { + if (t == 0.0f || t == 1.0f) + return t; + + if (t < 0.5f) + return 0.5f * powf(2.0f, (20.0f * t) - 10.0f); + + return -0.5f * powf(2.0f, (-20.0f * t) + 10.0f) + 1.0f; +} + +f_inline +float +glm_ease_circ_in(float t) { + return 1.0f - sqrtf(1.0f - (t * t)); +} + +f_inline +float +glm_ease_circ_out(float t) { + return sqrtf((2.0f - t) * t); +} + +f_inline +float +glm_ease_circ_inout(float t) { + if (t < 0.5f) + return 0.5f * (1.0f - sqrtf(1.0f - 4.0f * (t * t))); + + return 0.5f * (sqrtf(-((2.0f * t) - 3.0f) * ((2.0f * t) - 1.0f)) + 1.0f); +} + +f_inline +float +glm_ease_back_in(float t) { + float o, z; + + o = 1.70158f; + z = ((o + 1.0f) * t) - o; + + return t * t * z; +} + +f_inline +float +glm_ease_back_out(float t) { + float o, z, n; + + o = 1.70158f; + n = t - 1.0f; + z = (o + 1.0f) * n + o; + + return n * n * z + 1.0f; +} + +f_inline +float +glm_ease_back_inout(float t) { + float o, z, n, m, s, x; + + o = 1.70158f; + s = o * 1.525f; + x = 0.5; + n = t / 0.5f; + + if (n < 1.0f) { + z = (s + 1) * n - s; + m = n * n * z; + return x * m; + } + + n -= 2.0f; + z = (s + 1.0f) * n + s; + m = (n * n * z) + 2; + + return x * m; +} + +f_inline +float +glm_ease_elast_in(float t) { + return sinf(13.0f * GLM_PI_2f * t) * powf(2.0f, 10.0f * (t - 1.0f)); +} + +f_inline +float +glm_ease_elast_out(float t) { + return sinf(-13.0f * GLM_PI_2f * (t + 1.0f)) * powf(2.0f, -10.0f * t) + 1.0f; +} + +f_inline +float +glm_ease_elast_inout(float t) { + float a; + + a = 2.0f * t; + + if (t < 0.5f) + return 0.5f * sinf(13.0f * GLM_PI_2f * a) + * powf(2.0f, 10.0f * (a - 1.0f)); + + return 0.5f * (sinf(-13.0f * GLM_PI_2f * a) + * powf(2.0f, -10.0f * (a - 1.0f)) + 2.0f); +} + +f_inline +float +glm_ease_bounce_out(float t) { + float tt; + + tt = t * t; + + if (t < (4.0f / 11.0f)) + return (121.0f * tt) / 16.0f; + + if (t < 8.0f / 11.0f) + return ((363.0f / 40.0f) * tt) - ((99.0f / 10.0f) * t) + (17.0f / 5.0f); + + if (t < (9.0f / 10.0f)) + return (4356.0f / 361.0f) * tt + - (35442.0f / 1805.0f) * t + + (16061.0f / 1805.0f); + + return ((54.0f / 5.0f) * tt) - ((513.0f / 25.0f) * t) + (268.0f / 25.0f); +} + +f_inline +float +glm_ease_bounce_in(float t) { + return 1.0f - glm_ease_bounce_out(1.0f - t); +} + +f_inline +float +glm_ease_bounce_inout(float t) { + if (t < 0.5f) + return 0.5f * (1.0f - glm_ease_bounce_out(t * 2.0f)); + + return 0.5f * glm_ease_bounce_out(t * 2.0f - 1.0f) + 0.5f; +} + + + + + + + + + + +/*! + * @brief helper function to calculate S*M*C multiplication for curves + * + * This function does not encourage you to use SMC, + * instead it is a helper if you use SMC. + * + * if you want to specify S as vector then use more generic glm_mat4_rmc() func. + * + * Example usage: + * B(s) = glm_smc(s, GLM_BEZIER_MAT, (vec4){p0, c0, c1, p1}) + * + * @param[in] s parameter between 0 and 1 (this will be [s3, s2, s, 1]) + * @param[in] m basis matrix + * @param[in] c position/control vector + * + * @return B(s) + */ +f_inline +float +glm_smc(float s, mat4 m, vec4 c) { + vec4 vs; + glm_vec4_cubic(s, vs); + return glm_mat4_rmc(vs, m, c); +} + + + + + + + + + +#define GLM_BEZIER_MAT_INIT {{-1.0f, 3.0f, -3.0f, 1.0f}, \ + { 3.0f, -6.0f, 3.0f, 0.0f}, \ + {-3.0f, 3.0f, 0.0f, 0.0f}, \ + { 1.0f, 0.0f, 0.0f, 0.0f}} +#define GLM_HERMITE_MAT_INIT {{ 2.0f, -3.0f, 0.0f, 1.0f}, \ + {-2.0f, 3.0f, 0.0f, 0.0f}, \ + { 1.0f, -2.0f, 1.0f, 0.0f}, \ + { 1.0f, -1.0f, 0.0f, 0.0f}} +/* for C only */ +#define GLM_BEZIER_MAT ((mat4)GLM_BEZIER_MAT_INIT) +#define GLM_HERMITE_MAT ((mat4)GLM_HERMITE_MAT_INIT) + +#define CGLM_DECASTEL_EPS 1e-9f +#define CGLM_DECASTEL_MAX 1000.0f +#define CGLM_DECASTEL_SMALL 1e-20f + +/*! + * @brief cubic bezier interpolation + * + * Formula: + * B(s) = P0*(1-s)^3 + 3*C0*s*(1-s)^2 + 3*C1*s^2*(1-s) + P1*s^3 + * + * similar result using matrix: + * B(s) = glm_smc(t, GLM_BEZIER_MAT, (vec4){p0, c0, c1, p1}) + * + * glm_eq(glm_smc(...), glm_bezier(...)) should return TRUE + * + * @param[in] s parameter between 0 and 1 + * @param[in] p0 begin point + * @param[in] c0 control point 1 + * @param[in] c1 control point 2 + * @param[in] p1 end point + * + * @return B(s) + */ +f_inline +float +glm_bezier(float s, float p0, float c0, float c1, float p1) { + float x, xx, ss, xs3, a; + + x = 1.0f - s; + xx = x * x; + ss = s * s; + xs3 = (s - ss) * 3.0f; + a = p0 * xx + c0 * xs3; + + return a + s * (c1 * xs3 + p1 * ss - a); +} + +/*! + * @brief cubic hermite interpolation + * + * Formula: + * H(s) = P0*(2*s^3 - 3*s^2 + 1) + T0*(s^3 - 2*s^2 + s) + * + P1*(-2*s^3 + 3*s^2) + T1*(s^3 - s^2) + * + * similar result using matrix: + * H(s) = glm_smc(t, GLM_HERMITE_MAT, (vec4){p0, p1, c0, c1}) + * + * glm_eq(glm_smc(...), glm_hermite(...)) should return TRUE + * + * @param[in] s parameter between 0 and 1 + * @param[in] p0 begin point + * @param[in] t0 tangent 1 + * @param[in] t1 tangent 2 + * @param[in] p1 end point + * + * @return H(s) + */ +f_inline +float +glm_hermite(float s, float p0, float t0, float t1, float p1) { + float ss, d, a, b, c, e, f; + + ss = s * s; + a = ss + ss; + c = a + ss; + b = a * s; + d = s * ss; + f = d - ss; + e = b - c; + + return p0 * (e + 1.0f) + t0 * (f - ss + s) + t1 * f - p1 * e; +} + +/*! + * @brief iterative way to solve cubic equation + * + * @param[in] prm parameter between 0 and 1 + * @param[in] p0 begin point + * @param[in] c0 control point 1 + * @param[in] c1 control point 2 + * @param[in] p1 end point + * + * @return parameter to use in cubic equation + */ +f_inline +float +glm_decasteljau(float prm, float p0, float c0, float c1, float p1) { + float u, v, a, b, c, d, e, f; + int i; + + if (prm - p0 < CGLM_DECASTEL_SMALL) + return 0.0f; + + if (p1 - prm < CGLM_DECASTEL_SMALL) + return 1.0f; + + u = 0.0f; + v = 1.0f; + + for (i = 0; i < CGLM_DECASTEL_MAX; i++) { + /* de Casteljau Subdivision */ + a = (p0 + c0) * 0.5f; + b = (c0 + c1) * 0.5f; + c = (c1 + p1) * 0.5f; + d = (a + b) * 0.5f; + e = (b + c) * 0.5f; + f = (d + e) * 0.5f; /* this one is on the curve! */ + + /* The curve point is close enough to our wanted t */ + if (fabsf(f - prm) < CGLM_DECASTEL_EPS) + return glm_clamp_zo((u + v) * 0.5f); + + /* dichotomy */ + if (f < prm) { + p0 = f; + c0 = e; + c1 = c; + u = (u + v) * 0.5f; + } else { + c0 = a; + c1 = d; + p1 = f; + v = (u + v) * 0.5f; + } + } + + return glm_clamp_zo((u + v) * 0.5f); +} + + + + + + + + + + +/*! + * @brief Möller–Trumbore ray-triangle intersection algorithm + * + * @param[in] origin origin of ray + * @param[in] direction direction of ray + * @param[in] v0 first vertex of triangle + * @param[in] v1 second vertex of triangle + * @param[in] v2 third vertex of triangle + * @param[in, out] d distance to intersection + * @return whether there is intersection + */ + +f_inline +byte +glm_ray_triangle(vec3 origin, + vec3 direction, + vec3 v0, + vec3 v1, + vec3 v2, + float *d) { + vec3 edge1, edge2, p, t, q; + float det, inv_det, u, v, dist; + const float epsilon = 0.000001f; + + glm_vec3_sub(v1, v0, edge1); + glm_vec3_sub(v2, v0, edge2); + glm_vec3_cross(direction, edge2, p); + + det = glm_vec3_dot(edge1, p); + if (det > -epsilon && det < epsilon) + return 0; + + inv_det = 1.0f / det; + + glm_vec3_sub(origin, v0, t); + + u = inv_det * glm_vec3_dot(t, p); + if (u < 0.0f || u > 1.0f) + return 0; + + glm_vec3_cross(t, edge1, q); + + v = inv_det * glm_vec3_dot(direction, q); + if (v < 0.0f || u + v > 1.0f) + return 0; + + dist = inv_det * glm_vec3_dot(edge2, q); + + if (d) + *d = dist; + + return dist > epsilon; +} + + + + + + + + + + +/*! + * @brief translate existing 2d transform matrix by v vector + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] v translate vector [x, y] + */ +f_inline +void +glm_translate2d(mat3 m, vec2 v) { + m[2][0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0]; + m[2][1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1]; + m[2][2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2]; +} + +/*! + * @brief translate existing 2d transform matrix by v vector + * and store result in dest + * + * source matrix will remain same + * + * @param[in] m affine transfrom + * @param[in] v translate vector [x, y] + * @param[out] dest translated matrix + */ +f_inline +void +glm_translate2d_to(mat3 m, vec2 v, mat3 dest) { + glm_mat3_copy(m, dest); + glm_translate2d(dest, v); +} + +/*! + * @brief translate existing 2d transform matrix by x factor + * + * @param[in, out] m affine transfrom + * @param[in] x x factor + */ +f_inline +void +glm_translate2d_x(mat3 m, float x) { + m[2][0] = m[0][0] * x + m[2][0]; + m[2][1] = m[0][1] * x + m[2][1]; + m[2][2] = m[0][2] * x + m[2][2]; +} + +/*! + * @brief translate existing 2d transform matrix by y factor + * + * @param[in, out] m affine transfrom + * @param[in] y y factor + */ +f_inline +void +glm_translate2d_y(mat3 m, float y) { + m[2][0] = m[1][0] * y + m[2][0]; + m[2][1] = m[1][1] * y + m[2][1]; + m[2][2] = m[1][2] * y + m[2][2]; +} + +/*! + * @brief creates NEW translate 2d transform matrix by v vector + * + * @param[out] m affine transfrom + * @param[in] v translate vector [x, y] + */ +f_inline +void +glm_translate2d_make(mat3 m, vec2 v) { + glm_mat3_identity(m); + m[2][0] = v[0]; + m[2][1] = v[1]; +} + +/*! + * @brief scale existing 2d transform matrix by v vector + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] v scale vector [x, y] + * @param[out] dest scaled matrix + */ +f_inline +void +glm_scale2d_to(mat3 m, vec2 v, mat3 dest) { + dest[0][0] = m[0][0] * v[0]; + dest[0][1] = m[0][1] * v[0]; + dest[0][2] = m[0][2] * v[0]; + + dest[1][0] = m[1][0] * v[1]; + dest[1][1] = m[1][1] * v[1]; + dest[1][2] = m[1][2] * v[1]; + + dest[2][0] = m[2][0]; + dest[2][1] = m[2][1]; + dest[2][2] = m[2][2]; +} + +/*! + * @brief creates NEW 2d scale matrix by v vector + * + * @param[out] m affine transfrom + * @param[in] v scale vector [x, y] + */ +f_inline +void +glm_scale2d_make(mat3 m, vec2 v) { + glm_mat3_identity(m); + m[0][0] = v[0]; + m[1][1] = v[1]; +} + +/*! + * @brief scales existing 2d transform matrix by v vector + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] v scale vector [x, y] + */ +f_inline +void +glm_scale2d(mat3 m, vec2 v) { + m[0][0] = m[0][0] * v[0]; + m[0][1] = m[0][1] * v[0]; + m[0][2] = m[0][2] * v[0]; + + m[1][0] = m[1][0] * v[1]; + m[1][1] = m[1][1] * v[1]; + m[1][2] = m[1][2] * v[1]; +} + +/*! + * @brief applies uniform scale to existing 2d transform matrix v = [s, s] + * and stores result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] s scale factor + */ +f_inline +void +glm_scale2d_uni(mat3 m, float s) { + m[0][0] = m[0][0] * s; + m[0][1] = m[0][1] * s; + m[0][2] = m[0][2] * s; + + m[1][0] = m[1][0] * s; + m[1][1] = m[1][1] * s; + m[1][2] = m[1][2] * s; +} + +/*! + * @brief creates NEW rotation matrix by angle around Z axis + * + * @param[out] m affine transfrom + * @param[in] angle angle (radians) + */ +f_inline +void +glm_rotate2d_make(mat3 m, float angle) { + float c, s; + + s = sinf(angle); + c = cosf(angle); + + m[0][0] = c; + m[0][1] = s; + m[0][2] = 0; + + m[1][0] = -s; + m[1][1] = c; + m[1][2] = 0; + + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; +} + +/*! + * @brief rotate existing 2d transform matrix around Z axis by angle + * and store result in same matrix + * + * @param[in, out] m affine transfrom + * @param[in] angle angle (radians) + */ +f_inline +void +glm_rotate2d(mat3 m, float angle) { + float m00 = m[0][0], m10 = m[1][0], + m01 = m[0][1], m11 = m[1][1], + m02 = m[0][2], m12 = m[1][2]; + float c, s; + + s = sinf(angle); + c = cosf(angle); + + m[0][0] = m00 * c + m10 * s; + m[0][1] = m01 * c + m11 * s; + m[0][2] = m02 * c + m12 * s; + + m[1][0] = m00 * -s + m10 * c; + m[1][1] = m01 * -s + m11 * c; + m[1][2] = m02 * -s + m12 * c; +} + +/*! + * @brief rotate existing 2d transform matrix around Z axis by angle + * and store result in dest + * + * @param[in] m affine transfrom + * @param[in] angle angle (radians) + * @param[out] dest destination + */ +f_inline +void +glm_rotate2d_to(mat3 m, float angle, mat3 dest) { + float m00 = m[0][0], m10 = m[1][0], + m01 = m[0][1], m11 = m[1][1], + m02 = m[0][2], m12 = m[1][2]; + float c, s; + + s = sinf(angle); + c = cosf(angle); + + dest[0][0] = m00 * c + m10 * s; + dest[0][1] = m01 * c + m11 * s; + dest[0][2] = m02 * c + m12 * s; + + dest[1][0] = m00 * -s + m10 * c; + dest[1][1] = m01 * -s + m11 * c; + dest[1][2] = m02 * -s + m12 * c; + + dest[2][0] = m[2][0]; + dest[2][1] = m[2][1]; + dest[2][2] = m[2][2]; +} diff --git a/texture.h b/texture.h new file mode 100644 index 0000000..07b7b04 --- /dev/null +++ b/texture.h @@ -0,0 +1,247 @@ + +void tex_gen_checkerboard(byte *data, int w, int h, uint color1, uint color2) { + byte color[8]; + color1 = (color1 << 8) | (color1 >> 24); + color2 = (color2 << 8) | (color2 >> 24); + for(int z = 0; z < 4; z++) { + color[z] = (uint)((color1 >> ((3 - z) * 8)) & 0xff); + color[z+4] = (uint)((color2 >> ((3 - z) * 8)) & 0xff); + } + for(int x = 0; x < w; x++) { + for(int y = 0; y < h; y++) { + memcpy(&data[(y*w+x) << 2], &color[((x & 1) ^ (y & 1)) << 2], 4); + } + } +} + +float tri(float x, float p, float a) { + return ((4.0f * a) / p) * fabsf(fmodf(x - p / 4.0f, p) - p / 2.0f) - a; +} + +void tex_gen_triwave(byte *data, int w, int h, uint color1, uint color2, uint color3, uint color4, uint color5, uint color6) { + byte color[16]; + color1 = (color1 << 8) | (color1 >> 24); + color2 = (color2 << 8) | (color2 >> 24); + color3 = (color3 << 8) | (color3 >> 24); + color4 = (color4 << 8) | (color4 >> 24); + color5 = (color5 << 8) | (color5 >> 24); + color6 = (color6 << 8) | (color6 >> 24); + for(int z = 0; z < 4; z++) { + color[z] = (uint)((color1 >> ((3 - z) * 8)) & 0xff); + color[z+4] = (uint)((color2 >> ((3 - z) * 8)) & 0xff); + color[z+8] = (uint)((color3 >> ((3 - z) * 8)) & 0xff); + color[z+12] = (uint)((color4 >> ((3 - z) * 8)) & 0xff); + color[z+16] = (uint)((color5 >> ((3 - z) * 8)) & 0xff); + color[z+20] = (uint)((color6 >> ((3 - z) * 8)) & 0xff); + } + for(int y = 0; y < h; y++) { + int offs = ((y / 2 == h / 16) || (y / 2 == (h / 2 - h / 16) - 1)) ? 16 : (((y / 2 == h / 16 + 1) || (y / 2 == (h / 2 - h / 16) - 2)) ? 20 : 0); + for(int x = 0; x < w; x++) { + memcpy(&data[(y*w+x) << 2], &color[offs], 4); + } + } + for(int x = 0; x < w; x++) { + int y = ((x < w / 8) || (x >= w - w / 8)) ? (h / 2) : (h / 2 + (int)tri((float)(w / 4 + x), (float)((w * 3) / 4), (float)(((h * 3) / 4) / 2))); + sys_assert(y - 8 >= 0 && y + 7 < h) + memcpy(&data[((y-8)*w+x) << 2], &color[0], 4); + memcpy(&data[((y-7)*w+x) << 2], &color[0], 4); + memcpy(&data[((y-6)*w+x) << 2], &color[0], 4); + memcpy(&data[((y-5)*w+x) << 2], &color[4], 4); + memcpy(&data[((y-4)*w+x) << 2], &color[4], 4); + memcpy(&data[((y-3)*w+x) << 2], &color[0], 4); + memcpy(&data[((y-2)*w+x) << 2], &color[0], 4); + memcpy(&data[((y-1)*w+x) << 2], &color[8], 4); + memcpy(&data[((y+0)*w+x) << 2], &color[8], 4); + memcpy(&data[((y+1)*w+x) << 2], &color[0], 4); + memcpy(&data[((y+2)*w+x) << 2], &color[0], 4); + memcpy(&data[((y+3)*w+x) << 2], &color[12], 4); + memcpy(&data[((y+4)*w+x) << 2], &color[12], 4); + memcpy(&data[((y+5)*w+x) << 2], &color[0], 4); + memcpy(&data[((y+6)*w+x) << 2], &color[0], 4); + memcpy(&data[((y+7)*w+x) << 2], &color[0], 4); + } +} + +/* +void tex_pack_1bit(const byte *data, byte *pack, uint size) { + for(int z = 0; z < size; z++) { + pack[z >> 3] &= ~(1 << (z & 7)); + if(data[(z << 2) | 3]) + pack[z >> 3] |= 1 << (z & 7); + } +} + +void tex_unpack_1bit(byte *data, const byte *pack, uint size) { + for(int z = 0; z < size; z++) { + memset(&data[z << 2], (pack[z >> 3] & (1 << (z & 7))) ? 0xff : 0x00, 4); + } +} +*/ + +void tex_param(tex_t *tex) { + byte mipmap = (gdr.tex_miptype < tex->mipmap) ? gdr.tex_miptype : tex->mipmap; + glBindTexture(GL_TEXTURE_2D, *(tex->id)); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (gdr.tex_filter && tex->filter) + ? (mipmap ? ((mipmap == 1) ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR) : GL_LINEAR) + : (mipmap ? ((mipmap == 1) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR) : GL_NEAREST)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (gdr.tex_filter && tex->filter) ? GL_LINEAR : GL_NEAREST); + // if(gdr.aniso_avail) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, tex->filter ? MIN_VALUE(gdr.tex_aniso, gdr.aniso_max) : 1.0f); +} + +int tex_config() { + int done = 0; + ulong iter = 0; + tex_t *tex; + while(tex = tbl_iter(&gdr.textures, &iter)) { + tex_param(tex); + done += 1; + } + logd(LOG_GFX, STR_TEX_PARAMS, done); + return done; +} + +void tex_delete(uint *tex) { +// uint *tex; +// for(int z = 0; z < gdr.tex_loaded; z++) { +// tex = gdr.textures[z]; + if(*tex) { + glDeleteTextures(1, tex); + logd(LOG_GFX, STR_TEX_IUNLOADED, *tex); + *tex = 0; + } +// } +} + +byte tex_unload(tex_t *tex) { + if(!(*(tex->id))) { + return 0; + } + glDeleteTextures(1, tex->id); + gdr.tex_mem -= tex->width * tex->height * 4; + logd(LOG_GFX, STR_TEX_UNLOADED, tex->path ? tex->path : "[built-in]", *(tex->id)); + *(tex->id) = 0; + return 1; +} + +void tex_remove(tex_t *tex) { + if(tex_unload(tex)) { + // tbl_pop(&gdr.textures, tex); + gdr.tex_loaded -= 1; + } +} + +int tex_clear() { + return tbl_clear_func(&gdr.textures, (clear_func*)tex_remove); +} + +byte *img_load(const char *filename, int *width, int *height, byte flip, byte ignmiss) { + byte *data; + int err; + FILE *fd; + if(!(fd = fopen(filename, "rb"))) { + err = errno; + if(err != ENOENT || !ignmiss) + loge(LOG_GFX, STR_TEX_EOPEN, filename, strerror(err), err); + return 0; + } + data = stbi_load(fd, width, height, flip); + fclose(fd); + if(!data) { + loge(LOG_GFX, STR_TEX_ELOAD, filename, stbi_failure_desc(), stbi_failure_reason()); + } + return data; +} + +void tex_push(uint *tex, const byte *data, int width, int height) { + glGenTextures(1, tex); + glBindTexture(GL_TEXTURE_2D, *tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + gdr.tex_mem += width * height * 4; + gdr.tex_peak = gdr.tex_mem > gdr.tex_peak ? gdr.tex_mem : gdr.tex_peak; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +void tex_gen_fallback(uint *id, int size) { + byte *data = mem_alloc(size * size * 4, MEM_IMAGE); + tex_gen_checkerboard(data, size, size, 0xff000000, 0xffff00ff); + tex_push(id, data, size, size); + mem_free(data); + logd(LOG_GFX, STR_TEX_ILOADED, "fallback", *id); +} + +void tex_push_par(uint *tex, const byte *data, const char *filename, int width, int height, byte filter, byte mipmap) { + tex_t *bind; + sys_assert(bind = (tex_t*)tbl_push(&gdr.textures)); + tex_push(tex, data, width, height); + glGenerateMipmap(GL_TEXTURE_2D); + // mem_free(data); + bind->id = tex; + bind->width = width; + bind->height = height; + bind->filter = filter; + bind->mipmap = mipmap; + bind->path = filename; + tex_param(bind); +} + +/* +byte tex_load_sys(uint *tex, const char *filename, byte flip) { + byte *data; + int width, height; + if(!(data = img_load(filename, &width, &height, flip, 0))) { + *tex = sys.tex_fallback; + return 0; + } + tex_push(tex, data, width, height); + mem_free(data); + logd(LOG_GFX, STR_TEX_ILOADED, filename, *tex); + return 1; +} +*/ + +byte tex_load(uint *tex, const char *filename, byte flags) { + byte *data; + int width, height; + if(!(data = img_load(filename, &width, &height, (flags & TEX_FLAG_FLIP) != 0, 0))) { + *tex = gdr.tex_fallback; + return 0; + } + tex_push_par(tex, data, filename, width, height, (flags & TEX_FLAG_FILTER) != 0, ((flags & TEX_FLAG_MIPMAP) != 0) ? 2 : + ((flags & TEX_FLAG_NMIPMAP) != 0)); + gdr.tex_loaded += 1; + mem_free(data); + logd(LOG_GFX, STR_TEX_LOADED, filename, *tex); + return 1; +} + +byte img_save(const char *filename, int x, int y, byte alpha, byte flip, int stride, const byte *data) { + int len; + byte *png; + int err; + FILE *fd; + if(!(png = stbi_write_png_to_mem(data, stride, x, y, alpha ? 4 : 3, &len, flip))) { + loge(LOG_GFX, STR_TEX_ESAVE, filename); + return 0; + } + if(!(fd = fopen(filename, "wb"))) { + err = errno; + loge(LOG_GFX, STR_TEX_EWOPEN, filename, strerror(err), err); + mem_free(png); + return 0; + } + if(fwrite(png, 1, len, fd) != len) { + loge(LOG_GFX, STR_TEX_EWRITE, filename); + fclose(fd); + mem_free(png); + return 0; + } + fclose(fd); + mem_free(png); + return 1; +} diff --git a/textures/crosshair.png b/textures/crosshair.png new file mode 100644 index 0000000..24cb6c8 Binary files /dev/null and b/textures/crosshair.png differ diff --git a/textures/floor.png b/textures/floor.png new file mode 100644 index 0000000..c15842d Binary files /dev/null and b/textures/floor.png differ diff --git a/textures/logo.png b/textures/logo.png new file mode 100644 index 0000000..91245a0 Binary files /dev/null and b/textures/logo.png differ diff --git a/textures/logo.xcf b/textures/logo.xcf new file mode 100644 index 0000000..36dad83 Binary files /dev/null and b/textures/logo.xcf differ diff --git a/textures/test.png b/textures/test.png new file mode 100644 index 0000000..3c92ac0 Binary files /dev/null and b/textures/test.png differ diff --git a/textures/tri.png b/textures/tri.png new file mode 100644 index 0000000..57e060b Binary files /dev/null and b/textures/tri.png differ diff --git a/themes.h b/themes.h new file mode 100644 index 0000000..817dc75 --- /dev/null +++ b/themes.h @@ -0,0 +1,27 @@ + +theme_add(STR_THEME_SHINE); +theme_window(0x202020, 0x808080, 0xcfcfcf, 0x3f3f3f, 0xffffff); +theme_border(0xffffff, 0x3f3f3f, 1); +theme_background(0x000000, 0x000000); +theme_base(0x404040, 0x000000, 0xffffff, 0xefefef); +theme_field(0x000000, 0x202020, 0xdfdfdf); +theme_slider(10); +theme_select(0x30ffffff, 0x30ffffff, 0x28808080, 0x28ffffff, 0x60dfdfdf, 0xffffffff); + +theme_add(STR_THEME_GRAY); +theme_window(0x404040, 0x202020, 0x808080, 0x000000, 0xffffff); +theme_border(0x000000, 0x202020, 1); +theme_background(0x404040, 0x0a0a0a); +theme_base(0x808080, 0x000000, 0xffffff, 0xffffff); +theme_field(0x404040, 0x808080, 0xffffff); +theme_slider(10); +theme_select(0x18ffffff, 0x18ffffff, 0x288080ff, 0x288080ff, 0x808080ff, 0xff000000); + +theme_add(STR_THEME_BLUE); +theme_window(0x404040, 0x202020, 0x808080, 0x000000, 0x6fff8f); +theme_border(0x0000df, 0x300020, 1); +theme_background(0x20208f, 0x0a0a2d); +theme_base(0x2020a0, 0x000020, 0x8fffaf, 0x00cfaf); +theme_field(0x505090, 0x406060, 0xcfdfff); +theme_slider(10); +theme_select(0x288f00ff, 0x18ffffff, 0x288080ff, 0x28c080ff, 0x604020ff, 0xff2fff6f); diff --git a/timing.h b/timing.h new file mode 100644 index 0000000..f279367 --- /dev/null +++ b/timing.h @@ -0,0 +1,37 @@ + +ulong tmr_time() { + struct timespec tm; + if(clock_gettime((clockid_t)sys.tmr_timer, &tm)) { + return 0ULL; + } + return tm.tv_sec * 1000000ULL + tm.tv_nsec / 1000ULL; +} + +ulong tmr_ntime() { + struct timespec tm; + if(clock_gettime((clockid_t)sys.tmr_timer, &tm)) { + return 0ULL; + } + return tm.tv_sec * 1000000000ULL + tm.tv_nsec; +} + +ulong tmr_rtime() { + return tmr_time() - sys.tmr_start; +} + +double tmr_ftime() { + return ((double)tmr_rtime()) / 1000000.0; +} + +clockid_t tmr_gettimer() { + struct timespec tm; + clockid_t timer = CLOCK_MONOTONIC_RAW; + if(clock_gettime(timer, &tm)) { + timer = CLOCK_MONOTONIC; + if(clock_gettime(timer, &tm)) { + timer = CLOCK_REALTIME; + sys_assert(!clock_gettime(timer, &tm)) + } + } + return timer; +} diff --git a/todo-skc.txt b/todo-skc.txt new file mode 100644 index 0000000..fc77c1e --- /dev/null +++ b/todo-skc.txt @@ -0,0 +1,32 @@ +tick-> ptick, stick, ... +hammerspace_rules +gui alpha +PRIOF.DAR +splashes +OPK?? +3d world graph - chars, entities, etc. +in-gui keys +dc32 +CAD-Funktionen, ... +"Fünfstein" +demo + +DAR file - data access registry +LCFS - low carbon file system + +?? global dynlight +?? dropdown scroll + +midi velo graph + perc reassign +axis indicator +lightmap color vertex multiplier +lights dist based +tbl -> dat -> dynamic allocation table +confirm popup +console tab + history, ... +hide crosshair + hud +gldebug func split + cmd opt +nosound cmd opt (valgrind) +noerror, flush, preproc, assert, memtrace ... + +transparent win + no win side + move by drag whole window diff --git a/types.h b/types.h new file mode 100644 index 0000000..7c7a0bb --- /dev/null +++ b/types.h @@ -0,0 +1,2243 @@ + +// memory sizes +#define MEM_CATS 12 +#define LOG_BUF 65536 +#define WORK_BUF 65536 +#define LOCALE_SIZE 512 +#define LOCALE_ELEM 128 +#define GUI_MAX_ELEM 512 +#define GUI_STR_SIZE 256 +#define SHD_INCLUDE 256 +#define KEY_MAXBINDS 64 +#define PERF_SECTIONS 10 +#define CVAR_MAX 256 +#define CCMD_MAX 256 +#define CON_BUF 32768 +#define CON_LINE 1024 +#define HUD_MSGS 128 +#define HUD_BUF 16384 +#define VID_MODES 28 +#define VID_MODE_STR 32 +#define SND_QUEUE 1024 +#define SND_LOG 256 +#define SND_LOG_LEN 256 +#define COLL_MAX 1024 +#define MATERIAL_MAX 4096 +#define SND_INFO 64 +#define SND_KAR 72 +#define SND_KLOG 5 +#define BANK_MAX 64 +#define GUI_AUX_STR 320 + +// numerical constants +#define GLM_E 2.71828182845904523536028747135266250 /* e */ +#define GLM_LOG2E 1.44269504088896340735992468100189214 /* log2(e) */ +#define GLM_LOG10E 0.434294481903251827651128918916605082 /* log10(e) */ +#define GLM_LN2 0.693147180559945309417232121458176568 /* loge(2) */ +#define GLM_LN10 2.30258509299404568401799145468436421 /* loge(10) */ +#define GLM_PI 3.14159265358979323846264338327950288 /* pi */ +#define GLM_PI_2 1.57079632679489661923132169163975144 /* pi/2 */ +#define GLM_PI_4 0.785398163397448309615660845819875721 /* pi/4 */ +#define GLM_1_PI 0.318309886183790671537767526745028724 /* 1/pi */ +#define GLM_2_PI 0.636619772367581343075535053490057448 /* 2/pi */ +#define GLM_2_SQRTPI 1.12837916709551257389615890312154517 /* 2/sqrt(pi) */ +#define GLM_SQRT2 1.41421356237309504880168872420969808 /* sqrt(2) */ +#define GLM_SQRT1_2 0.707106781186547524400844362104849039 /* 1/sqrt(2) */ + +#define GLM_Ef ((float)GLM_E) +#define GLM_LOG2Ef ((float)GLM_LOG2E) +#define GLM_LOG10Ef ((float)GLM_LOG10E) +#define GLM_LN2f ((float)GLM_LN2) +#define GLM_LN10f ((float)GLM_LN10) +#define GLM_PIf ((float)GLM_PI) +#define GLM_PI_2f ((float)GLM_PI_2) +#define GLM_PI_4f ((float)GLM_PI_4) +#define GLM_1_PIf ((float)GLM_1_PI) +#define GLM_2_PIf ((float)GLM_2_PI) +#define GLM_2_SQRTPIf ((float)GLM_2_SQRTPI) +#define GLM_SQRT2f ((float)GLM_SQRT2) +#define GLM_SQRT1_2f ((float)GLM_SQRT1_2) + +#define FILE_FLAG_IGNMISS 0x01 +#define FILE_FLAG_IGNEMPTY 0x02 +#define FILE_FLAG_STRING 0x04 + +#define TEX_FLAG_FLIP 0x01 +#define TEX_FLAG_FILTER 0x02 +#define TEX_FLAG_MIPMAP 0x04 +#define TEX_FLAG_NMIPMAP 0x08 + +#define WIN_BORDER 2 +#define WIN_TITLEBAR 20 + +#define WIN_MINX (WIN_BORDER * 2) +#define WIN_MINY (WIN_TITLEBAR + WIN_BORDER * 2) + +#define SHD_FLAG_UL 0x01 +#define SHD_FLAG_CL 0x02 +#define SHD_FLAG_BL 0x04 +#define SHD_FLAG_FD 0x08 + +#define SHD_TRACE_SIZE 1024 +#define ERR_LOG_SIZE 1024 + +#define CCMD_UNLIMITED 65535 + +#define GUI_SCROLL_LN 3 + +#define KEY_BINDS 20 + +#define CHUNK_SIZE 32 +#define CHUNK_POLY_GROW 32 +// #define CHUNK_LIGHT_WIDTH (CHUNK_SIZE * 2) +// #define CHUNK_LIGHT_BORDER 2 +// #define CHUNK_LIGHT_SIZE (CHUNK_LIGHT_WIDTH + (CHUNK_LIGHT_BORDER * 2)) + +#define WORLD_CLAMP_XZ 0x01 +#define WORLD_CLAMP_Y 0x02 + +#define MID_DEFTEMPO 500000 + +#define BANK_KEEP 0x01 +#define BANK_UNKN 0x02 +#define BANK_PBRANGE 2.0 + +#define RSM_FRAC 10 + +#define WAV_HDRSIZE 44 + +#define BNK_IDX_MELO_ONLY 25 +#define BNK_IDX_DRUM_ONLY 30 + +#define SND_VOLUMES 4 + +#define SND_LOGX 8 + +#define MOUSE_BTNS 8 + +// #define GUI_REFRESH 0xfd +#define GUI_INVALID 0xff + +#define UNI_MAX_PAGES 4352 + +#define LIGHT_SSIZE (sizeof(float) * 20) + +// strings, characters, ... +#define COL_RESET "\x01" +#define COL_AUX1 "\x02" +#define COL_AUX2 "\x03" +#define COL_AUX3 "\x04" +#define COL_AUX4 "\x05" +#define COL_AUX5 "\x06" +#define COL_AUX6 "\x07" +#define COL_AUX7 "\x08" +#define COL_AUX8 "\x09" +#define STR_NLN "\x0a" +#define FMT_RESET "\x0b" +#define FMT_ULINE "\x0c" +#define FMT_CLINE "\x0d" +#define FMT_BLINK "\x0e" +#define FMT_FADE "\x0f" +#define COL_BLACK "\x10" +#define COL_DGRAY "\x11" +#define COL_GRAY "\x12" +#define COL_LGRAY "\x13" +#define COL_WHITE "\x14" +#define COL_RED "\x15" +#define COL_GREEN "\x16" +#define COL_BLUE "\x17" +#define COL_YELLOW "\x18" +#define COL_MAGENTA "\x19" +#define COL_CYAN "\x1a" +#define COL_VIOLET "\x1b" +#define COL_ORANGE "\x1c" +#define COL_CRIMSON "\x1d" +#define COL_MIDNIGHT "\x1e" +#define COL_NEON "\x1f" +#define STR_SPC "\x20" +#define STR_UNK "\x7f" +#define STR_WSPC "\u3000" + +#define CHR_CRESET 0x01 +#define CHR_COLORS2 0x02 +#define CHR_COLORE2 0x09 +#define CHR_NLN 0x0a +#define CHR_FRESET 0x0b +#define CHR_ULINE 0x0c +#define CHR_CLINE 0x0d +#define CHR_BLINK 0x0e +#define CHR_FADE 0x0f +#define CHR_COLORS1 0x10 +#define CHR_COLORE1 0x1f +#define CHR_SPC 0x20 +#define CHR_UNK 0x7f +#define CHR_WSPC 0x3000 + +#define LOG_SYS "SYS" +#define LOG_MEM "MEM" +#define LOG_GFX "OGL" +#define LOG_IO "FIO" +#define LOG_CON "CON" +#define LOG_TIC "TIC" +#define LOG_SND "SND" +#define LOG_X11 "X11" +#define LOG_GLX "GLX" + +#define DIR_SCREENSHOTS "screenshots" +#define DIR_BANKS "banks" +#define DIR_SHADERS "shaders" + +#define SHD_VERSION "450 core" + +#define SYM_IMP "\U0001f47f" +#define SYM_BLKHEART "\U0001f5a4" +#define SYM_DEMON "\U0001f608" +#define SYM_HORNS "\U0001f918" + +#define SYM_PLAY "\U00100000" +#define SYM_STOP "\U00100001" +#define SYM_PAUSE "\U00100002" +#define SYM_NEXT "\U00100003" +#define SYM_PREV "\U00100004" +#define SYM_FFORWARD "\U00100005" +#define SYM_FREVERSE "\U00100006" +#define SYM_CONTINUOS "\U00100007" +#define SYM_REPEAT "\U00100008" +#define SYM_LOOPED "\U00100009" +#define SYM_SHUFFLE "\U0010000a" +#define SYM_PRANDOM "\U0010000b" +#define SYM_JUMPTO "\U0010000c" +#define SYM_FOLDER "\U0010000d" +#define SYM_DELETE "\U0010000e" +#define SYM_SAVEDISK "\U0010000f" +#define SYM_BUG "\U00100010" +#define SYM_INVALID "\U00100011" + +#define KEYSYM_0 1 +#define KEYSYM_1 2 +#define KEYSYM_2 3 +#define KEYSYM_3 4 +#define KEYSYM_4 5 +#define KEYSYM_5 6 +#define KEYSYM_6 7 +#define KEYSYM_7 8 +#define KEYSYM_8 9 +#define KEYSYM_9 10 + +#define KEYSYM_A 11 +#define KEYSYM_B 12 +#define KEYSYM_C 13 +#define KEYSYM_D 14 +#define KEYSYM_E 15 +#define KEYSYM_F 16 +#define KEYSYM_G 17 +#define KEYSYM_H 18 +#define KEYSYM_I 19 +#define KEYSYM_J 20 +#define KEYSYM_K 21 +#define KEYSYM_L 22 +#define KEYSYM_M 23 +#define KEYSYM_N 24 +#define KEYSYM_O 25 +#define KEYSYM_P 26 +#define KEYSYM_Q 27 +#define KEYSYM_R 28 +#define KEYSYM_S 29 +#define KEYSYM_T 30 +#define KEYSYM_U 31 +#define KEYSYM_V 32 +#define KEYSYM_W 33 +#define KEYSYM_X 34 +#define KEYSYM_Y 35 +#define KEYSYM_Z 36 + +#define KEYSYM_F1 37 +#define KEYSYM_F2 38 +#define KEYSYM_F3 39 +#define KEYSYM_F4 40 +#define KEYSYM_F5 41 +#define KEYSYM_F6 42 +#define KEYSYM_F7 43 +#define KEYSYM_F8 44 +#define KEYSYM_F9 45 +#define KEYSYM_F10 46 +#define KEYSYM_F11 47 +#define KEYSYM_F12 48 + +#define KEYSYM_KP_0 49 +#define KEYSYM_KP_1 50 +#define KEYSYM_KP_2 51 +#define KEYSYM_KP_3 52 +#define KEYSYM_KP_4 53 +#define KEYSYM_KP_5 54 +#define KEYSYM_KP_6 55 +#define KEYSYM_KP_7 56 +#define KEYSYM_KP_8 57 +#define KEYSYM_KP_9 58 + +#define KEYSYM_SPACE 59 +#define KEYSYM_CIRCUMFLEX 60 +#define KEYSYM_SHARP_S 61 +#define KEYSYM_ACUTE 62 +#define KEYSYM_UE 63 +#define KEYSYM_PLUS 64 +#define KEYSYM_OE 65 +#define KEYSYM_AE 66 +#define KEYSYM_NUMBER_SIGN 67 +#define KEYSYM_LESS_THAN 68 +#define KEYSYM_COMMA 69 +#define KEYSYM_PERIOD 70 +#define KEYSYM_HYPHEN 71 + +#define KEYSYM_KP_DECIMAL 72 +#define KEYSYM_KP_DIVIDE 73 +#define KEYSYM_KP_MULTIPLY 74 +#define KEYSYM_KP_SUBTRACT 75 +#define KEYSYM_KP_ADD 76 +#define KEYSYM_KP_ENTER 77 +#define KEYSYM_KP_EQUAL 78 + +#define KEYSYM_CAPS_LOCK 79 +#define KEYSYM_SCROLL_LOCK 80 +#define KEYSYM_NUM_LOCK 81 + +#define KEYSYM_ESCAPE 82 +#define KEYSYM_RETURN 83 +#define KEYSYM_TAB 84 +#define KEYSYM_BACKSPACE 85 +#define KEYSYM_INSERT 86 +#define KEYSYM_DELETE 87 +#define KEYSYM_RIGHT 88 +#define KEYSYM_LEFT 89 +#define KEYSYM_DOWN 90 +#define KEYSYM_UP 91 +#define KEYSYM_PAGE_UP 92 +#define KEYSYM_PAGE_DOWN 93 +#define KEYSYM_HOME 94 +#define KEYSYM_END 95 +#define KEYSYM_PRINT_SCREEN 96 +#define KEYSYM_PAUSE 97 +#define KEYSYM_LEFT_SHIFT 98 +#define KEYSYM_LEFT_CONTROL 99 +#define KEYSYM_ALT 100 +#define KEYSYM_LEFT_META 101 +#define KEYSYM_RIGHT_SHIFT 102 +#define KEYSYM_RIGHT_CONTROL 103 +#define KEYSYM_ALT_GRAPH 104 +#define KEYSYM_RIGHT_META 105 +#define KEYSYM_MENU 106 + +#define KEYSYM_FIRST KEYSYM_SPACE +#define KEYSYM_LAST KEYSYM_MENU + +#define KEY_MOUSE_OFFSET (1 + KEYSYM_LAST - KEYSYM_FIRST) + +#define STR_ARG_USAGE "Verwendung" +#define STR_ARG_HELP "Diese Hilfe anzeigen" +#define STR_ARG_OPVEC "Option %s (-%c)" +#define STR_ARG_OPPOS "Option %s (#%d)" +#define STR_ARG_EINT "muss ein ganzzahliger Wert sein" +#define STR_ARG_ERANGE "muss sich im Bereich %d .. %d befinden" +#define STR_ARG_UNKNOWN "Unbekannte Option -%c" +#define STR_ARG_EMORE "Zu viele Argumente, Maximum %d" +#define STR_ARG_ELESS "Nicht genug Argumente, Minimum %d" +#define STR_ARG_EREQARG STR_ARG_OPVEC " benoetigt ein Argument" +#define STR_ARG_EINT_V STR_ARG_OPVEC " " STR_ARG_EINT +#define STR_ARG_EINT_P STR_ARG_OPPOS " " STR_ARG_EINT +#define STR_ARG_ERANGE_V STR_ARG_OPVEC " " STR_ARG_ERANGE +#define STR_ARG_ERANGE_P STR_ARG_OPPOS " " STR_ARG_ERANGE + +#define ARGSTR_COMMANDS "Befehle" +#define ARGSTR_VERSION "Version ausgeben und beenden" +#define ARGSTR_LOGLEVEL "Ausgabestufe" +#define ARGSTR_GLDEBUG "OpenGL-Debugging" +#define ARGSTR_NOSOUND "Tonausgabe deaktivieren" +#define ARGSTR_CONFIG "Konfigurationsdatei" +#define ARGSTR_LANG "Sprachdatei" + +#define SYS_BUILD "D" STRINGIZE(BUILD_ID) +#define SYS_PROGRAM SYS_PROGNAME " " SYS_BUILD +#define SYS_PROGRAM_FULL SYS_PROGRAM " [" BUILD_DATE "]" + +// macros +#define SIZE_TEST(t, s) assert((sizeof(t) * 8) == s) + +#define STRINGIZE(s) ISTRINGIZE(s) +#define ISTRINGIZE(s) #s + +#define f_inline static inline __attribute((always_inline)) +#define f_align(x) __attribute((aligned(x))) + +#define LOG_FUNC(func, level) void func(const char *prefix, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_msg(prefix, level, fmt, ap); va_end(ap); } +#define SLOG_FUNC(func, level) void func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); snd_log(level, fmt, ap); va_end(ap); } +#define file_read(d, f) file_read_hdr(d, f, NULL, 0, 0) +#define file_sread(d, f) file_read_hdr((byte**)(d), f, NULL, 0, FILE_FLAG_IGNMISS | FILE_FLAG_STRING) +#define LOAD_SHADER(f, r, s) if(!shd_load(&(gdr.shd_ ## f), DIR_SHADERS "/" STRINGIZE(f) ".vsh", DIR_SHADERS "/" STRINGIZE(f) ".fsh", r, (shd_dfunc*)s)) { loge(LOG_GFX, STR_SHD_ELOAD, STRINGIZE(f)); return 0; } +#define FONT_STRIDE(x, y, c) ((((c & 15) * width + x) + (((c >> 4) & 15) * height + y) * (width << 4)) << 2) + +#define PERF_FIRST() sys.tmr_atime = tmr_time(); +#define PERF_START(s) sys.tmr_ftime = sys.tmr_stime = tmr_time(); sys.perf_pos = s; +#define PERF_SEC(s) sys.tmr_dtime = tmr_time() - sys.tmr_ftime; sys.tmr_ftime = tmr_time(); sys.tmr_total[sys.perf_pos] += sys.tmr_profile[sys.perf_swap + sys.perf_pos] = sys.tmr_dtime; sys.perf_pos = s; +#define PERF_END() sys.tmr_dtime = (sys.tmr_etime = tmr_time()) - sys.tmr_ftime; sys.tmr_total[sys.perf_pos] += sys.tmr_profile[sys.perf_swap + sys.perf_pos] = sys.tmr_dtime; sys.tmr_ttime = sys.tmr_etime - sys.tmr_stime; sys.tmr_iters += 1; sys.perf_swap = sys.perf_swap ? 0 : PERF_SECTIONS; +#define PERF_LAST() sys.tmr_atime = tmr_time() - sys.tmr_atime; + +#define CLAMP_VALUE(v, a, b) ((v) < (a) ? (a) : ((v) > (b) ? (b) : (v))) +#define MIN_VALUE(a, b) ((a) < (b) ? (a) : (b)) +#define MAX_VALUE(a, b) ((a) > (b) ? (a) : (b)) + +#define VEC3_SET(v, x, y, z) v[0] = x; v[1] = y; v[2] = z; + +#define AABB_OFFSET(bb, x, y, z) bb.x1 += x; bb.x2 += x; bb.y1 += y; bb.y2 += y; bb.z1 += z; bb.z2 += z; +#define AABB_SET(bb, px1, py1, pz1, px2, py2, pz2) bb.x1 = px1; bb.x2 = px2; bb.y1 = py1; bb.y2 = py2; bb.z1 = pz1; bb.z2 = pz2; +#define AABB_PSET(bb, px1, py1, pz1, px2, py2, pz2) bb->x1 = px1; bb->x2 = px2; bb->y1 = py1; bb->y2 = py2; bb->z1 = pz1; bb->z2 = pz2; +#define AABB_COPY(aa, bb) memcpy(&bb, &aa, sizeof(bbox_t)) +#define AABB_PCOPY(aa, bb) memcpy(bb, aa, sizeof(bbox_t)) + +// #define CHUNK_STRIDE(w, x, y, z) (((x) + w->size_x / 2) + w->size_x * (((z) + w->size_z / 2) + (w->size_z * ((y) + w->size_y / 2)))) +// #define CHUNK_OUT(w, x, y, z) ((x) < -(w->size_x / 2) || (y) < -(w->size_y / 2) || (z) < -(w->size_z / 2) || (x) >= ((w->size_x + 1) / 2) || (y) >= ((w->size_y + 1) / 2) || (z) >= ((w->size_z + 1) / 2)) +// #define CHUNK_CLAMP(w, v, c) CLAMP_VALUE(v, -(w->size_ ## c / 2), ((w->size_ ## c + 1) / 2) - 1) + +#define BANK_VELOFUNC(x, n) (1.0-log(1.0+((1.0-pow(x,n))*(M_E-1.0)))) + +/* input: [0, 256), output: [0, 65536] */ +#define OPL_SIN(x) ((int)(sin((x) * M_PI / 512.0) * 65536.0)) + +#define SYS_BASE (sizeof(sys_t) + sizeof(gfx_t) + sizeof(isnd_t) + sizeof(snd_t) + sizeof(wcf_t)) + +#define DL_LOPEN(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) + +#define X [0] +#define Y [1] +#define Z [2] + +#define NEG_OFFSET(x, d) (x < 0 ? ((x / d) - 1) : (x / d)) + +#define sys_assert(expr) if(!(expr)){sys_panic("ASSERTION_FAILURE", "Falsch: " STRINGIZE(expr));} + +#define CHUNK_ID(x, y, z) (((ulong)(x & 0xfffff) | (x < 0 ? 0x100000ULL : 0ULL)) | (((ulong)(y & 0xfffff) | (y < 0 ? 0x100000ULL : 0ULL)) << 21) | \ +(((ulong)(z & 0xfffff) | (z < 0 ? 0x100000ULL : 0ULL)) << 42) | 0x8000000000000000) + +// enums +enum mem_category { + MEM_SYSTEM, + MEM_IMAGE, + MEM_FONT, + MEM_DRAW, + MEM_FILE, + MEM_LIGHTS, + MEM_WORLD, + MEM_POLY, + MEM_CHUNK, + MEM_OBJECT, + MEM_OBJMAP, + MEM_MISC +}; + +enum log_level { + LOG_SILENT, LOG_USER, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_PERF, LOG_DEBUG, LOG_TRACE +}; + +enum gui_type { + GUI_LABEL, + GUI_BUTTON, + GUI_TOGGLE_OFF, + GUI_TOGGLE_ON, + GUI_ENUM, + GUI_SLIDER, + GUI_SLIDER_HANDLE, + GUI_DROPDOWN, + GUI_DROPDOWN_HANDLE, + GUI_FIELD, + GUI_CUSTOM +}; + +enum key_event { + KEY_RELEASE, + KEY_PRESS, + KEY_REPEAT +}; + +enum key_bind { + KEY_QUIT, + KEY_SCREENSHOT, + KEY_CONSOLE, + KEY_SHOW, + KEY_FULLSCREEN, + KEY_SYNC, + KEY_MENU, + KEY_DOWN, + KEY_UP, + KEY_FORWARD, + KEY_BACKWARD, + KEY_LEFT, + KEY_RIGHT, + KEY_FAST, + KEY_NOCLIP, + KEY_FLY, + KEY_ZOOM_IN, + KEY_ZOOM_OUT, + KEY_CAMERA, + KEY_PLAYER +}; + +enum mouse_button { + MOUSE_LEFT = 1, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_BTN_X, + MOUSE_BTN_Y, + MOUSE_BTN_A, + MOUSE_BTN_B, + MOUSE_BTN_C, + SCROLL_UP, + SCROLL_DOWN, + SCROLL_LEFT, + SCROLL_RIGHT +}; + +enum perf_section { + PERF_TIMING, + PERF_INPUT, + PERF_TICK, + PERF_UPDATE, + PERF_RENDER, + PERF_GUI, + PERF_REST, + PERF_SWAP, + PERF_EVENTS, + PERF_WAIT +}; + +enum cvar_type { + CVAR_BOOL, + CVAR_INT, + CVAR_FLOAT, + CVAR_ENUM, + CVAR_COLOR, + CVAR_COLOR_ALPHA, + CVAR_KEY +}; + +enum cvar_category { + CVAR_SYSTEM, + CVAR_WINDOW, + CVAR_GUI, + CVAR_STYLE, + CVAR_RENDER, + CVAR_BIND, + CVAR_CONSOLE, + CVAR_PHYSICS, + CVAR_WORLD, + CVAR_SOUND +}; + +enum move_dir { + DIR_LEFT, + DIR_RIGHT, + DIR_DOWN, + DIR_UP, + DIR_BACKWARD, + DIR_FORWARD +}; + +enum cardinal_dir { + FACE_WEST, + FACE_EAST, + FACE_DOWN, + FACE_UP, + FACE_NORTH, + FACE_SOUTH +}; + +enum mipmap_type { + MIP_NONE, + MIP_NEAREST, + MIP_LINEAR +}; + +enum geom_type { + GEOM_TRI, + GEOM_QUAD, + GEOM_PLANE +}; + +enum mid_event { + midev_noteoff = 0x80, + midev_noteon = 0x90, + midev_aftertouch = 0xa0, + midev_control = 0xb0, + midev_progchg = 0xc0, + midev_chnpressure = 0xd0, + midev_pitchbend = 0xe0, + midev_sysex = 0xf0, + midev_songpos = 0xf2, + midev_songsel = 0xf3, + midev_tunereq = 0xf6, + midev_endsysex = 0xf7, + midev_clock = 0xf8, + midev_start = 0xfa, + midev_continue = 0xfb, + midev_stop = 0xfc, + midev_actsense = 0xfe, + midev_meta = 0xff +}; + +enum mid_meta { + midmt_seqnum = 0x00, // nn nn + midmt_text = 0x01, // text... + midmt_copyright = 0x02, // text... + midmt_trackname = 0x03, // text... + midmt_instrname = 0x04, // text... + midmt_lyric = 0x05, // text... + midmt_marker = 0x06, // text... + midmt_cuepoint = 0x07, // text... + midmt_chnprefix = 0x20, // cc + midmt_endtrack = 0x2f, // + midmt_tempo = 0x51, // tt tt tt + midmt_smpte = 0x54, // hr mn se fr ff + midmt_timesig = 0x58, // nn dd cc bb + midmt_keysig = 0x59, // sf mi + midmt_seqspec = 0x7f // data... +}; + +enum bank_op { + b_op2, + b_op4, + b_op22, + b_op0 +}; + +enum opl3_op { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2 +}; + +enum envelope_gen_num { + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +enum snd_volume { + SND_VOL_MASTER, + SND_VOL_MUSIC, + SND_VOL_SFX, + SND_VOL_GUI +}; + +// types + +typedef int ivec2[2]; +typedef int ivec3[3]; +typedef int ivec4[4]; +typedef float vec2[2]; +typedef float vec3[3]; +typedef f_align(16) float vec4[4]; +typedef f_align(16) vec2 mat2[2]; +typedef vec3 mat3[3]; +typedef f_align(16) vec4 mat4[4]; + +// arrays +static const char *mem_types[] = {"system", "textures", "fonts", "rendering", "files", "lighting", "worlds", "polys", "chunks", "objects", "mapping", "misc"}; + +static const char *log_levels[] = {"UNK?", "USER", "ERR!", "WARN", "INFO", "PERF", "DBG#", "TRC#"}; +static const char *log_elevels[] = {"silent", "user", "error", "warn", "info", "perf", "debug", "trace"}; +static const char *log_colors[] = {"", COL_WHITE, COL_RED, COL_YELLOW, COL_BLUE, COL_CYAN, COL_MAGENTA, COL_VIOLET}; + +static const char *perf_sections[] = {"Timing", "Input", "Tick", "Update", "Render", "Gui", "Rest", "Swap", "Events", "Wait"}; + +static const char *cvar_types[] = {COL_MAGENTA"bool", COL_GREEN"int", COL_YELLOW"float", COL_CYAN"enum", COL_LGRAY"color", COL_GRAY"color32", COL_VIOLET"key"}; +static const char *cvar_categories[] = {COL_RED"system", COL_BLUE"window", COL_GREEN"gui", COL_VIOLET"style", COL_NEON"render", COL_ORANGE"bind", COL_YELLOW"console", COL_CYAN"physics", COL_MAGENTA"world", + COL_CRIMSON"sound"}; + +// BLACK DGRAY GRAY LGRAY WHITE RED GREEN BLUE YELLOW MAGENTA CYAN VIOLET ORANGE CRIMSON MIDNIGHT NEON +static const uint text_colors[] = {0x000000, 0x585858, 0x808080, 0xc0c0c0, 0xffffff, 0xcf0000, 0x00cf00, 0x0000cf, 0xbfbf00, 0xbf00bf, 0x00bfbf, 0x6000ff, 0xff7000, 0x601010, 0x000080, 0x80c0f0}; +static const uint aux_colors[] = {0x80f0c0, 0x3000c0, 0x56250b, 0x80ff00, 0x202020, 0x606060, 0xa0a0a0, 0xe0e0e0}; + +static const char dc32_ascii[] = { // 0x1f -> AUX1 + 0, ' ', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'G', // $00, $80 + 'H', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'V', 'W', 'X', 'Z', 0 // $10, $90 +}; + +static const char dc32_aux1[] = { // 0x01 -> lc / UC, 0x1f -> AUX2 + 0, 0, '!', '"', '#', '$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', 'F', // $20, $A0 + ':', '1', 'J', ';', '=', '?', '0', '\\', 'Q', '^', '_', 'U', '|', '~', 'Y', 0 // $30, $B0 +}; + +static const char dc32_aux2[] = { // 0x0f -> [0x10 ~ 0x1f ... 0x0f -> UTF] | [0x01 ~ 0x0e -> AUX3], 0x0c -> STR_WSPC + 0, 0x01, '(', ')', '<', '>', '@', '[', ']', '`', '{', '}', 0, 0x7f, 0x0a, 0, // $40 + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f // $50 +}; + +static const char dc32_aux3[] = { // 0x01 -> SYM_DEMON + 0, 0, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0 // $60 +}; + +static const char plr_wheel[] = { '/', '-', '\\', '|'}; + +static const byte ascii_dc32[] = { + 0x00, 0x41, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x4e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x01, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x42, 0x43, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x36, 0x31, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x30, 0x33, 0x44, 0x34, 0x45, 0x35, + 0x46, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x2f, 0x0f, 0x10, 0x11, 0x32, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x38, 0x18, 0x19, 0x1a, 0x3b, 0x1b, 0x1c, 0x1d, 0x3e, 0x1e, 0x47, 0x37, 0x48, 0x39, 0x3a, + 0x49, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0xaf, 0x8f, 0x90, 0x91, 0xb2, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0xb8, 0x98, 0x99, 0x9a, 0xbb, 0x9b, 0x9c, 0x9d, 0xbe, 0x9e, 0x4a, 0x3c, 0x4b, 0x3d, 0x4d +}; + +static const byte ascii_dc32s[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x16, 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0, 0, 0, 0, 0, 0, + 0, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x1b, 0x0f, 0x10, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x16, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0, 0, 0, 0, 0, + 0, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x1b, 0x0f, 0x10, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x16, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0, 0, 0, 0, 0 +}; + +static const float vert_quad[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + +/* +static const float vert_box[] = { +// x y z nx ny nz u v + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // -Z + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // +Z + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // -X + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // +X + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // -Y + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // +Y + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f +}; +*/ + +static const char *mipmap_etype[] = {"off", "nearest", "linear"}; + +static const char *snd_eformat[] = {"s16le", "s32le"}; + +static const char *snd_edevice[] = {"default", "null", "jack", "pulse", "hw", "plughw", "none"}; + +static const char *con_epositions[] = {"top", "bottom"}; + +static const char *mid_extensions[] = {"mid", "kar", "midi"}; + +static const char *snd_cvar_volume[] = {"snd_volume_master", "snd_volume_music", "snd_volume_sfx", "snd_volume_gui"}; + +static const char *key_names[] = { + "space", "^", "ß", "´", "ü", "+", "ö", "ä", "#", "<", ",", ".", "-", + "kp.", "kp/", "kp*", "kp-", "kp+", "enter", "kp=", + "caps", "scroll", "num", + "esc", "return", "tab", "bksp", "ins", "del", "right", "left", "down", "up", "pgup", "pgdn", "home", "end", "print", "pause", + "lshift", "lctrl", "alt", "lmeta", "rshift", "rctrl", "altgr", "rmeta", "menu", + "lmb", "rmb", "mmb", "xmb", "ymb", "m6", "m7", "m8", "scrup", "scrdn", "scrl", "scrr" +}; + +static const char *snd_banknames[] = { + "dmx_dmx", "dmx_doom1", "dmx_doom2", "dmx_raptor", "dmx_strife", "wolfinstein", "std_o3", "std_sb", "hmi_144", "cyberpuck", + "d3dtimbr", "d3dtimbr_mod", "gmoconel", "gmopl_mod", "swtimbr", "tmb_default", "wallence", "fat2", "fat4", "op2x2", "wallace", + "earthsieg", "warcraft", "nemesis", "bank49", + + "jv_2op", "gmopl", "mt32", "insmaker_std", "bank53", + + "drumopl" +}; + +static const char *snd_bankfiles[] = { + DIR_BANKS"/dmx_dmx.op2", DIR_BANKS"/dmx_doom1.op2", DIR_BANKS"/dmx_doom2.op2", DIR_BANKS"/dmx_raptor.op2", + DIR_BANKS"/dmx_strife.op2", DIR_BANKS"/wolfinstein.op2", DIR_BANKS"/std_o3.skb", DIR_BANKS"/std_sb.skb", + DIR_BANKS"/hmi_144.skb", DIR_BANKS"/cyberpuck.tmb", DIR_BANKS"/d3dtimbr.tmb", DIR_BANKS"/d3dtimbr_mod.tmb", + DIR_BANKS"/gmoconel.tmb", DIR_BANKS"/gmopl_mod.tmb", DIR_BANKS"/swtimbr.tmb", DIR_BANKS"/tmb_default.tmb", + DIR_BANKS"/wallence.op3", DIR_BANKS"/fat2.op3", DIR_BANKS"/fat4.op3", DIR_BANKS"/op2x2.op3", + DIR_BANKS"/wallace.op3", DIR_BANKS"/earthsieg.ad", DIR_BANKS"/warcraft.ad", DIR_BANKS"/nemesis.opl", + DIR_BANKS"/bank49.opl", DIR_BANKS"/jv_2op.op3", DIR_BANKS"/gmopl.ibk", DIR_BANKS"/mt32.ibk", + DIR_BANKS"/insmaker_std.bnk", DIR_BANKS"/bank53.opl", DIR_BANKS"/drumopl.tmb" +}; + +static const char *mid_hdr = "MThd"; +static const char *mid_trk = "MTrk"; + +static const uint bank_notes[] = { + 8175, 8661, 9177, 9722, 10300, 10913, 11562, 12249, + 12978, 13750, 14567, 15433, 16351, 17323, 18354, 19445, + 20601, 21826, 23124, 24499, 25956, 27500, 29135, 30867, + 32703, 34647, 36708, 38890, 41203, 43653, 46249, 48999, + 51913, 55000, 58270, 61735, 65406, 69295, 73416, 77781, + 82406, 87307, 92498, 97998, 103826, 110000, 116540, 123470, + 130812, 138591, 146832, 155563, 164813, 174614, 184997, 195997, + 207652, 220000, 233081, 246941, 261625, 277182, 293664, 311126, + 329627, 349228, 369994, 391995, 415304, 440000, 466163, 493883, + 523251, 554365, 587329, 622253, 659255, 698456, 739988, 783990, + 830609, 880000, 932327, 987766, 1046502, 1108730, 1174659, 1244507, + 1318510, 1396912, 1479977, 1567981, 1661218, 1760000, 1864655, 1975533, + 2093004, 2217461, 2349318, 2489015, 2637020, 2793825, 2959955, 3135963, + 3322437, 3520000, 3729310, 3951066, 4186009, 4434922, 4698636, 4978031, + 5274040, 5587651, 5919910, 6271926, 6644875, 7040000, 7458620, 7902132, + 8372018, 8869844, 9397272, 9956063, 10548081, 11175303, 11839821, 12543853 +}; + +static const uint opl3_maxfreq[] = { + 48503, + 97006, + 194013, + 388026, + 776053, + 1552107, + 3104215, + 6208431 +}; + +static const ushort logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +static const ushort exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +static const byte op_mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +static const byte kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const byte kslshift[4] = { + 8, 1, 2, 0 +}; + +static const byte eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const char *wav_hdr = "RIFF"; +static const char *wav_hdr_fmt = "fmt "; +static const char *wav_hdr_data = "data"; +static const char *wav_format = "WAVE"; + +static const byte rng_table[256] = { + 0x95, 0xbc, 0x64, 0x99, 0xc3, 0x31, 0x68, 0xc5, 0xba, 0xe2, 0x67, 0x66, 0xd1, 0x14, 0xce, 0xd7, + 0x4c, 0xdb, 0x78, 0x41, 0xb9, 0xf3, 0x38, 0x55, 0x71, 0x1b, 0x5f, 0x83, 0x3c, 0xaf, 0xa7, 0x9c, + 0x16, 0x9d, 0xe0, 0x54, 0x93, 0x5d, 0xaa, 0x2a, 0xef, 0xff, 0x6b, 0x4a, 0xfa, 0x39, 0x08, 0xcb, + 0x97, 0x8b, 0x86, 0x2c, 0x60, 0xa0, 0xac, 0x4e, 0x7a, 0xc9, 0xd3, 0x1e, 0xee, 0xbb, 0x9b, 0x09, + 0xca, 0x33, 0x9e, 0x0d, 0xbe, 0x75, 0x9a, 0x29, 0xf7, 0x88, 0xdd, 0x5c, 0x7e, 0xa3, 0x58, 0x94, + 0x00, 0x74, 0xad, 0x17, 0x07, 0x84, 0x7c, 0x63, 0x76, 0xb6, 0x1c, 0xa1, 0xb8, 0x79, 0xf0, 0xf9, + 0xb3, 0x98, 0x3e, 0xeb, 0x8a, 0x5e, 0x6e, 0x6a, 0x70, 0x7d, 0xc8, 0xa4, 0x01, 0x42, 0x2f, 0xd9, + 0x2b, 0xf1, 0x90, 0x44, 0xea, 0xa6, 0xcd, 0x72, 0x18, 0x91, 0x22, 0xf4, 0x03, 0x2d, 0xe1, 0xb0, + 0xda, 0xb1, 0x6d, 0xe6, 0x37, 0xc0, 0xfb, 0x65, 0x8d, 0xe9, 0xfe, 0x28, 0xfc, 0x47, 0xd2, 0xe4, + 0xdf, 0x19, 0x4d, 0x8e, 0x80, 0x56, 0x1d, 0x21, 0xc1, 0x82, 0xc2, 0x27, 0x7b, 0x24, 0xa8, 0x8c, + 0x11, 0x43, 0xd8, 0xdc, 0xb7, 0x48, 0x85, 0x30, 0xf5, 0x0e, 0xd4, 0x89, 0x4b, 0x0c, 0x45, 0x87, + 0x04, 0x0b, 0xfd, 0x53, 0xd5, 0xf6, 0x06, 0xcf, 0xf2, 0xe7, 0x51, 0xe5, 0x50, 0x25, 0xed, 0x12, + 0x4f, 0x20, 0x96, 0x81, 0xae, 0x8f, 0xd0, 0x36, 0xc6, 0x3b, 0x1a, 0xb5, 0xa2, 0x13, 0x1f, 0x2e, + 0x0f, 0x34, 0x9f, 0x61, 0x05, 0x0a, 0xbf, 0xc7, 0x10, 0xa9, 0x23, 0x15, 0x5b, 0xf8, 0x6c, 0xd6, + 0xb4, 0xab, 0x49, 0x5a, 0x6f, 0xb2, 0x02, 0x59, 0x92, 0x73, 0xde, 0x3f, 0x3d, 0x46, 0x57, 0xe8, + 0x77, 0xcc, 0xa5, 0x3a, 0x62, 0x69, 0x35, 0xec, 0x26, 0x52, 0x7f, 0xc4, 0xe3, 0x40, 0xbd, 0x32 +}; + +// struct prototypes +typedef struct _table_e table_e; +typedef struct _itable_t itable_t; +typedef struct _list_e list_e; +typedef struct _list_t list_t; +typedef struct _map_t map_t; +typedef struct _smap_t smap_t; +typedef struct _gui_t gui_t; +typedef struct _cvar_t cvar_t; +typedef struct _ccmd_t ccmd_t; +typedef struct _chunk_t chunk_t; +typedef struct _world_t world_t; +typedef struct _geometry_t geometry_t; +typedef struct _bank_voice bank_voice; +typedef struct _bank_key bank_key; +typedef struct _bank_channel bank_channel; +typedef struct _bank_handle bank_handle; +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; +typedef struct _window_t window_t; +typedef struct _monitor_t monitor_t; +typedef struct _cursor_t cursor_t; + +// function types +typedef uint map_hashfunc(void*); +typedef byte map_eqfunc(void*, void*); +typedef void gui_func(gui_t*, int); +typedef void clear_func(void*); +typedef byte clear_sfunc(void*); +typedef void cvar_func(cvar_t*, int); +typedef void ccmd_func(ccmd_t*, const char**, int); +typedef byte (*cvar_parse_func)(const char *, int *); +typedef void (*cvar_fmt_func)(char *, int); +typedef void shd_func(); +typedef void shd_dfunc(void*); +typedef short(*envelope_sinfunc)(ushort phase, ushort envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); +typedef float graph_func(void*, float); +typedef float graph2_func(void*, float, float); +typedef byte gui_initfnc(window_t*, int, int, byte); + +// structs +typedef struct { + char spec; + char arg; + int min; + int max; + int *value; + char **str; + const char *name; +} arg_t; + +typedef struct { + uint arg_min; + uint arg_max; + uint arg_count; + arg_t *arg_vec; +} argp_t; + +struct _table_e { + table_e *u_next; + table_e *u_prev; + table_e *f_next; + table_e *f_prev; + // void *value; + uint cid; + uint eid; +}; + +struct _itable_t { + table_e *elems; + void **data; + uint *sizes; + table_e *used; + table_e *free; + uint size; + uint load; + uint chunks; + uint stride; + uint alloc; + uint stored; + byte cat; + byte pad[7]; +}; + +typedef struct { + itable_t *nodes; + uint load; + uint block; + uint stride; + uint size; + uint stored; + byte cat; + byte pad[3]; +} table_t; + +struct _list_e { + list_e *u_next; + list_e *u_prev; + list_e *f_next; + list_e *f_prev; + uint id; + uint pad; +}; + +struct _list_t { + list_e *elems; + list_e *used; + list_e *free; + uint size; + uint stored; +}; + +struct _map_t { + map_hashfunc *hash_func; + map_eqfunc *eq_func; + void **elems; + uint load; + uint stored; + byte cat; + byte pad[7]; +}; + +struct _smap_t { + void *elems[27]; + uint load; + uint stored; + byte cat; + byte pad[7]; +}; + +typedef struct { + uint *id; + const char *path; + ushort width; + ushort height; + byte filter; + byte mipmap; + byte fallback; + byte system; +} tex_t; + +typedef struct { + uint *vao; + uint *vbo; + uint size; + byte stride; + byte groups; + byte dynamic; + byte system; +} buf_t; + +typedef struct { + uint *fbo; + uint *tex; + uint *rbo; + window_t *resize; + int xsize; + int ysize; + byte linear; + byte pad[7]; +} fbo_t; + +typedef struct { + const char *vpath; + const char *fpath; + shd_func *init; + shd_dfunc *draw; + uint id; + uint pad; +} shd_t; + +typedef struct { + byte s; + byte t; + byte u; + byte v; +} fchar_t; + +typedef struct { + uint textures[UNI_MAX_PAGES]; + fchar_t *sizes[UNI_MAX_PAGES]; + int xsize; + int ysize; + int xglyph; + int yglyph; +} font_t; + +struct _gui_t { + window_t *window; + gui_func *func; + gui_func *format; + font_t *font; + char *text; + const char *format_text; + void *aux_data; + const char ***format_data; + cvar_t *cv_data; + + uint color_fill_t; + uint color_fill_b; + uint color_brdr_t; + uint color_brdr_b; + uint color_text; + int border; + uint texture; + int font_size; + + int pos_x; + int pos_y; + int size_x; + int size_y; + int text_x; + int text_y; + int tsize_x; + int tsize_y; + + int margin_x1; // slider fill top + int margin_y1; // slider fill bottom + int margin_x2; // slider border top + int margin_y2; // slider border bottom + int sel_start; // slider handle pos + int sel_end; // slider handle width + int sel_drag; // slider border + int capacity; + + int value; + int def; + int min; + int max; + int line_space; + int id; + + byte type; + byte visible; + byte enabled; + byte t_dirty; + byte r_dirty; + byte pad; + byte xbreak; + byte precision; +}; + +typedef struct { + const char *name; + + uint brdr_top; + uint brdr_btm; + uint wbrdr_top; + uint wbrdr_btm; + uint win_top; + uint win_btm; + + uint fill_top; + uint fill_btm; + uint field_top; + uint field_btm; + uint text_label; + uint text_base; + uint text_field; + uint text_win; + + int slider_width; + int border; + + uint bg_top; + uint bg_btm; + uint press_top; + uint press_btm; + uint hover_top; + uint hover_btm; + uint select; + uint cursor; +} style_t; + +typedef struct { + int key; + byte id; + byte pressed; + byte active; + byte last_state; +} bind_t; + +struct _cvar_t { + const char *name; + cvar_func *func; + void *data; + void *aux_data; + int value; + int def; + int min; + int max; + int id; + byte readonly; + byte type; + byte category; + byte startup; +}; + +struct _ccmd_t { + const char *name; + const char *args; + ccmd_func *func; + int id; + ushort min; + ushort max; +}; + +typedef struct { + int width; + int height; + int refresh; + int pad; +} vidmode_t; + +struct _window_t { + window_t* next; + monitor_t* monitor; + cursor_t* cursor; + gui_t *selected; + const char *title; + gui_initfnc *initializer; + world_t *world; + + ulong tmr_scroll; + ulong tmr_leftmb; + + uint fb_gui; + uint fbtex_gui; + + int video_width; + int video_height; + int video_refresh; + int pad1; + int minwidth; + int minheight; + int maxwidth; + int maxheight; + int width; + int height; + int xpos; + int ypos; + int last_cur_x; + int last_cur_y; + int warp_cur_x; + int warp_cur_y; + int virtual_cur_x; + int virtual_cur_y; + int scrollx; + int scrolly; + int min_x; + int min_y; + int max_x; + int max_y; + int n_elems; + int mouse_clickx; + int mouse_clicky; + int offset_x; + int offset_y; + int frame_x; + int frame_y; + int mouse_x; + int mouse_y; + int xsize; + int ysize; + int saved_xpos; + int saved_ypos; + int fb_x; + int fb_y; + + byte resizable; + byte floating; + byte override_redir; + byte iconified; + byte cursor_set; + byte redraw; + byte open; + byte queue; + + byte drag_x; + byte drag_y; + byte focused; + byte mouse; + byte scroll; + byte pad2[3]; + + gui_t elems[GUI_MAX_ELEM]; + char strings[GUI_STR_SIZE*GUI_MAX_ELEM]; + char str_aux[GUI_AUX_STR]; + char buttons[MOUSE_BTNS]; + char keys[KEYSYM_LAST + 1]; + Time key_times[256]; + + GLXWindow window; + Colormap colormap; + Window handle; + Window parent; + XIC ic; +}; + +struct _monitor_t { + window_t* window; + vidmode_t* modes; + + int n_modes; + int index; + + vidmode_t current_mode; + RROutput output; + RRCrtc crtc; + RRMode old_mode; +}; + +struct _cursor_t { + cursor_t* next; + Cursor handle; +}; + +typedef struct { + char *message; + window_t *window; + ulong time; + int offset; + int length; +} conmsg_t; + +typedef struct { + void *buffer; + short command; + short address; + uint param; +} snd_cmd_t; + +typedef struct { + vec3 front; + vec3 up; + vec3 right; + vec3 world_up; + double pos_x; + double pos_y; + double pos_z; + float yaw; + float pitch; + float zoom; + float pad; +} camera_t; + +typedef struct { + double x1; + double y1; + double z1; + double x2; + double y2; + double z2; +} bbox_t; + +typedef struct { + double pos_x; + double pos_y; + double pos_z; + double motion_x; + double motion_y; + double motion_z; + double width; + double height; + + double jump_motion; + double fly_base_speed; + double walk_base_speed; + double air_base_speed; + double fly_speed; + double walk_speed; + double air_speed; + double speed; + + bbox_t box; + double forward; + double strafe; + + double eye; + double rot_yaw; + double step_height; + double fall_distance; + double last_x; + double last_y; + double last_z; + float yaw; + float pitch; + + int fly_timer; + int jump_ticks; + + byte jump; + byte sprint; + byte sneak; + byte use; + byte blocked; + byte braked; + byte pad[2]; + + byte first_tick; + byte last_jump; + byte flying; + byte sprinting; + byte ground; + byte collided; + byte collided_h; + byte collided_v; + + world_t *world; +} entity_t; + +typedef struct { + shd_t *shader; + uint texture; + float density; + uint spec_color; + float shine; +} material_t; + +typedef struct { + uint color; + float amb_fact; + float dif_fact; + float range_xz; + float range_y; + float constant; + float linear; + float quadratic; +} light_b; + +typedef struct { + vec3 pos; + vec2 coord; +} vertex_b; + +typedef struct { + vertex_b vertices[3]; + float pad; + material_t *material; +} polygon_b; + +typedef struct { + vec3 pos; + float pad1; + vec3 ambient; + float constant; + vec3 diffuse; + float linear; + vec3 specular; + float quadratic; + vec3 range; + float pad2; + ulong id; +} light_t; + +typedef struct { + vec3 pos; + vec3 normal; + vec2 coord; +} vertex_t; + +typedef struct { + vertex_t vertices[3]; + material_t *material; + uint next; + uint pad; +} polygon_t; + +struct _geometry_t { + geometry_t **global; + bbox_t collision; + ulong id; + uint polys; + int chunk_x; + int chunk_y; + int chunk_z; +}; + +typedef struct { + material_t *material; + int size; + int offset; +} draw_t; + +typedef struct { + uint vao; + uint vbo; + draw_t *draws; + buf_t *bind; + int size; + vec3 position; +} drawlist_t; + +struct _chunk_t { + table_t obj_table; + table_t light_table; + list_t poly_list; + bbox_t box; + world_t *world; + polygon_t *poly_pool; + drawlist_t *drawlist; + chunk_t *next_dirty; + int x; + int y; + int z; + int size; + byte dirty; + byte pad[7]; +}; + +struct _world_t { + chunk_t *dirty_first; + chunk_t *dirty_queue; + entity_t *entity; + shd_t *shader; + fbo_t *post_buf; + + ulong pointed; + ulong obj_id; + double gravity; + double friction; + + vec3 look_pos; + + uint fb_post; + uint fbtex_post; + uint rbo_post; + uint light_buf; + int s_lights; + int n_chunks; + int n_objects; + int n_lights; + int last_cx; + int last_cy; + int last_cz; + + byte light_dirty; + byte chunks_dirty; + byte noclip; + byte camonly; + + table_t global_geom; + table_t chunk_table; + map_t chunk_map; + map_t obj_map; + camera_t camera; + entity_t entity_base; +}; + +typedef struct { + FILE *fd; + const char *filename; + byte buffer[8]; + uint samplerate; + uint samples; + ushort channels; + ushort bytes; + ushort pad[2]; +} wav_handle; + +typedef struct { + byte *buffer; + uint size; + uint pos; + byte status; + byte ending; + uint wait; + ushort trknum; +} mid_track; + +typedef struct { + mid_track *track; + ushort tracks; + uint tpqn; + uint uspb; + uint ticktime; +} mid_handle; + +typedef struct { + byte tremolo; + byte vibrato; + byte sustaining; + byte ksr; + byte mult; + byte ksl; + byte level; + byte attack; + byte decay; + byte sustain; + byte release; + byte waveform; +} bank_operator; + +typedef struct { + bank_operator ops[2]; + short detune; + short offset; + byte feedback; + byte am; +} bank_pair; + +typedef struct { + bank_pair channels[2]; + byte op; + byte fixed; + byte percnum; + char name[32]; +} bank_instr; + +struct _bank_voice { + bank_channel *channel; + opl3_channel *opl; + byte note; + byte op; + short detune; + bank_voice *pair; +}; + +struct _bank_key { + byte note; + byte velocity; + bank_voice *voice; +}; + +struct _bank_channel { + bank_handle *bank; + bank_key keys[BANK_MAX]; + byte notes[128]; + byte keyindex; + byte active; + ushort pbank; + char pan; + byte volume; + short pitch; + byte program; + bank_instr *instr; + byte ch_num; +}; + +struct _bank_handle { + bank_channel channel[16]; + bank_voice *voices; + bank_instr *bdata; + ushort voiceindex; + ushort v_avail; + ushort v_used; + byte flags; + char velo_func; +}; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + short out; + short fbmod; + short *mod; + short prout; + ushort eg_rout; + ushort eg_out; + // byte eg_inc; + byte eg_gen; + // byte eg_rate; + byte eg_ksl; + byte *trem; + byte reg_vib; + byte reg_type; + byte reg_ksr; + byte reg_mult; + byte reg_ksl; + byte reg_tl; + byte reg_ar; + byte reg_dr; + byte reg_sl; + byte reg_rr; + byte reg_wf; + byte key; + byte detrigger; + byte retrigger; + uint pg_reset; + uint pg_phase; + ushort pg_phase_out; + byte slot_num; +}; + +struct _opl3_channel { + opl3_slot *slots[2]; + opl3_channel *pair; + opl3_chip *chip; + short *out[4]; + + int level[2]; + + byte chtype; + ushort f_num; + byte block; + byte fb; + byte con; + byte alg; + byte ksv; + byte ch_num; +}; + +struct _opl3_chip { + opl3_channel *channel; + opl3_slot *slot; + ushort timer; + ulong eg_timer; + byte eg_timerrem; + byte eg_state; + byte eg_add; + byte nts; + byte vibpos; + byte vibshift; + byte tremolo; + byte tremolopos; + byte tremoloshift; + byte n_voices; + uint noise; + short zeromod; + int mixbuff[4]; + + int rateratio; + int samplecnt; + short oldsamples[2]; + short samples[2]; +}; + +typedef struct { + window_t *console; + const char *cfg_file; + style_t *style_def; + window_t *keywin; + window_t *win_full; + + ulong mem_pool_sizes[MEM_CATS]; + ulong mem_pool_blocks[MEM_CATS]; + + ulong mem_alloc; + ulong mem_blocks; + ulong mem_peak; + ulong sync_limit; + ulong tmr_timer; + ulong tmr_start; + ulong tmr_current; + ulong tmr_last; + + ulong tmr_delta; + ulong tmr_update; + ulong tmr_frames; + ulong tmr_iters; + ulong tmr_stime; + ulong tmr_etime; + ulong tmr_ttime; + ulong tmr_ftime; + + ulong tmr_dtime; + ulong tmr_atime; + ulong tick_torun; + ulong tick_done; + ulong tick_total; + ulong tick_time; + ulong tick_stime; + ulong tick_ftime; + + ulong tick_ttime; + ulong tick_update; + + ulong rng_uniq; + + double tick_fraction; + + ulong tmr_profile[PERF_SECTIONS*2]; + ulong tmr_total[PERF_SECTIONS]; + + float tmr_framerate; + float tick_tickrate; + float fdelta; + float sensitivity; + float speed; + + int n_cvars; + int key_release; + + int log_len; + int n_modes; + int vid_mode; + int n_ccmds; + int tick_target; + int tick_timeout; + + int win_xfull; + int win_yfull; + int win_refresh; + int font_pages; + int tick_frame; + int dclick_delay; + + int hud_pos; + int hud_size; + int hud_fadeout; + int hud_len; + + byte hud_overlay; + byte hud_bottom; + byte hud_opacity; + + byte mouse_first; + byte input; + byte mouse_bind; + byte nograb; + byte cfg_dirty; + byte ticked; + byte win_vsync; + byte sync_limited; + byte interrupted; + + byte screenshot; + byte draw_fps; + byte draw_debug; + byte gl_flush; + byte log_level; + byte log_set; + byte perf_pos; + byte perf_swap; + + byte keywait; + byte con_timestamps; + byte con_autoscroll; + + bind_t binds[KEY_MAXBINDS]; + font_t font; + cvar_t cvars[CVAR_MAX]; + ccmd_t ccmds[CCMD_MAX]; + smap_t cvar_map; + smap_t ccmd_map; + vidmode_t vid_modes[VID_MODES]; + conmsg_t hud_msgs[HUD_MSGS]; + bbox_t collision_list[COLL_MAX]; + + style_t style; + table_t themes; + + const char *str_format[LOCALE_SIZE]; + char log_buf[LOG_BUF]; + char work_buf[WORK_BUF]; + char con_buf[CON_BUF]; + char con_line[CON_LINE]; + char con_tok[CON_LINE]; + char *con_arg[CON_LINE / 2]; + char hud_buf[HUD_BUF]; + char str_table[LOCALE_SIZE*LOCALE_ELEM]; + char vid_mode_list[VID_MODES*VID_MODE_STR]; +} sys_t; + +typedef struct { + shd_t *shd_rect; + shd_t *shd_text; + shd_t *shd_blit; + shd_t *shd_world; + shd_t *shd_vis; + shd_t *shd_grid; + + uint aux_colors[8]; + + uint shader; + uint vbo_quad; + uint vao_quad; + uint tex_fallback; + uint tex_logo; + uint tex_crosshair; + + float clear_r; + float clear_g; + float clear_b; + float clear_a; + float clip_near; + float clip_far; + float aniso_max; + float tex_aniso; + float fov; + float light_blend; + float light_dist_vert; + float light_dist_cam; + + float ambient_x; + float ambient_y; + float ambient_z; + + byte tex_miptype; + byte tex_miplevel; + byte tex_filter; + byte wireframe; + byte post; + // byte aniso_avail; + + ulong tex_mem; + ulong tex_peak; + ulong buf_mem; + ulong buf_peak; + ulong fb_mem; + ulong fb_peak; + + int light_dist_chunk; + int ldist_chunk_xz; + int ldist_chunk_y; + int light_max; + int tex_loaded; + int shd_loaded; + int buf_loaded; + int fb_loaded; + int lists_drawn; + int tris_drawn; + + table_t textures; + table_t buffers; + table_t shaders; + table_t framebufs; + table_t drawlists; + table_t materials; + + char shd_include[SHD_INCLUDE]; +} gfx_t; + +typedef struct { + pthread_t thread; + pthread_mutex_t lock; + pthread_mutex_t log_lock; + ulong mid_time; + uint cmd_push; + uint cmd_queue; + uint log_push; + uint log_queue; + uint log_pop; + uint query; + uint mid_tick; + byte waiting; + byte disabled; + byte pad1[2]; + snd_cmd_t cmds[SND_QUEUE]; + char logs[SND_LOG * SND_LOG_LEN]; + + byte interrupt; + byte playing; + byte nowait; + byte paused; + byte stopped; + byte log_debug; + byte capture; + ushort volumes[SND_VOLUMES]; + wav_handle wav; + mid_handle mid; + opl3_chip *chip; + bank_handle *bank; + bank_instr ibank[256]; +} isnd_t; + +typedef struct { + window_t *player; + ulong mid_poll; + char **mid_queue; + char *mid_list; + byte mid_nowait; + byte mid_repeat; + byte mid_bank; + int mid_queued; + int mid_playpos; + int mid_prevpos; + + byte mid_keep; + byte mid_unknown; + byte mid_visual; + int mid_velo; + int mid_voices; + + byte mid_debug; + byte mid_karaoke; + uint mid_klog_pos; + uint mid_klog_offs; + uint mid_info_offs[6]; + char mid_klogger[SND_KLOG * SND_KAR]; + char mid_info[SND_INFO * 6]; +} snd_t; + +typedef struct { + cursor_t* cursor_list; + window_t* window_list; + window_t* current; + window_t* active; + window_t* grab_cur_window; + Display* display; + void* handle; + char* primary_str; + char* clipboard_str; + monitor_t** monitors; + + int n_monitors; + int screen; + int major; + int minor; + int event_base; + int error_base; + int restore_cur_x; + int restore_cur_y; + int error_code; + int depth; + + byte debug; + byte pad1[7]; + + char keycodes[256]; + + Window root; + Window helper_window; + Cursor empty_cursor; + XContext context; + XIM im; + XErrorHandler error_handler; + Visual* visual; + GLXFBConfig native; + GLXContext glx; + + // Window manager atoms + Atom NET_SUPPORTED; + Atom NET_SUPPORTING_WM_CHECK; + Atom WM_PROTOCOLS; + Atom WM_STATE; + Atom WM_DELETE_WINDOW; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; + Atom NET_WM_PID; + Atom NET_WM_PING; + Atom NET_WM_WINDOW_TYPE; + Atom NET_WM_WINDOW_TYPE_NORMAL; + Atom NET_WM_STATE; + Atom NET_WM_STATE_ABOVE; + Atom NET_WM_STATE_FULLSCREEN; + Atom NET_WM_BYPASS_COMPOSITOR; + Atom NET_WM_FULLSCREEN_MONITORS; + Atom NET_WM_WINDOW_OPACITY; + Atom NET_WM_CM_Sx; + Atom NET_CURRENT_DESKTOP; + Atom NET_ACTIVE_WINDOW; + Atom NET_FRAME_EXTENTS; + Atom NET_REQUEST_FRAME_EXTENTS; + Atom MOTIF_WM_HINTS; + + // Xdnd (drag and drop) atoms + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; + + // Selection (clipboard) atoms + Atom TARGETS; + Atom MULTIPLE; + Atom INCR; + Atom CLIPBOARD; + Atom PRIMARY; + Atom CLIPBOARD_MANAGER; + Atom SAVE_TARGETS; + Atom NULL_; + Atom UTF8_STRING; + Atom COMPOUND_STRING; + Atom ATOM_PAIR; + Atom WCF_SELECTION; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXCREATENEWCONTEXTPROC CreateNewContext; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + + byte SGI_swap_control; + byte EXT_swap_control; + byte MESA_swap_control; + byte ARB_multisample; + // byte ARB_create_context_no_error; + byte pad2[4]; + + struct { + byte available; + byte monitor_broken; + void* handle; + int event_base; + int error_base; + int major; + int minor; + PFN_XRRFreeCrtcInfo FreeCrtcInfo; + PFN_XRRFreeOutputInfo FreeOutputInfo; + PFN_XRRFreeScreenResources FreeScreenResources; + PFN_XRRGetCrtcInfo GetCrtcInfo; + PFN_XRRGetOutputInfo GetOutputInfo; + PFN_XRRGetOutputPrimary GetOutputPrimary; + PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent; + PFN_XRRQueryExtension QueryExtension; + PFN_XRRQueryVersion QueryVersion; + PFN_XRRSelectInput SelectInput; + PFN_XRRSetCrtcConfig SetCrtcConfig; + PFN_XRRUpdateConfiguration UpdateConfiguration; + } randr; + + struct { + byte available; + byte detectable; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; + uint group; + } xkb; + + struct { + int version; + Window source; + Atom format; + } xdnd; + + struct { + void* handle; + PFN_XcursorImageCreate ImageCreate; + PFN_XcursorImageDestroy ImageDestroy; + PFN_XcursorImageLoadCursor ImageLoadCursor; + } xcursor; + + struct { + byte available; + void* handle; + int major; + int minor; + PFN_XineramaIsActive IsActive; + PFN_XineramaQueryExtension QueryExtension; + PFN_XineramaQueryScreens QueryScreens; + } xinerama; + + struct { + void* handle; + PFN_XGetXCBConnection GetXCBConnection; + } x11xcb; + + struct { + byte available; + void* handle; + int event_base; + int error_base; + PFN_XF86VidModeQueryExtension QueryExtension; + } vidmode; + + struct { + byte available; + void* handle; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; + PFN_XIQueryVersion QueryVersion; + PFN_XISelectEvents SelectEvents; + } xi; + + struct { + byte available; + void* handle; + int major; + int minor; + int event_base; + int error_base; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; +} wcf_t; + +// global state +sys_t sys; +gfx_t gdr; +isnd_t sgt; +snd_t snd; +wcf_t wcf; diff --git a/vars.h b/vars.h new file mode 100644 index 0000000..ced913e --- /dev/null +++ b/vars.h @@ -0,0 +1,172 @@ + +cvar_add_key("key_quit", KEY_QUIT, KEYSYM_F4); +cvar_add_key("key_screenshot", KEY_SCREENSHOT, KEYSYM_F10); +cvar_add_key("key_console", KEY_CONSOLE, KEYSYM_F1); +cvar_add_key("key_overlay", KEY_SHOW, KEYSYM_F2); +cvar_add_key("key_fullscreen", KEY_FULLSCREEN, KEYSYM_F12); +cvar_add_key("key_sync", KEY_SYNC, KEYSYM_F11); +cvar_add_key("key_menu", KEY_MENU, KEYSYM_ESCAPE); +cvar_add_key("key_down", KEY_DOWN, KEYSYM_LEFT_CONTROL); +cvar_add_key("key_up", KEY_UP, KEYSYM_SPACE); +cvar_add_key("key_forward", KEY_FORWARD, KEYSYM_W); +cvar_add_key("key_backward", KEY_BACKWARD, KEYSYM_S); +cvar_add_key("key_left", KEY_LEFT, KEYSYM_A); +cvar_add_key("key_right", KEY_RIGHT, KEYSYM_D); +cvar_add_key("key_fast", KEY_FAST, KEYSYM_LEFT_SHIFT); +cvar_add_key("key_noclip", KEY_NOCLIP, KEYSYM_N); +cvar_add_key("key_fly", KEY_FLY, KEYSYM_F); +cvar_add_key("key_zoomin", KEY_ZOOM_IN, -SCROLL_UP); +cvar_add_key("key_zoomout", KEY_ZOOM_OUT, -SCROLL_DOWN); +cvar_add_key("key_camera", KEY_CAMERA, KEYSYM_K); +cvar_add_key("key_player", KEY_PLAYER, KEYSYM_F8); + +cvar_add_wint("win_sync", CVAR_WINDOW, cvar_fnc_sync, 0, -1, 16384); +cvar_add_rint("win_pos_x", CVAR_WINDOW, 0x80000000, -65536, 65536); +cvar_add_rint("win_pos_y", CVAR_WINDOW, 0x80000000, -65536, 65536); +cvar_add_rint("win_width", CVAR_WINDOW, 0, 1, 65536); +cvar_add_rint("win_height", CVAR_WINDOW, 0, 1, 65536); +// cvar_add_rint("win_full_width", CVAR_WINDOW, 0, RES_MINX, 65536); +// cvar_add_rint("win_full_height", CVAR_WINDOW, 0, RES_MINY, 65536); +// cvar_add_rint("win_full_refresh", CVAR_WINDOW, 0, 1, 960); +cvar_add_int("gui_dclick_delay", CVAR_GUI, &sys.dclick_delay, 250, 150, 750); +cvar_add_bool("gl_vsync_flush", CVAR_RENDER, &sys.gl_flush, 0); +cvar_add_bool("con_autoscroll", CVAR_CONSOLE, &sys.con_autoscroll, 1); +cvar_add_bool("con_timestamps", CVAR_CONSOLE, &sys.con_timestamps, 0); +cvar_add_bool("con_overlay", CVAR_CONSOLE, &sys.hud_overlay, 1); +cvar_add_benum("con_position", CVAR_CONSOLE, &sys.hud_bottom, con_epositions, 0, 2); +// cvar_add_bool("con_monospace", CVAR_CONSOLE, &sys.hud_monospace, 0); +cvar_add_benum("con_loglevel", CVAR_CONSOLE, &sys.log_level, log_elevels, sys.log_level, 8); +cvar_add_int("con_fadeout", CVAR_CONSOLE, &sys.hud_fadeout, 5000, 100, 60000); +cvar_add_sint("con_size", CVAR_CONSOLE, cvar_fnc_consize, &sys.hud_size, 32, 0, HUD_MSGS, 1); +cvar_add_byte("con_opacity", CVAR_CONSOLE, &sys.hud_opacity, 0x40, 0x00, 0xff); + +cvar_add_acolor("color_select", CVAR_STYLE, &sys.style.select, sys.style_def->select); +cvar_add_acolor("color_hover_t", CVAR_STYLE, &sys.style.hover_top, sys.style_def->hover_top); +cvar_add_acolor("color_press_t", CVAR_STYLE, &sys.style.press_top, sys.style_def->press_top); +cvar_add_color("color_background_t", CVAR_STYLE, &sys.style.bg_top, sys.style_def->bg_top); +cvar_add_color("color_border_top", CVAR_STYLE, &sys.style.brdr_top, sys.style_def->brdr_top); +cvar_add_color("color_wborder_top", CVAR_STYLE, &sys.style.wbrdr_top, sys.style_def->wbrdr_top); + +cvar_add_acolor("color_cursor", CVAR_STYLE, &sys.style.cursor, sys.style_def->cursor); +cvar_add_acolor("color_hover_b", CVAR_STYLE, &sys.style.hover_btm, sys.style_def->hover_btm); +cvar_add_acolor("color_press_b", CVAR_STYLE, &sys.style.press_btm, sys.style_def->press_btm); +cvar_add_color("color_background_b", CVAR_STYLE, &sys.style.bg_btm, sys.style_def->bg_btm); +cvar_add_color("color_border_btm", CVAR_STYLE, &sys.style.brdr_btm, sys.style_def->brdr_btm); +cvar_add_color("color_wborder_btm", CVAR_STYLE, &sys.style.wbrdr_btm, sys.style_def->wbrdr_btm); + +cvar_add_color("color_button_top", CVAR_STYLE, &sys.style.fill_top, sys.style_def->fill_top); +cvar_add_color("color_textbox_top", CVAR_STYLE, &sys.style.field_top, sys.style_def->field_top); +cvar_add_color("color_window_top", CVAR_STYLE, &sys.style.win_top, sys.style_def->win_top); +cvar_add_color("color_label_text", CVAR_STYLE, &sys.style.text_label, sys.style_def->text_label); +cvar_add_color("color_button_text", CVAR_STYLE, &sys.style.text_base, sys.style_def->text_base); +cvar_add_int("border_gui", CVAR_STYLE, &sys.style.border, sys.style_def->border, 0, 2); + +cvar_add_color("color_button_btm", CVAR_STYLE, &sys.style.fill_btm, sys.style_def->fill_btm); +cvar_add_color("color_textbox_btm", CVAR_STYLE, &sys.style.field_btm, sys.style_def->field_btm); +cvar_add_color("color_window_btm", CVAR_STYLE, &sys.style.win_btm, sys.style_def->win_btm); +cvar_add_color("color_textbox_text", CVAR_STYLE, &sys.style.text_field, sys.style_def->text_field); +cvar_add_color("color_window_text", CVAR_STYLE, &sys.style.text_win, sys.style_def->text_win); +cvar_add_int("width_handle", CVAR_STYLE, &sys.style.slider_width, sys.style_def->slider_width, 4, 24); + +/* +cvar_add_acolor("color_select", CVAR_STYLE, &gui.style.select, 0x808080ff); +cvar_add_acolor("color_hover_t", CVAR_STYLE, &gui.style.hover_top, 0x288080ff); +cvar_add_acolor("color_press_t", CVAR_STYLE, &gui.style.press_top, 0x18ffffff); +cvar_add_acolor("color_background_t", CVAR_STYLE, &gui.style.bg_top, 0x8020208f); +cvar_add_color("color_border_top", CVAR_STYLE, &gui.style.brdr_top, 0x000000); +cvar_add_color("color_wborder_top", CVAR_STYLE, &gui.style.wbrdr_top, 0x404040); + +cvar_add_acolor("color_cursor", CVAR_STYLE, &gui.style.cursor, 0xff000000); +cvar_add_acolor("color_hover_b", CVAR_STYLE, &gui.style.hover_btm, 0x288080ff); +cvar_add_acolor("color_press_b", CVAR_STYLE, &gui.style.press_btm, 0x18ffffff); +cvar_add_acolor("color_background_b", CVAR_STYLE, &gui.style.bg_btm, 0x800a0a2d); +cvar_add_color("color_border_btm", CVAR_STYLE, &gui.style.brdr_btm, 0x202020); +cvar_add_color("color_wborder_btm", CVAR_STYLE, &gui.style.wbrdr_btm, 0x202020); + +cvar_add_color("color_button_top", CVAR_STYLE, &gui.style.fill_top, 0x808080); +cvar_add_color("color_textbox_top", CVAR_STYLE, &gui.style.field_top, 0x404040); +cvar_add_color("color_window_top", CVAR_STYLE, &gui.style.win_top, 0x808080); + +cvar_add_color("color_button_btm", CVAR_STYLE, &gui.style.fill_btm, 0x000000); +cvar_add_color("color_textbox_btm", CVAR_STYLE, &gui.style.field_btm, 0x808080); +cvar_add_color("color_window_btm", CVAR_STYLE, &gui.style.win_btm, 0x000000); + +cvar_add_color("color_label_text", CVAR_STYLE, &gui.style.text_label, 0xffffff); +cvar_add_color("color_button_text", CVAR_STYLE, &gui.style.text_base, 0xffffff); +cvar_add_color("color_textbox_text", CVAR_STYLE, &gui.style.text_field, 0xffffff); +cvar_add_color("color_window_text", CVAR_STYLE, &gui.style.text_win, 0xffffff); + +cvar_add_int("border_gui", CVAR_STYLE, &gui.style.border, 1, 0, 2); +cvar_add_int("width_handle", CVAR_STYLE, &gui.style.slider_width, 10, 4, 24); +*/ +/* +#define CVAR_ADD_STYLE(t, n, ctop, cbottom, btop, bbottom, ctext) \ +cvar_add_color("color_" n "_fill_t", CVAR_STYLE, &gui.style.types[t].fill_top, ctop);\ +cvar_add_color("color_" n "_fill_b", CVAR_STYLE, &gui.style.types[t].fill_bottom, cbottom);\ +cvar_add_color("color_" n "_border_t", CVAR_STYLE, &gui.style.types[t].brdr_top, btop);\ +cvar_add_color("color_" n "_border_b", CVAR_STYLE, &gui.style.types[t].brdr_bottom, bbottom);\ +cvar_add_color("color_" n "_text", CVAR_STYLE, &gui.style.types[t].text, ctext); +*/ +// CVAR_ADD_STYLE(GUI_BUTTON, "button", 0x808080, 0x000000, 0x000000, 0x202020, 0xffffff) +// CVAR_ADD_STYLE(GUI_TOGGLE_OFF, "toggle0", 0x808080, 0x101010, 0x000000, 0x202020, 0xcf0000) +// CVAR_ADD_STYLE(GUI_TOGGLE_ON, "toggle1", 0x101010, 0x808080, 0x202020, 0x000000, 0x00cf00) +// CVAR_ADD_STYLE(GUI_ENUM, "switch", 0x808080, 0x000000, 0x000000, 0x202020, 0xffffff) +// CVAR_ADD_STYLE(GUI_SLIDER, "slider", 0x202020, 0x808080, 0x000000, 0x202020, 0xffffff) +// cvar_add_color("color_shandle_fill_t", CVAR_STYLE, &gui.style.types[GUI_SLIDER_HANDLE].fill_top, 0xc0c0c0); +// cvar_add_color("color_shandle_fill_b", CVAR_STYLE, &gui.style.types[GUI_SLIDER_HANDLE].fill_bottom, 0x303030); +// cvar_add_color("color_shandle_border_t", CVAR_STYLE, &gui.style.types[GUI_SLIDER_HANDLE].brdr_top, 0x000000); +// cvar_add_color("color_shandle_border_b", CVAR_STYLE, &gui.style.types[GUI_SLIDER_HANDLE].brdr_bottom, 0x181818); +// CVAR_ADD_STYLE(GUI_DROPDOWN, "dropdown", 0x808080, 0x000000, 0x000000, 0x202020, 0xffffff) +// CVAR_ADD_STYLE(GUI_DROPDOWN_HANDLE, "dhandle", 0x606060, 0x202020, 0x000000, 0x101010, 0xffffff) +// CVAR_ADD_STYLE(GUI_FIELD, "textbox", 0x404040, 0x808080, 0x000000, 0x202020, 0xffffff) +// #undef CVAR_ADD_STYLE + +cvar_add_bool("gl_wireframe", CVAR_RENDER, &gdr.wireframe, 0); +cvar_add_float("phy_sensitivity", CVAR_PHYSICS, &sys.sensitivity, 1.0f, 0.01f, 10.0f); +cvar_add_float("gl_fov", CVAR_RENDER, &gdr.fov, 70.0f, 1.0f, 179.0f); +cvar_add_float("gl_clip_near", CVAR_RENDER, &gdr.clip_near, 0.05f, 0.01f, 1.0f); +cvar_add_float("gl_clip_far", CVAR_RENDER, &gdr.clip_far, 10000.0f, 1.0f, 1000000.0f); +cvar_add_sint("gl_dynlight_max", CVAR_RENDER, cvar_fnc_dlight, NULL, 64, 0, 112, 0); +cvar_add_int("gl_dynlight_chunkrange", CVAR_RENDER, &gdr.light_dist_chunk, 8, 0, 64); +cvar_add_float("gl_dynlight_viewdist", CVAR_RENDER, &gdr.light_dist_cam, 256.0f, 1.0f, 10000.0f); +cvar_add_float("gl_dynlight_maxdist", CVAR_RENDER, &gdr.light_dist_vert, 128.0f, 1.0f, 10000.0f); +cvar_add_sfloat("tic_target", CVAR_PHYSICS, cvar_fnc_ticktarget, NULL, 20.0f, 1.0f, 1200.0f, 0); +cvar_add_int("tic_timeout", CVAR_PHYSICS, &sys.tick_timeout, 2000, 1, 60000); +cvar_add_sbool("gl_tex_filter", CVAR_RENDER, cvar_fnc_tex, &gdr.tex_filter, 1, 1); +cvar_add_sbenum("gl_tex_mipmaps", CVAR_RENDER, cvar_fnc_tex, &gdr.tex_miptype, mipmap_etype, MIP_LINEAR, 3, 1); +cvar_add_sfloat("gl_tex_anisotropic", CVAR_RENDER, cvar_fnc_ftex, &gdr.tex_aniso, 1.0f, 1.0f, 16.0f, 1); +cvar_add_scolor("gl_light_color", CVAR_RENDER, cvar_fnc_clight, NULL, 0x101020, 1); +cvar_add_float("gl_light_blend", CVAR_RENDER, &gdr.light_blend, 0.5f, 0.0f, 1.0f); +cvar_add_float("gl_ambient_x", CVAR_RENDER, &gdr.ambient_x, 0.1f, -1.0f, 1.0f); +cvar_add_float("gl_ambient_y", CVAR_RENDER, &gdr.ambient_y, -1.0f, -1.0f, 1.0f); +cvar_add_float("gl_ambient_z", CVAR_RENDER, &gdr.ambient_z, 0.2f, -1.0f, 1.0f); + +cvar_add_float("phy_speed", CVAR_PHYSICS, &sys.speed, 1.0f, 0.1f, 25.0f); +// cvar_add_double("phy_gravity", CVAR_PHYSICS, &wld.gravity, 0.8f, -20.0f, 20.0f); +// cvar_add_double("phy_friction", CVAR_PHYSICS, &wld.friction, 0.8f, -0.2f, 1.2f); +// cvar_add_int("chunk_grow_objects", CVAR_WORLD, &wld.chunk_cap, 128, 32, 16384); +// cvar_add_int("chunk_grow_polygons", CVAR_WORLD, &wld.chunk_poly, 512, 64, 65536); +// cvar_add_int("chunk_grow_lights", CVAR_WORLD, &wld.chunk_light, 64, 32, 1024); +cvar_add_int("chunk_view_horizontal", CVAR_WORLD, &gdr.ldist_chunk_xz, 16, 1, 128); +cvar_add_int("chunk_view_vertical", CVAR_WORLD, &gdr.ldist_chunk_y, 16, 1, 128); + +cvar_add_bool("mid_play_unknown", CVAR_SOUND, &snd.mid_unknown, 1); +cvar_add_bool("mid_keep_notes", CVAR_SOUND, &snd.mid_keep, 0); +cvar_add_bool("mid_dont_fade", CVAR_SOUND, &snd.mid_nowait, 0); +cvar_add_sbool("mid_debug_events", CVAR_SOUND, cvar_fnc_middebug, NULL, 0, 0); +cvar_add_int("mid_velocity_func", CVAR_SOUND, &snd.mid_velo, 1, -128, 127); +cvar_add_int("mid_opl_voices", CVAR_SOUND, &snd.mid_voices, 64, 4, 192); +cvar_add_benum("mid_opl_bank", CVAR_SOUND, &snd.mid_bank, snd_banknames, 0, 31); +cvar_add_bool("mid_visualizer", CVAR_SOUND, &snd.mid_visual, 1); + +cvar_add_sbenum("snd_device_type", CVAR_SOUND, cvar_fnc_sound, NULL, snd_edevice, 0, 7, 0); +cvar_add_sint("snd_device_index", CVAR_SOUND, cvar_fnc_sound, NULL, -1, -1, 255, 0); +cvar_add_sint("snd_device_sub", CVAR_SOUND, cvar_fnc_sound, NULL, -1, -1, 255, 0); +cvar_add_sint("snd_sample_rate", CVAR_SOUND, cvar_fnc_sound, NULL, 48000, 8000, 384000, 0); +cvar_add_sint("snd_buffer_size", CVAR_SOUND, cvar_fnc_sound, NULL, 0, 0, 1048576, 0); +cvar_add_sint("snd_frame_size", CVAR_SOUND, cvar_fnc_sound, NULL, 32, 2, 8192, 0); +cvar_add_sbenum("snd_sample_format", CVAR_SOUND, cvar_fnc_sound, NULL, snd_eformat, 0, 2, 0); + +for(int z = 0; z < SND_VOLUMES; z++) { + cvar_add_sfloat(snd_cvar_volume[z], CVAR_SOUND, cvar_fnc_volume, NULL, 1.0f, 0.0f, 1.0f, 0); +} diff --git a/wavfile.h b/wavfile.h new file mode 100644 index 0000000..9d0516b --- /dev/null +++ b/wavfile.h @@ -0,0 +1,90 @@ + +void wav_write_uint32(byte *data, uint value) { + for(int h = 0; h < 4; h++) { + data[h] = ((uint)((value >> (8*h) & 0xff))); + } +} + +void wav_write_uint16(byte *data, ushort value) { + for(int h = 0; h < 2; h++) { + data[h] = ((ushort)((value >> (8*h) & 0xff))); + } +} + +void wav_write_header(byte *data, uint samplerate, ushort channels, uint samples, ushort bytes) { + memcpy(data+0, wav_hdr, 4); + wav_write_uint32(data+4, 36 + samples * ((uint)channels) * ((uint)bytes)); + memcpy(data+8, wav_format, 4); + memcpy(data+12, wav_hdr_fmt, 4); + wav_write_uint32(data+16, 16); + wav_write_uint16(data+20, 1); + wav_write_uint16(data+22, channels); + wav_write_uint32(data+24, samplerate); + wav_write_uint32(data+28, samplerate * ((uint)channels) * ((uint)bytes)); + wav_write_uint16(data+32, channels * bytes); + wav_write_uint16(data+34, 8 * bytes); + memcpy(data+36, wav_hdr_data, 4); + wav_write_uint32(data+40, samples * ((uint)channels) * ((uint)bytes)); +} + +byte wav_open(wav_handle *wav, const char *filename, uint samplerate, ushort channels, ushort bytes) { + int err; + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) { + err = errno; + snd_loge(STR_WAV_OPNERR, filename, strerror(err), err); + return 0; + } + byte pad[WAV_HDRSIZE]; + memset(pad, 0, WAV_HDRSIZE); + if(fwrite(pad, 1, WAV_HDRSIZE, fd) != WAV_HDRSIZE) { + snd_loge(STR_WAV_EIO, filename); + fclose(fd); + return 0; + } + wav->fd = fd; + wav->samplerate = samplerate; + wav->channels = channels; + wav->bytes = bytes; + wav->samples = 0; + wav->filename = filename; + return 1; +} + +byte wav_close(wav_handle *wav) { + if(wav->fd == NULL) { + return 0; + } + int err; + if(fseek(wav->fd, 0L, SEEK_SET)) { + err = errno; + snd_loge(STR_WAV_EGEN, wav->filename, strerror(err), err); + fclose(wav->fd); + return 0; + } + byte header[WAV_HDRSIZE]; + wav_write_header(header, wav->samplerate, wav->channels, wav->samples, wav->bytes); + if(fwrite(header, 1, WAV_HDRSIZE, wav->fd) != WAV_HDRSIZE) { + snd_loge(STR_WAV_EIO, wav->filename); + fclose(wav->fd); + return 0; + } + fclose(wav->fd); + return 1; +} + +void wav_write16(wav_handle *wav, short *samples, uint count) { + if(wav->fd == NULL) { + return; + } + for(int c = 0; c < (count * ((uint)wav->channels)); c++) { + wav_write_uint16((wav->buffer)+(c*2), samples[c]); + } + if(fwrite(wav->buffer, 1, 2 * ((uint)wav->channels) * count, wav->fd) != (2 * ((uint)wav->channels) * count)) { + snd_loge(STR_WAV_EIO, wav->filename); + fclose(wav->fd); + wav->fd = NULL; + return; + } + wav->samples += count; +} diff --git a/window.h b/window.h new file mode 100644 index 0000000..3e88497 --- /dev/null +++ b/window.h @@ -0,0 +1,287 @@ + +void win_fbsize(window_t *win, int x, int y) { + glViewport(0, 0, x, y); + fb_resize_int(win->fbtex_gui, win->fb_x, win->fb_y, x, y); + win->fb_x = x; + win->fb_y = y; + // if(win == wld.window) + fb_reconfig(win); + if(sys.win_full == win) { + win->offset_x = 0; + win->offset_y = 0; + win->frame_x = x; + win->frame_y = y; + } + else { + win->offset_x = WIN_BORDER; + win->offset_y = WIN_TITLEBAR + WIN_BORDER; + win->frame_x = x - WIN_BORDER * 2; + win->frame_y = y - (WIN_TITLEBAR + WIN_BORDER * 2); + } + gui_reopen(win); + if(sys.win_full != win) { + win->xsize = x; + win->ysize = y; + } +} + +void win_pos(window_t *win, int x, int y) { + if(sys.win_full != win) { + win->saved_xpos = x; + win->saved_ypos = y; + } +} + +byte win_shift() { + return wcf.active && ((wcf_getkey(wcf.active, KEYSYM_LEFT_SHIFT) == KEY_PRESS) || (wcf_getkey(wcf.active, KEYSYM_RIGHT_SHIFT) == KEY_PRESS)); +} + +byte win_ctrl() { + return wcf.active && ((wcf_getkey(wcf.active, KEYSYM_LEFT_CONTROL) == KEY_PRESS) || (wcf_getkey(wcf.active, KEYSYM_RIGHT_CONTROL) == KEY_PRESS)); +} + +void win_mouse(window_t *win, int x, int y) { + if(win->world) { + int xoff; + int yoff; + if(sys.mouse_first) { + win->mouse_x = x; + win->mouse_y = y; + sys.mouse_first = 0; + } + xoff = x - win->mouse_x; + yoff = win->mouse_y - y; + if(!(win->open)) { + cam_swing(&win->world->camera, (float)xoff, (float)yoff); + } + } + win->mouse_x = x; + win->mouse_y = y; + if(win->open && sys.input && win->mouse && !(win->mouse & 0xfc) && !win_ctrl()) { + // if(sys.mouse_clickx != INT_MIN && sys.mouse_clicky != INT_MIN) { + // logd("t1", "%d %d; %d %d", sys.mouse_x, sys.mouse_y, sys.mouse_clickx, sys.mouse_clicky); + if(!win->drag_x && !win->drag_y && (win->mouse_clickx < win->offset_x || win->mouse_clicky < win->offset_y)) { + if(win->mouse_clickx < win->fb_x - WIN_TITLEBAR * 3) { + int wx, wy; + wcf_getpos(win, &wx, &wy); + wcf_setpos(win, wx + (win->mouse_x - win->mouse_clickx), wy + (win->mouse_y - win->mouse_clicky)); + } + return; + } + if(win->resizable && !win->drag_x && !win->drag_y && (win->mouse_clickx >= win->offset_x + win->frame_x || win->mouse_clicky >= win->offset_y + win->frame_y)) { + win->drag_x = win->mouse_clickx >= win->offset_x + win->frame_x - 10; + win->drag_y = win->mouse_clicky >= win->offset_y + win->frame_y - 10; + } + if(win->drag_x || win->drag_y) { + int wx, wy; //, dx, dy; + // wcf_getpos(sys.win, &dx, &dy); + wcf_getsize(win, &wx, &wy); + wcf_resize(win, win->drag_x ? (win->mouse_x < 1 ? 1 : win->mouse_x) : wx, win->drag_y ? (win->mouse_y < 1 ? 1 : win->mouse_y) : wy); + // wcf_setpos(sys.win, dx, dy); + return; + } + // } + gui_drag(win, win->mouse_x, win->mouse_y); + } +} + +void win_scroll(window_t *win, int x, int y) { + x *= -1; + if(win->open && sys.input) { + gui_scroll(win, x, y, win->mouse_x, win->mouse_y, win_ctrl(), win_shift()); + } + else if(win->open) { + if((x != 0) || (y != 0)) + gui_setkey((x < 0) ? -SCROLL_LEFT : ((x > 0) ? -SCROLL_RIGHT : ((y < 0) ? -SCROLL_DOWN : -SCROLL_UP)), 0); + } + else { + win->scroll |= ((x < 0) ? (1 << (SCROLL_LEFT - SCROLL_UP)) : ((x > 0) ? (1 << (SCROLL_RIGHT - SCROLL_UP)) : 0)) | ((y < 0) ? (1 << (SCROLL_DOWN - SCROLL_UP)) : ((y > 0) ? (1 << (SCROLL_UP - SCROLL_UP)) : 0)); + } +} + +void win_button(window_t *win, int btn, int act) { + if(btn < 0 || btn >= 8) { + return; + } + byte press = act == KEY_PRESS; + byte prev = (win->mouse >> btn) & 1; + if(win->open && prev != press && sys.input && !(win->mouse)) { + win->mouse_clickx = win->mouse_x; + win->mouse_clicky = win->mouse_y; + gui_mouse(win, btn + 1, win->mouse_x, win->mouse_y, win_ctrl(), win_shift()); + } + else if(win->open && press) { + gui_setkey(-(MOUSE_LEFT + btn), 0); + } + win->mouse = (win->mouse & ~(1 << btn)) | (press << btn); + if(win->open && prev != press && sys.input && !(win->mouse)) { + gui_mouserel(win); + win->drag_x = win->drag_y = 0; + } +} + +void win_key(window_t *win, int key, int act) { + if(win->open && (act != KEY_RELEASE) && (key != -1) && sys.input) { + gui_key(win, key, win_ctrl(), win_shift()); + } + else if(win->open && (act != KEY_REPEAT) && (key != -1)) { + gui_setkey(key, act == KEY_RELEASE); + } +} + +void win_char(window_t *win, uint code) { + if(win->open && sys.input) { + gui_char(win, code); + } +} + +void win_drop(window_t *win, int num, const char **paths) { + if(win == snd.player && win->open) { + con_cmd_play(NULL, paths, num); + gui_reopen(win); + } +} + +void win_focus(window_t *win, byte focus) { + win->focused = focus; + win->scroll = 0; + gui_mouse(win, 0, -1, -1, 0, 0); + gui_restyle(win); +} + +void win_redraw(window_t *win) { + gui_reopen(win); +} + +void win_close(window_t *win) { + fb_delete(&win->fb_gui, &win->fbtex_gui); + if(win->world) + world_delete(win); + wcf_destroy(win); +} + +void win_closed(window_t *win) { + if(win == snd.player) + snd.player = NULL; + if(win == sys.console) + sys.interrupted = 1; + else + win_close(win); +} + +void win_menu(window_t *win, byte menu) { + wcf_grab(win, !(win->world) || (wcf.grab_cur_window && (wcf.grab_cur_window != win)) || menu || sys.nograb); + sys.mouse_first = 1; +} + +void win_full(byte full) { + if(full ? (wcf.active && wcf.active->world) : (sys.win_full != NULL)) { + if(full) { + wcf_fullscreen(wcf.active, sys.win_xfull, sys.win_yfull, sys.win_refresh ? sys.win_refresh : -1); + } + else { + wcf_windowed(sys.win_full, sys.win_full->saved_xpos, sys.win_full->saved_ypos, sys.win_full->xsize, sys.win_full->ysize); + } + wcf_selcontext(full ? wcf.active : sys.win_full); + wcf_sync(sys.win_vsync); + sys.win_full = full ? wcf.active : NULL; + } +} + +void win_sync(int sync) { + window_t *win; + sys.win_vsync = sync == 0; + sys.sync_limited = sync > 0; + sys.sync_limit = sync > 0 ? sync : wcf_getmode()->refresh; + for(win = wcf.window_list; win; win = win->next) { + wcf_sync(sys.win_vsync); + } +} + +void win_init() { + window_t *win = sys.console; + int sx = cvar_int("win_pos_x"); + int sy = cvar_int("win_pos_y"); + int wx = cvar_int("win_width"); + int wy = cvar_int("win_height"); + int mx = 960; + int my = 600; + int x, y; + const vidmode_t *mode = wcf_getmode(); + mx += WIN_MINX; + my += WIN_MINY; + sys.win_xfull = mode->width; + sys.win_yfull = mode->height; + sys.win_refresh = mode->refresh; + wcf_getscrpos(&x, &y); + win->xsize = win->xsize > mode->width ? mode->width : mx; + win->ysize = win->ysize > mode->height ? mode->height : my; + win->saved_xpos = x + mode->width / 2 - win->xsize / 2; + win->saved_ypos = y + mode->height / 2 - win->ysize / 2; + win->xsize = win->xsize < mx ? mx : win->xsize; + win->ysize = win->ysize < my ? my : win->ysize; + if(sx != 0x80000000 && sy != 0x80000000) { + win->saved_xpos = sx; + win->saved_ypos = sy; + } + if(wx && wy) { + win->xsize = wx; + win->ysize = wy; + } + wcf_windowed(win, win->saved_xpos, win->saved_ypos, win->xsize, win->ysize); + // wcf_fullscreen does not move while hidden ... + wcf_setpos(win, win->saved_xpos, win->saved_ypos); + wcf_show(win, 1); + byte *icon = mem_alloc(64 * 64 * 4, MEM_IMAGE); + tex_gen_triwave(icon, 64, 64, 0x00000000, 0xffff0000, 0xff00ff00, 0xff0000ff, 0xff7f00ff, 0xff000000); + wcf_icon(win, icon, 64, 64); + mem_free(icon); + wcf_limits(win, mx, my, -1, -1); + win->title = STR_TITLE_CONSOLE; + win->initializer = gui_init_console; + win->queue = 1; +} + +window_t *win_open(int sx, int sy, int wx, int wy, int mx, int my, byte fixed, const char *title, gui_initfnc *initializer, byte type) { + int x, y, w, h; + window_t *win = wcf_create(SYS_PROGRAM, !fixed); + sys_assert(win); + wcf_getpos(sys.console, &x, &y); + wcf_getsize(sys.console, &w, &h); + mx += WIN_MINX; + my += WIN_MINY; + wx += WIN_MINX; + wy += WIN_MINY; + sx = sx < 0 ? (x - wx - (-sx - 1)) : (sx > 0 ? (x + w + (sx - 1)) : x); + sy = sy < 0 ? (y - wy - (-sy - 1)) : (sy > 0 ? (y + h + (sy - 1)) : y); + win->saved_xpos = sx; + win->saved_ypos = sy; + win->xsize = wx; + win->ysize = wy; + wcf_windowed(win, win->saved_xpos, win->saved_ypos, win->xsize, win->ysize); + wcf_setpos(win, win->saved_xpos, win->saved_ypos); + wcf_show(win, 1); + byte *icon = mem_alloc(64 * 64 * 4, MEM_IMAGE); + tex_gen_triwave(icon, 64, 64, 0x00000000, 0xffff0000, 0xff00ff00, 0xff0000ff, 0xff7f00ff, 0xff000000); + wcf_icon(win, icon, 64, 64); + mem_free(icon); + wcf_limits(win, mx, my, fixed ? mx : -1, fixed ? my : -1); + sys_assert(fb_init_sys(&win->fb_gui, &win->fbtex_gui)); + wcf_sync(sys.win_vsync); + win->title = title; + win->initializer = initializer; + win->queue = win->open = type; + return win; +} + +void win_end() { + window_t *win; + for(win = wcf.window_list; win; win = win->next) { + fb_delete(&win->fb_gui, &win->fbtex_gui); + if(win->world) + world_delete(win); + // wcf_destroy(win); + if(win == sys.console) + sys.console = NULL; + } +} diff --git a/world.h b/world.h new file mode 100644 index 0000000..3e1c46a --- /dev/null +++ b/world.h @@ -0,0 +1,564 @@ + +uint map_f_chunkhash(void *ptr) { + ulong hash = (ulong)ptr; + hash = (hash & 0x3f) | (((hash >> 21) & 0x0f) << 12) | (((hash >> 42) & 0x3f) << 6); + return (uint)hash; +} + +void world_init(window_t *win, double x, double y, double z, float yaw, float pitch) { + world_t *world = win->world = mem_zeros(sizeof(world_t), MEM_WORLD); + tbl_init(&world->global_geom, 4, 32, sizeof(geometry_t*), MEM_OBJECT); + world->obj_id = 0ULL; + // world->size_x = x; + // world->size_y = y; + // world->size_z = z; + // world->clamp_xz = (clamp & WORLD_CLAMP_XZ) ? 1 : 0; + // world->clamp_y = (clamp & WORLD_CLAMP_Y) ? 1 : 0; + world->n_chunks = world->n_objects = world->n_lights = world->s_lights = 0; + map_init(&world->chunk_map, map_f_chunkhash, map_f_inteq, 32, MEM_WORLD); + // world->chunks = mem_alloc(sizeof(chunk_t*) * x * y * z, MEM_WORLD); + // memset(world->chunks, 0, sizeof(chunk_t*) * x * y * z); + tbl_init(&world->chunk_table, 16384, 32, sizeof(chunk_t), MEM_CHUNK); + map_init(&world->obj_map, map_f_inthash, map_f_inteq, 32, MEM_OBJMAP); + world->light_dirty = world->chunks_dirty = 0; + world->dirty_first = world->dirty_queue = NULL; + + world->gravity = 0.8; + world->friction = 0.8; + + world->entity = &world->entity_base; + ent_init(world->entity, world, x, y, z, yaw, pitch); + cam_init(&world->camera, x, y + world->entity->eye, z, yaw, pitch); + + sys_assert(world->post_buf = fb_init(&world->fb_post, &world->fbtex_post, &world->rbo_post, win, 0)); + glCreateBuffers(1, &world->light_buf); + glNamedBufferData(world->light_buf, LIGHT_SSIZE * gdr.light_max, NULL, GL_DYNAMIC_DRAW); +} + +void world_unload(world_t *world); + +void world_delete(window_t *win) { + world_t *world = win->world; + // logd("t", "%d", world->obj_map.stored); + world_unload(world); + // chunk_delete(&world->global, 0); + // mem_free(world->chunks); + // tbl_clear(&world->chunk_table); + // map_clear(&world->obj_map); + + glDeleteBuffers(1, &world->light_buf); + fb_remove(world->post_buf); + + mem_free(world); +} + +void chunk_dirty(chunk_t *chunk) { + if(!(chunk->dirty)) { + if(chunk->world->dirty_first) + chunk->world->dirty_queue->next_dirty = chunk; + else + chunk->world->dirty_first = chunk; + chunk->world->dirty_queue = chunk; + chunk->next_dirty = NULL; + chunk->world->chunks_dirty = 1; + chunk->dirty = 1; + } +} + +chunk_t *chunk_get(world_t *world, int x, int y, int z, byte load) { + ulong id = CHUNK_ID(x, y, z); + // if(CHUNK_OUT(world, x, y, z)) + // return &world->global; + // int offset = CHUNK_STRIDE(world, x, y, z); + chunk_t *chunk = map_get(&world->chunk_map, (void*)id); + if(chunk) + return chunk; + else if(!load) + return NULL; + sys_assert(chunk = (chunk_t*)tbl_push(&world->chunk_table)); + chunk->x = x; + chunk->y = y; + chunk->z = z; + chunk->drawlist = NULL; + chunk->dirty = 0; + chunk->size = 1; + chunk->world = world; + tbl_init(&chunk->obj_table, 4, 32, sizeof(geometry_t), MEM_OBJECT); + tbl_init(&chunk->light_table, 2, 32, sizeof(light_t), MEM_LIGHTS); + lst_init(&chunk->poly_list, CHUNK_POLY_GROW, MEM_POLY); + chunk->poly_pool = mem_alloc(CHUNK_POLY_GROW * sizeof(polygon_t), MEM_POLY); + AABB_SET(chunk->box, (double)(int)(x * CHUNK_SIZE), (double)(int)(y * CHUNK_SIZE), (double)(int)(z * CHUNK_SIZE), + (double)(int)((x + 1) * CHUNK_SIZE), (double)(int)((y + 1) * CHUNK_SIZE), (double)(int)((z + 1) * CHUNK_SIZE)) + map_put(&world->chunk_map, (void*)id, chunk); + world->n_chunks += 1; + return chunk; +} + +void chunk_resize(chunk_t *chunk, int size) { + // table_t obj_table; + list_t poly_list; + polygon_t *poly_pool; + ulong pos = 0; + uint id; + geometry_t *geom; + // geometry_t *ngeom; + polygon_t *poly; + polygon_t *npoly; + // tbl_init(&obj_table, size * wld.chunk_cap / 32, 32, sizeof(geometry_t), MEM_OBJECT); + lst_init(&poly_list, size * CHUNK_POLY_GROW, MEM_POLY); + poly_pool = mem_alloc(size * CHUNK_POLY_GROW * sizeof(polygon_t), MEM_POLY); + // logd("TST", "chunk @ %d %d %d -> %d %d ---> %d %d", chunk->x, chunk->y, chunk->z, chunk->obj_table.size, chunk->poly_list.size, obj_table.size, poly_list.size); + while(geom = tbl_iter(&chunk->obj_table, &pos)) { + // ngeom = (geometry_t*)tbl_push(&obj_table); + // memcpy(ngeom, geom, sizeof(geometry_t)); + // sys_assert(!map_put(&chunk->world->obj_map, (void*)geom->id, ngeom)) + poly = &chunk->poly_pool[geom->polys]; + geom->polys = 0xffffffff; + while(poly) { + id = lst_push(&poly_list); + if(geom->polys != 0xffffffff) { + npoly->next = id; + } + else { + geom->polys = id; + } + npoly = &poly_pool[id]; + memcpy(npoly, poly, sizeof(polygon_t)); + // npoly->id = id; + poly = (poly->next == 0xffffffff) ? NULL : &chunk->poly_pool[poly->next]; + } + npoly->next = 0xffffffff; + } + // tbl_clear(&chunk->obj_table); + lst_free(&chunk->poly_list); + mem_free(chunk->poly_pool); + // memcpy(&chunk->obj_table, &obj_table, sizeof(table_t)); + memcpy(&chunk->poly_list, &poly_list, sizeof(list_t)); + chunk->poly_pool = poly_pool; + chunk->size = size; +} + +// void chunk_resize_light(chunk_t *chunk, int size) { + // table_t light_table; + // ulong pos = 0; + // light_t *light; + // light_t *nlight; + // tbl_init(&light_table, size * wld.chunk_light / 32, 32, sizeof(light_t), MEM_LIGHTS); + // logd("TST", "chunk @ %d %d %d -> %d ---> %d", chunk->x, chunk->y, chunk->z, chunk->light_table.size, light_table.size); + // while(light = tbl_iter(&chunk->light_table, &pos)) { + // nlight = (light_t*)tbl_push(&light_table); + // memcpy(nlight, light, sizeof(light_t)); + // sys_assert(!map_put(&chunk->world->obj_map, (void*)light->id, nlight)) + // } + // tbl_clear(&chunk->light_table); + // memcpy(&chunk->light_table, &light_table, sizeof(table_t)); + // chunk->light_size = size; +// } + +void vec_tri_normal(vertex_t *result, vertex_b *vertices) { + vec3 u, v; + glm_vec3_sub(vertices[1].pos, vertices[0].pos, u); + glm_vec3_sub(vertices[2].pos, vertices[0].pos, v); + glm_vec3_crossn(u, v, v); + for(int n = 0; n < 3; n++) { + glm_vec3_copy(v, result[n].normal); + } +} + +geometry_t *chunk_add(chunk_t *chunk, polygon_b *polys, int n_polys, float x, float y, float z) { + geometry_t *obj; + if(chunk->poly_list.stored + n_polys > chunk->poly_list.size) + chunk_resize(chunk, chunk->size + (((chunk->poly_list.stored + n_polys) - chunk->poly_list.size) + (CHUNK_POLY_GROW - 1)) / CHUNK_POLY_GROW); + sys_assert(obj = (geometry_t*)tbl_push(&chunk->obj_table)); + // if(!obj) { + // chunk_resize(chunk, chunk->size + 1); + // obj = (geometry_t*)tbl_push(&chunk->obj_table); + // } + // logd("TST", "chunk @ %d %d %d -> %d %d", chunk->x, chunk->y, chunk->z, chunk->obj_table.stored, chunk->poly_list.stored); + vertex_b *bvertex; + vertex_t *nvertex; + uint id; + polygon_t *poly; + AABB_SET(obj->collision, x + polys[0].vertices[0].pos[0], y + polys[0].vertices[0].pos[1], z + polys[0].vertices[0].pos[2], x + polys[0].vertices[0].pos[0], y + polys[0].vertices[0].pos[1], z + polys[0].vertices[0].pos[2]) + obj->polys = id = lst_push(&chunk->poly_list); + poly = &chunk->poly_pool[id]; + for(int c = 0; c < n_polys; c++) { + if(c) { + id = lst_push(&chunk->poly_list); + poly->next = id; + poly = &chunk->poly_pool[id]; + } + // poly->id = id; + poly->material = polys[c].material; + vec_tri_normal(poly->vertices, polys[c].vertices); + for(int v = 0; v < 3; v++) { + bvertex = &polys[c].vertices[v]; + nvertex = &poly->vertices[v]; + box_union(NULL, &obj->collision, x + bvertex->pos[0], y + bvertex->pos[1], z + bvertex->pos[2]); + nvertex->pos[0] = x + bvertex->pos[0] - chunk->box.x1; + nvertex->pos[1] = y + bvertex->pos[1] - chunk->box.y1; + nvertex->pos[2] = z + bvertex->pos[2] - chunk->box.z1; + // norm(nvertex->pos, nvertex->normal); + // VEC3_SET(nvertex->normal, 0.0f, 1.0f, 0.0f); + nvertex->coord[0] = bvertex->coord[0]; + nvertex->coord[1] = bvertex->coord[1]; + } + } + poly->next = 0xffffffff; + // obj->n_polys = n_polys; + // chunk->dirty = 1; + if(obj->collision.x2 - obj->collision.x1 >= (float)CHUNK_SIZE || obj->collision.y2 - obj->collision.y1 >= (float)CHUNK_SIZE || + obj->collision.z2 - obj->collision.z1 >= (float)CHUNK_SIZE) { + obj->global = tbl_push(&chunk->world->global_geom); + *(obj->global) = obj; + } + else { + obj->global = NULL; + } + return obj; +} + +ulong world_add(world_t *world, polygon_b *polys, int n_polys, float x, float y, float z) { + chunk_t *chunk; + geometry_t *obj; + ulong id; + chunk = chunk_get(world, NEG_OFFSET(x, CHUNK_SIZE), NEG_OFFSET(y, CHUNK_SIZE), NEG_OFFSET(z, CHUNK_SIZE), 1); + // if(!chunk) + // return 0; + obj = chunk_add(chunk, polys, n_polys, x, y, z); + // if(!obj) + // return 0; + id = world->obj_id += 1ULL; + map_put(&world->obj_map, (void*)id, obj); + obj->id = id; + obj->chunk_x = chunk->x; + obj->chunk_y = chunk->y; + obj->chunk_z = chunk->z; + world->n_objects += 1; + chunk_dirty(chunk); + return id; +} + +void chunk_remove(chunk_t *chunk, geometry_t *geom) { + uint poly = geom->polys; + while(poly != 0xffffffff) { + lst_pop(&chunk->poly_list, poly); + poly = (&chunk->poly_pool[poly])->next; + } + if(geom->global) + tbl_pop(&chunk->world->global_geom, geom->global); + tbl_pop(&chunk->obj_table, geom); + if((chunk->size > 1) && (chunk->poly_list.stored <= (CHUNK_POLY_GROW * (chunk->size - 1)))) + // && (chunk->obj_table.stored <= (((wld.chunk_cap / 32) * (chunk->size - 1)) * (32 * (chunk->size - 1))))) + chunk_resize(chunk, chunk->size - 1); + // chunk->dirty = 1; +} + +void world_remove(world_t *world, ulong id) { + chunk_t *chunk; + geometry_t *obj; + obj = map_get(&world->obj_map, (void*)id); + if(!obj) + return; + chunk = chunk_get(world, obj->chunk_x, obj->chunk_y, obj->chunk_z, 0); + if(!chunk) + return; + chunk_remove(chunk, obj); + map_remove(&world->obj_map, (void*)id); + world->n_objects -= 1; + chunk_dirty(chunk); +} + +// void chunk_remove_light(chunk_t *chunk, light_t *light) { + // tbl_pop(&chunk->light_table, light); +// } + +ulong world_add_light(world_t *world, light_b *lamp, float x, float y, float z) { + chunk_t *chunk; + light_t *light; + ulong id; + chunk = chunk_get(world, NEG_OFFSET(x, CHUNK_SIZE), NEG_OFFSET(y, CHUNK_SIZE), NEG_OFFSET(z, CHUNK_SIZE), 1); + // if(!chunk) + // return 0; + sys_assert(light = (light_t*)tbl_push(&chunk->light_table)); + // if(!light) { + // chunk_resize_light(chunk, chunk->light_size + 1); + // light = (light_t*)tbl_push(&chunk->light_table); + // } + id = world->obj_id += 1ULL; + map_put(&world->obj_map, (void*)id, light); + light_init_base(light, lamp); + light->id = id; + VEC3_SET(light->pos, x, y, z); + world->n_lights += 1; + world->light_dirty = 1; + return id; +} + +void world_set_light(world_t *world, ulong id, light_b *lamp) { + light_t *light = map_get(&world->obj_map, (void*)id); + if(!light) + return; + light_init_base(light, lamp); + world->light_dirty = 1; +} + +void world_remove_light(world_t *world, ulong id) { + chunk_t *chunk; + light_t *light; + light = map_get(&world->obj_map, (void*)id); + if(!light) + return; + chunk = chunk_get(world, NEG_OFFSET(light->pos[0], CHUNK_SIZE), NEG_OFFSET(light->pos[1], CHUNK_SIZE), NEG_OFFSET(light->pos[2], CHUNK_SIZE), 0); + if(!chunk) + return; + tbl_pop(&chunk->light_table, light); + map_remove(&world->obj_map, (void*)id); + world->n_lights -= 1; + // if((chunk->light_size > 1) && (chunk->light_table.stored <= (((wld.chunk_light / 32) * (chunk->light_size - 1)) * (32 * (chunk->light_size - 1))))) + // chunk_resize_light(chunk, chunk->light_size - 1); + world->light_dirty = 1; +} + +void chunk_clear(chunk_t *chunk, byte unmap) { + ulong pos = 0; + geometry_t *geom; + uint poly; + while(geom = tbl_iter(&chunk->obj_table, &pos)) { + if(unmap) + map_remove(&chunk->world->obj_map, (void*)geom->id); + poly = geom->polys; + while(poly != 0xffffffff) { + lst_pop(&chunk->poly_list, poly); + poly = (&chunk->poly_pool[poly])->next; + } + if(geom->global) + tbl_pop(&chunk->world->global_geom, geom->global); + } + chunk->world->n_objects -= chunk->obj_table.stored; + chunk->world->n_lights -= chunk->light_table.stored; + if(chunk->light_table.stored) + chunk->world->light_dirty = 1; + tbl_clear(&chunk->obj_table); + tbl_clear(&chunk->light_table); + if(chunk->size > 1) + chunk_resize(chunk, 1); + // if(chunk->light_size > 1) + // chunk_resize_light(chunk, 1); + // chunk->dirty = 1; + chunk_dirty(chunk); +} + +void chunk_update(chunk_t *chunk) { + if(chunk->dirty) { + drawlist_t *list = chunk->drawlist; + if(list) { + draw_destroy(list); + } + else if(chunk->obj_table.stored) { + chunk->drawlist = list = tbl_push(&gdr.drawlists); + } + if(chunk->obj_table.stored && list) { + draw_build(chunk, list); + } + else if(list) { + tbl_pop(&gdr.drawlists, list); + chunk->drawlist = NULL; + } + chunk->dirty = 0; + } +} + +void chunk_delete(chunk_t *chunk, byte unmap) { + chunk_clear(chunk, unmap); + chunk_update(chunk); + tbl_clear(&chunk->obj_table); + tbl_clear(&chunk->light_table); + lst_free(&chunk->poly_list); + mem_free(chunk->poly_pool); +} + +void chunk_unload(world_t *world, int x, int y, int z) { + // if(CHUNK_OUT(world, x, y, z)) + // return; + ulong id = CHUNK_ID(x, y, z); + chunk_t *chunk = map_remove(&world->chunk_map, (void*)id); + if(chunk) { + chunk_delete(chunk, 1); + // world->chunks[offset] = NULL; + tbl_pop(&world->chunk_table, chunk); + world->n_chunks -= 1; + } +} + +void world_update(world_t *world) { + if(world->chunks_dirty) { + int pos = 0; + chunk_t *chunk = world->dirty_first; + chunk_t *nchunk; + while(chunk) { + chunk_update(chunk); + if(chunk->obj_table.stored == 0 && chunk->light_table.stored == 0) { + nchunk = chunk->next_dirty; + chunk_unload(world, chunk->x, chunk->y, chunk->z); + chunk = nchunk; + } + else { + chunk = chunk->next_dirty; + } + } + world->dirty_first = world->dirty_queue = NULL; + world->chunks_dirty = 0; + } + if(world->light_dirty) { + light_calc(world); + world->light_dirty = 0; + } +} + +void world_unload(world_t *world) { + ulong pos = 0; + chunk_t *chunk; + while(chunk = tbl_iter(&world->chunk_table, &pos)) { + chunk_delete(chunk, 0); + } + map_clear(&world->chunk_map); + tbl_clear(&world->global_geom); + tbl_clear(&world->chunk_table); + map_clear(&world->obj_map); + world->n_chunks = world->n_objects = world->n_lights = 0; +} + +void vec_make_quad(polygon_b *polys, float u, float v, float w, float p, float q, int m, int n, int o) { + byte uflip = m < 0; + byte vflip = n < 0; + m = (m < 0) ? (-1 - m) : (m - 1); + n = (n < 0) ? (-1 - n) : (n - 1); + o -= 1; + + for(int c = 0; c < 3; c++) { + polys[0].vertices[c].pos[o] = w; + polys[1].vertices[c].pos[o] = w; + } + + polys[0].vertices[0].pos[m] = polys[1].vertices[2].pos[m] = u; + polys[0].vertices[0].pos[n] = polys[1].vertices[2].pos[n] = v; + polys[0].vertices[0].coord[0] = polys[1].vertices[2].coord[0] = uflip ? p : 0.0f; + polys[0].vertices[0].coord[1] = polys[1].vertices[2].coord[1] = vflip ? q : 0.0f; + + polys[0].vertices[1].pos[m] = u + p; + polys[0].vertices[1].pos[n] = v; + polys[0].vertices[1].coord[0] = uflip ? 0.0f : p; + polys[0].vertices[1].coord[1] = vflip ? q : 0.0f; + + polys[0].vertices[2].pos[m] = polys[1].vertices[0].pos[m] = u + p; + polys[0].vertices[2].pos[n] = polys[1].vertices[0].pos[n] = v + q; + polys[0].vertices[2].coord[0] = polys[1].vertices[0].coord[0] = uflip ? 0.0f : p; + polys[0].vertices[2].coord[1] = polys[1].vertices[0].coord[1] = vflip ? 0.0f : q; + + polys[1].vertices[1].pos[m] = u; + polys[1].vertices[1].pos[n] = v + q; + polys[1].vertices[1].coord[0] = uflip ? p : 0.0f; + polys[1].vertices[1].coord[1] = vflip ? 0.0f : q; +} + +void vec_make_dquad(polygon_b *polys, float x, float y, float z, float xsize, float ysize, float zsize, material_t *material, byte dir, byte shift) { + float offset = shift ? 1.0f : 0.0f; + switch(dir) { + case FACE_WEST: + vec_make_quad(polys, z, y, x, zsize, ysize, 3, 2, 1); + break; + case FACE_EAST: + vec_make_quad(polys, z, y, x + offset * xsize, zsize, ysize, -3, 2, 1); + break; + case FACE_DOWN: + vec_make_quad(polys, x, z, y, xsize, zsize, 1, 3, 2); + break; + case FACE_UP: + vec_make_quad(polys, x, z, y + offset * ysize, xsize, zsize, 1, 3, 2); + break; + case FACE_NORTH: + vec_make_quad(polys, x, y, z, xsize, ysize, 1, 2, 3); + break; + case FACE_SOUTH: + vec_make_quad(polys, x, y, z + offset * zsize, xsize, ysize, -1, 2, 3); + break; + } + polys[0].material = polys[1].material = material; +} + +ulong world_add_floor(world_t *world, float x, float y, float z, float xsize, float zsize, material_t *material) { + polygon_b polys[2]; + vec_make_dquad(polys, 0.0f, 0.0f, 0.0f, xsize, 0.0f, zsize, material, FACE_UP, 0); + return world_add(world, polys, 2, x, y, z); +} + +ulong world_add_box(world_t *world, float x, float y, float z, float xsize, float ysize, float zsize, material_t *material) { + polygon_b polys[12]; + for(int n = 0; n < 6; n++) { + vec_make_dquad(&polys[n << 1], 0.0f, 0.0f, 0.0f, xsize, ysize, zsize, material, n, 1); + } + return world_add(world, polys, 12, x, y, z); +} + +void vec_make_gquad(polygon_b *polys, float x, float z, float p, float q, float y00, float y10, float y01, float y11) { + polys[0].vertices[0].pos X = polys[1].vertices[2].pos X = x; + polys[0].vertices[0].pos Y = polys[1].vertices[2].pos Y = y00; + polys[0].vertices[0].pos Z = polys[1].vertices[2].pos Z = z; + polys[0].vertices[0].coord[0] = polys[1].vertices[2].coord[0] = 0.0f; + polys[0].vertices[0].coord[1] = polys[1].vertices[2].coord[1] = 0.0f; + + polys[0].vertices[1].pos X = x + p; + polys[0].vertices[1].pos Y = y10; + polys[0].vertices[1].pos Z = z; + polys[0].vertices[1].coord[0] = p; + polys[0].vertices[1].coord[1] = 0.0f; + + polys[0].vertices[2].pos X = polys[1].vertices[0].pos X = x + p; + polys[0].vertices[2].pos Y = polys[1].vertices[0].pos Y = y11; + polys[0].vertices[2].pos Z = polys[1].vertices[0].pos Z = z + q; + polys[0].vertices[2].coord[0] = polys[1].vertices[0].coord[0] = p; + polys[0].vertices[2].coord[1] = polys[1].vertices[0].coord[1] = q; + + polys[1].vertices[1].pos X = x; + polys[1].vertices[1].pos Y = y01; + polys[1].vertices[1].pos Z = z + q; + polys[1].vertices[1].coord[0] = 0.0f; + polys[1].vertices[1].coord[1] = q; +} + +void world_add_graph(world_t *world, graph2_func *func, void *p, float x, float y, float z, float xsize, float ysize, float zsize, + material_t *material, uint xvals, uint zvals, float xoffset, float zoffset, float xrange, float zrange, uint frag) { + uint xfrags = (xvals + frag - 1) / frag; + uint zfrags = (zvals + frag - 1) / frag; + uint fdx = frag; + uint fdz = frag; + uint sx, sz, fx, fz, gx, gz; + polygon_b *polys = mem_alloc(sizeof(polygon_b) * 2 * frag * frag, MEM_POLY); + polygon_b *poly; + for(sx = 0; sx < xfrags; sx++) { + if(sx == xfrags - 1) + fdx = xvals - ((xfrags - 1) * frag); + for(sz = 0; sz < zfrags; sz++) { + if(sz == zfrags - 1) + fdz = zvals - ((zfrags - 1) * frag); + for(fx = 0; fx < fdx; fx++) { + gx = sx * frag + fx; + for(fz = 0; fz < fdz; fz++) { + gz = sz * frag + fz; + poly = &polys[(fx + fz * fdx) * 2]; + poly[0].material = poly[1].material = material; + vec_make_gquad(poly, xsize * (float)fx / (float)xvals, zsize * (float)fz / (float)zvals, xsize / (float)xvals, zsize / (float)zvals, + func(p, xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange) * ysize, + func(p, xoffset + (float)(gx + 1) / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange) * ysize, + func(p, xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)(gz + 1) / (float)zvals * zrange) * ysize, + func(p, xoffset + (float)(gx + 1) / (float)xvals * xrange, zoffset + (float)(gz + 1) / (float)zvals * zrange) * ysize); + // logi("t", "%d %d; %d %d; %d %d; %.1f %.1f -> %.1f", sx, sz, fx, fz, gx, gz, + // xoffset + (float)gx / (float)xvals * xrange, zoffset + (float)gz / (float)zvals * zrange, poly[0].vertices[0].pos Y); + } + } + world_add(world, polys, fdx * fdz * 2, x + (xsize * (float)(sx * frag) / (float)xvals), y, z + (zsize * (float)(sz * frag) / (float)zvals)); + } + } + mem_free(polys); +} diff --git a/x11_stub.h b/x11_stub.h new file mode 100644 index 0000000..a1b0b7b --- /dev/null +++ b/x11_stub.h @@ -0,0 +1,69 @@ + +typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); +typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*); +typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*); +typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc); +typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput); +typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window); +typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window); +typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*); +typedef void (* PFN_XRRSelectInput)(Display*,Window,int); +typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int); +typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); +#define XRRFreeCrtcInfo wcf.randr.FreeCrtcInfo +#define XRRFreeOutputInfo wcf.randr.FreeOutputInfo +#define XRRFreeScreenResources wcf.randr.FreeScreenResources +#define XRRGetCrtcInfo wcf.randr.GetCrtcInfo +#define XRRGetOutputInfo wcf.randr.GetOutputInfo +#define XRRGetOutputPrimary wcf.randr.GetOutputPrimary +#define XRRGetScreenResourcesCurrent wcf.randr.GetScreenResourcesCurrent +#define XRRQueryExtension wcf.randr.QueryExtension +#define XRRQueryVersion wcf.randr.QueryVersion +#define XRRSelectInput wcf.randr.SelectInput +#define XRRSetCrtcConfig wcf.randr.SetCrtcConfig +#define XRRUpdateConfiguration wcf.randr.UpdateConfiguration + +typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); +typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); +typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); +#define XcursorImageCreate wcf.xcursor.ImageCreate +#define XcursorImageDestroy wcf.xcursor.ImageDestroy +#define XcursorImageLoadCursor wcf.xcursor.ImageLoadCursor + +typedef Bool (* PFN_XineramaIsActive)(Display*); +typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); +typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*); +#define XineramaIsActive wcf.xinerama.IsActive +#define XineramaQueryExtension wcf.xinerama.QueryExtension +#define XineramaQueryScreens wcf.xinerama.QueryScreens + +typedef XID xcb_window_t; +typedef XID xcb_visualid_t; +typedef struct xcb_connection_t xcb_connection_t; +typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); +#define XGetXCBConnection wcf.x11xcb.GetXCBConnection + +typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); +#define XF86VidModeQueryExtension wcf.vidmode.QueryExtension + +typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); +typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); +#define XIQueryVersion wcf.xi.QueryVersion +#define XISelectEvents wcf.xi.SelectEvents + +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension wcf.xrender.QueryExtension +#define XRenderQueryVersion wcf.xrender.QueryVersion +#define XRenderFindVisualFormat wcf.xrender.FindVisualFormat + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + +#define MWM_HINTS_DECORATIONS 2 +#define MWM_DECOR_ALL 1 + +#define WCF_XDND_VERSION 5