From 5104567a24021fb07e62f941b4b9e64f4bda56e7 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 10:57:30 +0100 Subject: Some code tweaks --- src/audio.c | 1 - src/core.c | 4 ++-- src/models.c | 44 ++++++++++++++++++++++++++++---------------- src/raylib.h | 2 +- src/rlgl.c | 2 +- src/utils.c | 2 +- 6 files changed, 33 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 40c24895..5963c11b 100644 --- a/src/audio.c +++ b/src/audio.c @@ -865,7 +865,6 @@ static Wave LoadOGG(char *fileName) TraceLog(DEBUG, "[%s] Total samples calculated: %i", fileName, totalSamples); - //short *data wave.data = malloc(sizeof(short)*totalSamplesLength); int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength); diff --git a/src/core.c b/src/core.c index 26b1dd6d..574de334 100644 --- a/src/core.c +++ b/src/core.c @@ -261,7 +261,7 @@ static void CommandCallback(struct android_app *app, int32_t cmd); // // Initialize Window and Graphics Context (OpenGL) void InitWindow(int width, int height, const char *title) { - TraceLog(INFO, "Initializing raylib..."); + TraceLog(INFO, "Initializing raylib (v1.2.2)"); // Store window title (could be useful...) windowTitle = title; @@ -300,7 +300,7 @@ void InitWindow(int width, int height, const char *title) // Android activity initialization void InitWindow(int width, int height, struct android_app *state) { - TraceLog(INFO, "Initializing raylib..."); + TraceLog(INFO, "Initializing raylib (v1.2.2)"); app_dummy(); diff --git a/src/models.c b/src/models.c index f61f79f5..bb172203 100644 --- a/src/models.c +++ b/src/models.c @@ -686,10 +686,13 @@ Model LoadModel(const char *fileName) Model model = rlglLoadModel(vData); // Upload vertex data to GPU // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -803,10 +806,13 @@ Model LoadHeightmap(Image heightmap, float maxHeight) Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -1118,10 +1124,13 @@ Model LoadCubicmap(Image cubesmap) Model model = rlglLoadModel(vData); // Now that vertex data is uploaded to GPU, we can free arrays - // NOTE: Despite vertex data is useless on OpenGL 3.3 or ES2, we will keep it... - //free(vData.vertices); - //free(vData.texcoords); - //free(vData.normals); + // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2 + if (rlGetVersion() != OPENGL_11) + { + free(vData.vertices); + free(vData.texcoords); + free(vData.normals); + } return model; } @@ -1129,10 +1138,13 @@ Model LoadCubicmap(Image cubesmap) // Unload 3d model from memory void UnloadModel(Model model) { - free(model.mesh.vertices); - free(model.mesh.texcoords); - free(model.mesh.normals); - + if (rlGetVersion() == OPENGL_11) + { + free(model.mesh.vertices); + free(model.mesh.texcoords); + free(model.mesh.normals); + } + rlDeleteBuffers(model.vboId[0]); rlDeleteBuffers(model.vboId[1]); rlDeleteBuffers(model.vboId[2]); diff --git a/src/raylib.h b/src/raylib.h index 69966069..14590d04 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -312,7 +312,6 @@ void SetExitKey(int key); // Set a custom key #endif int GetScreenWidth(void); // Get current screen width int GetScreenHeight(void); // Get current screen height -int GetKeyPressed(void); // Get latest key pressed void ClearBackground(Color color); // Sets Background Color void BeginDrawing(void); // Setup drawing canvas to start drawing @@ -342,6 +341,7 @@ bool IsKeyPressed(int key); // Detect if a key has b bool IsKeyDown(int key); // Detect if a key is being pressed bool IsKeyReleased(int key); // Detect if a key has been released once bool IsKeyUp(int key); // Detect if a key is NOT being pressed +int GetKeyPressed(void); // Get latest key pressed bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed diff --git a/src/rlgl.c b/src/rlgl.c index 50ef1efd..2b3dfc5f 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1682,7 +1682,7 @@ static GLuint LoadSimpleShader(void) char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! - "precision mediump float; \n" // WebGL, required for emscripten + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) #endif "uniform sampler2D texture0; \n" "varying vec2 fragTexCoord; \n" diff --git a/src/utils.c b/src/utils.c index c3c20b47..dd08f5f8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -79,7 +79,7 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, pUncomp = (mz_uint8 *)malloc((size_t)uncompSize); // Check correct memory allocation - if (!pUncomp) + if (pUncomp == NULL) { TraceLog(WARNING, "Out of memory while decompressing data"); } -- cgit v1.2.3 From 874dc89fca294af1d6f7ace7398716a658eeab2d Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Jan 2015 10:58:04 +0100 Subject: Adding support for TTF fonts (in progress) --- src/stb_rect_pack.h | 546 +++++++++++ src/stb_truetype.h | 2612 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/text.c | 107 ++- 3 files changed, 3263 insertions(+), 2 deletions(-) create mode 100644 src/stb_rect_pack.h create mode 100644 src/stb_truetype.h (limited to 'src') diff --git a/src/stb_rect_pack.h b/src/stb_rect_pack.h new file mode 100644 index 00000000..dcc9d887 --- /dev/null +++ b/src/stb_rect_pack.h @@ -0,0 +1,546 @@ +// stb_rect_pack.h - v0.05 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Version history: +// +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight, +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#include + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1, +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + + // unsort + qsort(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/src/stb_truetype.h b/src/stb_truetype.h new file mode 100644 index 00000000..56ef47b7 --- /dev/null +++ b/src/stb_truetype.h @@ -0,0 +1,2612 @@ +// stb_truetype.h - v1.02 - public domain +// authored from 2009-2014 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// +// Misc other: +// Ryan Gordon +// +// VERSION HISTORY +// +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; + +extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is // the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +extern void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +extern int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_char_in_range; + int num_chars_in_range; + stbtt_packedchar *chardata_for_range; // output +} stbtt_pack_range; + +extern int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. + + +extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s). The default (no oversampling) is achieved by +// h_oversample=1, v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts + +extern void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + searchRange >>= 1; + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +typedef struct +{ + stbrp_coord x,y; + int id,w,h,was_packed; +} stbrp_rect; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + for (j=0; j < h; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + for (j=0; j < w; ++j) { + int i; + unsigned int total; + memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + float recip_h = 1.0f / spc->h_oversample; + float recip_v = 1.0f / spc->v_oversample; + float sub_x = stbtt__oversample_shift(spc->h_oversample); + float sub_y = stbtt__oversample_shift(spc->v_oversample); + int i,j,k,n, return_value = 1; + stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars_in_range; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars_in_range; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + int x0,y0,x1,y1; + stbtt_GetCodepointBitmapBoxSubpixel(&info, ranges[i].first_unicode_char_in_range + j, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + stbrp_pack_rects(context, rects, k); + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(&info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + return return_value; +} + +int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_char_in_range = first_unicode_char_in_range; + range.num_chars_in_range = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/src/text.c b/src/text.c index 94481857..c2340af5 100644 --- a/src/text.c +++ b/src/text.c @@ -33,6 +33,12 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 #include "utils.h" // Required for function GetExtendion() +// Following libs will be used on LoadTTF() +#define STB_TRUETYPE_IMPLEMENTATION +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" +#include "stb_truetype.h" + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -64,6 +70,7 @@ static SpriteFont defaultFont; // Default font provided by raylib static bool PixelIsMagenta(Color p); // Check if a pixel is magenta static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet); // Parse image pixel data to obtain character set measures static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) +static SpriteFont LoadTTF(const char *fileName, int fontSize); // Generate a sprite font image from TTF data (font size required) extern void LoadDefaultFont(void); extern void UnloadDefaultFont(void); @@ -189,6 +196,7 @@ SpriteFont LoadSpriteFont(const char *fileName) // Check file extension if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); + else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, 20); else { Image image = LoadImage(fileName); @@ -567,11 +575,106 @@ static SpriteFont LoadRBMF(const char *fileName) } // Generate a sprite font from TTF data (font size required) -static SpriteFont GenerateFromTTF(const char *fileName, int fontSize) +static SpriteFont LoadTTF(const char *fileName, int fontSize) { SpriteFont font; + + Image image; + image.width = 512; + image.height = 512; + image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + + unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25); + + // TODO: Load TTF and generate bitmap font and chars data -> REVIEW! + + stbtt_packedchar chardata[128]; // Num characters: 128 (?) -> REVIEW! + + unsigned char *tempBitmap = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); // One channel bitmap returned! + + // REFERENCE +/* + typedef struct + { + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; + } stbtt_packedchar; +*/ + + stbtt_pack_context pc; + + FILE *ttfFile = fopen(fileName, "rb"); + + fread(ttfBuffer, 1, 1<<25, ttfFile); - // TODO: Load TTF and generate bitmap font and chars data + stbtt_PackBegin(&pc, tempBitmap, image.width, image.height, 0, 1, NULL); + + //stbtt_PackSetOversampling(&pc, 1, 1); + //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[0]+32); + stbtt_PackSetOversampling(&pc, 2, 2); // Better results + stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata + 32); + //stbtt_PackSetOversampling(&pc, 3, 1); + //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[2]+32); + + stbtt_PackEnd(&pc); + + free(ttfBuffer); + // Now we have image data in tempBitmap and chardata filled... + + for (int i = 0; i < 512*512; i++) + { + image.pixels[i].r = tempBitmap[i]; + image.pixels[i].g = tempBitmap[i]; + image.pixels[i].b = tempBitmap[i]; + image.pixels[i].a = 255; + } + + free(tempBitmap); + + // REFERENCE EXAMPLE +/* + //To draw, provide *text, posX, posY + //stbtt_aligned_quad letter; + //stbtt_GetPackedQuad(chardata[0], BITMAP_W, BITMAP_H, *text++, &posX, &posY, &letter, font ? 0 : integer_align); + + void print(float x, float y, int fontNum, char *text) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font_tex); + glBegin(GL_QUADS); + while (*text) { + stbtt_aligned_quad q; + stbtt_GetPackedQuad(chardata[fontNum], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, fontNum ? 0 : integer_align); + drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1); + } + glEnd(); + } + + print(100,160, 0, "This is a test"); +*/ + + font.numChars = 95; + font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); + font.texture = LoadTextureFromImage(image, false); + + //stbtt_aligned_quad letter; + //int x = 0, y = 0; + + for (int i = 0; i < font.numChars; i++) + { + font.charSet[i].value = i + 32; + + //stbtt_GetPackedQuad(chardata[0], 512, 512, i, &x, &y, &letter, 0); + + font.charSet[i].x = chardata[i + 32].x0; + font.charSet[i].y = chardata[i + 32].y0; + font.charSet[i].w = chardata[i + 32].x1 - chardata[i + 32].x0; + font.charSet[i].h = chardata[i + 32].y1 - chardata[i + 32].y0; + } + + UnloadImage(image); + return font; } \ No newline at end of file -- cgit v1.2.3 From c00cd38b017e7f8daad92aff546209bc0a68028c Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:12:30 +0100 Subject: Small note to remember --- src/rlgl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index 2b3dfc5f..7977ea84 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1259,6 +1259,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif + // TODO: Review this comment when called from window resize callback TraceLog(INFO, "OpenGL Graphics initialized successfully"); } -- cgit v1.2.3 From 7d0407c6a21e4fab566fd5a8f4d3edda6911f2ec Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:12:54 +0100 Subject: Pause music when window minimized --- src/audio.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 5963c11b..bd168158 100644 --- a/src/audio.c +++ b/src/audio.c @@ -574,6 +574,7 @@ void PauseMusicStream(void) { TraceLog(INFO, "Pausing music stream"); alSourcePause(currentMusic.source); + musicEnabled = false; } } @@ -581,10 +582,14 @@ void PauseMusicStream(void) void ResumeMusicStream(void) { // Resume music playing... if music available! - if (musicEnabled) + ALenum state; + alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); + + if (state == AL_PAUSED) { - TraceLog(INFO, "Resume music stream"); + TraceLog(INFO, "Resuming music stream"); alSourcePlay(currentMusic.source); + musicEnabled = true; } } -- cgit v1.2.3 From a9e045a1a882e1b4eec85c90b4dc04e803fe4b3f Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 Jan 2015 00:13:17 +0100 Subject: Pause loop execution on window minimized --- src/core.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------ src/raylib.h | 1 + 2 files changed, 45 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 574de334..2c5bad74 100644 --- a/src/core.c +++ b/src/core.c @@ -104,6 +104,7 @@ //---------------------------------------------------------------------------------- #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) +static bool windowMinimized = false; #elif defined(PLATFORM_ANDROID) static struct android_app *app; // Android activity static struct android_poll_source *source; // Android events polling source @@ -243,6 +244,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key); static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow* window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored #endif #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) @@ -397,8 +399,11 @@ void CloseWindow(void) // Detect if KEY_ESCAPE pressed or Close icon pressed bool WindowShouldClose(void) -{ +{ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + // While window minimized, stop loop execution + while (windowMinimized) glfwPollEvents(); + return (glfwWindowShouldClose(window)); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) return windowShouldClose; @@ -962,7 +967,12 @@ static void InitDisplay(int width, int height) if (rlGetVersion() == OPENGL_33) { - //glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + if (configFlags & FLAG_MSAA_4X_HINT) + { + glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + TraceLog(INFO, "Enabled MSAA x4"); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.2 and above! @@ -1009,12 +1019,18 @@ static void InitDisplay(int width, int height) glfwSetMouseButtonCallback(window, MouseButtonCallback); glfwSetCharCallback(window, CharCallback); glfwSetScrollCallback(window, ScrollCallback); + glfwSetWindowIconifyCallback(window, WindowIconifyCallback); glfwMakeContextCurrent(window); - //glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS) - // If not set, swap interval uses GPU v-sync configuration - // Framerate can be setup using SetTargetFPS() + // Enables GPU v-sync, so frames are not limited to screen refresh rate (60Hz -> 60 FPS) + // If not set, swap interval uses GPU v-sync configuration + // Framerate can be setup using SetTargetFPS() + if (configFlags & FLAG_VSYNC_HINT) + { + glfwSwapInterval(1); + TraceLog(INFO, "Trying to enable VSYNC"); + } //glfwGetFramebufferSize(window, &renderWidth, &renderHeight); // Get framebuffer size of current window @@ -1036,6 +1052,7 @@ static void InitDisplay(int width, int height) VC_RECT_T srcRect; #endif + // TODO: if (configFlags & FLAG_MSAA_4X_HINT) activate (EGL_SAMPLES, 4) const EGLint framebufferAttribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI? @@ -1242,6 +1259,25 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) // Background must be also re-cleared ClearBackground(RAYWHITE); } + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow* window, int iconified) +{ + if (iconified) + { + // The window was iconified + PauseMusicStream(); + + windowMinimized = true; + } + else + { + // The window was restored + ResumeMusicStream(); + + windowMinimized = false; + } +} #endif #if defined(PLATFORM_ANDROID) @@ -1643,7 +1679,7 @@ static void PollInputEvents(void) // Register previous mouse states for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; - glfwPollEvents(); // Register keyboard/mouse events + glfwPollEvents(); // Register keyboard/mouse events... and window events! #elif defined(PLATFORM_ANDROID) // TODO: Check virtual keyboard (?) @@ -1654,6 +1690,8 @@ static void PollInputEvents(void) drag = false; // Poll Events (registered events) + // TODO: Enable/disable activityMinimized to block activity if minimized + //while ((ident = ALooper_pollAll(activityMinimized ? 0 : -1, NULL, &events,(void**)&source)) >= 0) while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0) { // Process this event diff --git a/src/raylib.h b/src/raylib.h index 14590d04..ef487a30 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -90,6 +90,7 @@ #define FLAG_SHOW_MOUSE_CURSOR 4 #define FLAG_CENTERED_MODE 8 #define FLAG_MSAA_4X_HINT 16 +#define FLAG_VSYNC_HINT 32 // Keyboard Function Keys #define KEY_SPACE 32 -- cgit v1.2.3 From b92c1bbbf770da306059578cedc310f6d1542efb Mon Sep 17 00:00:00 2001 From: Constantine Tarasenkov Date: Sun, 25 Jan 2015 17:53:29 +0300 Subject: Fixes for makefiles, GLFW 3.1 library for Linux --- src/makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/makefile b/src/makefile index a726b92e..77aa2b5d 100644 --- a/src/makefile +++ b/src/makefile @@ -155,8 +155,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o libraylib.a else ifeq ($(PLATFORM_OS),LINUX) - find . -type f -executable -delete - rm -f *.o libraylib.a + find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -f else del *.o libraylib.a endif -- cgit v1.2.3 From b9e53cd1352e6a8d2f282b29cdab2828a40dfd33 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 2 Feb 2015 00:53:49 +0100 Subject: Removed useless spaces --- src/audio.c | 12 ++++++------ src/models.c | 30 +++++++++++++++++------------- src/raymath.c | 2 +- src/text.c | 46 +++++++++++++++++++++++----------------------- src/textures.c | 12 ++++++------ src/utils.c | 14 +++++++------- 6 files changed, 60 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index bd168158..0e07b8d0 100644 --- a/src/audio.c +++ b/src/audio.c @@ -49,7 +49,7 @@ #if defined(PLATFORM_RPI) // NOTE: On RPI should be lower to avoid frame-stalls #define MUSIC_BUFFER_SIZE 4096*2 // PCM data buffer (short) - 16Kb (RPI) -#else +#else // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... #define MUSIC_BUFFER_SIZE 4096*8 // PCM data buffer (short) - 64Kb #endif @@ -201,7 +201,7 @@ Sound LoadSound(char *fileName) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); - + TraceLog(INFO, "[%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); // Unallocate WAV data @@ -283,7 +283,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (rresFile == NULL) + if (rresFile == NULL) { TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); } @@ -378,7 +378,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); - + TraceLog(INFO, "[%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i)", rresName, wave.sampleRate, wave.bitsPerSample, wave.channels); // Unallocate WAV data @@ -584,7 +584,7 @@ void ResumeMusicStream(void) // Resume music playing... if music available! ALenum state; alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); - + if (state == AL_PAUSED) { TraceLog(INFO, "Resuming music stream"); @@ -875,7 +875,7 @@ static Wave LoadOGG(char *fileName) int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength); TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained); - + TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); stb_vorbis_close(oggFile); diff --git a/src/models.c b/src/models.c index bb172203..dfe521d6 100644 --- a/src/models.c +++ b/src/models.c @@ -452,22 +452,22 @@ void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Co rlBegin(RL_QUADS); rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); rlNormal3f(normals[0].x, normals[0].y, normals[0].z); - rlTexCoord2f(textcoords[0].x, textcoords[0].y); + rlTexCoord2f(textcoords[0].x, textcoords[0].y); rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); - + rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); rlNormal3f(normals[1].x, normals[1].y, normals[1].z); - rlTexCoord2f(textcoords[1].x, textcoords[1].y); + rlTexCoord2f(textcoords[1].x, textcoords[1].y); rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); rlNormal3f(normals[2].x, normals[2].y, normals[2].z); - rlTexCoord2f(textcoords[2].x, textcoords[2].y); + rlTexCoord2f(textcoords[2].x, textcoords[2].y); rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); rlNormal3f(normals[3].x, normals[3].y, normals[3].z); - rlTexCoord2f(textcoords[3].x, textcoords[3].y); + rlTexCoord2f(textcoords[3].x, textcoords[3].y); rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); rlEnd(); } @@ -1144,17 +1144,19 @@ void UnloadModel(Model model) free(model.mesh.texcoords); free(model.mesh.normals); } - + rlDeleteBuffers(model.vboId[0]); rlDeleteBuffers(model.vboId[1]); rlDeleteBuffers(model.vboId[2]); rlDeleteVertexArrays(model.vaoId); + rlDeleteTextures(model.textureId); + rlDeleteShader(model.shaderId); } void SetModelTexture(Model *model, Texture2D texture) { - if (texture.id <= 0) model->textureId = 1; // Default white texture (use mesh color) + if (texture.id <= 0) model->textureId = whiteTexture; // Default white texture (use mesh color) else model->textureId = texture.id; } @@ -1170,6 +1172,8 @@ void DrawModel(Model model, Vector3 position, float scale, Color tint) // Draw a model with extended parameters void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint) { + // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() + rlglDrawModel(model, position, rotation, scale, tint, false); } @@ -1262,11 +1266,11 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec // Bottom-left corner for texture and quad rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); rlVertex3f(a.x, a.y, a.z); - + // Top-left corner for texture and quad rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); rlVertex3f(d.x, d.y, d.z); - + // Top-right corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); rlVertex3f(c.x, c.y, c.z); @@ -1301,7 +1305,7 @@ static VertexData LoadOBJ(const char *fileName) FILE *objFile; objFile = fopen(fileName, "rt"); - + if (objFile == NULL) { TraceLog(WARNING, "[%s] OBJ file could not be opened", fileName); @@ -1547,15 +1551,15 @@ bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, V { /* // Get min and max vertex to construct bounds (AABB) - Vector3 minVertex = tempVertices[0]; + Vector3 minVertex = tempVertices[0]; Vector3 maxVertex = tempVertices[0]; - + for (int i = 1; i < tempVertices.Count; i++) { minVertex = Vector3.Min(minVertex, tempVertices[i]); maxVertex = Vector3.Max(maxVertex, tempVertices[i]); } - + bounds = new BoundingBox(minVertex, maxVertex); */ return false; diff --git a/src/raymath.c b/src/raymath.c index ed45ee92..df098c6a 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -498,7 +498,7 @@ Matrix MatrixRotate(float angle, float x, float y, float z) float c = cosf(angle*DEG2RAD); // cosine float s = sinf(angle*DEG2RAD); // sine float c1 = 1.0f - c; // 1 - c - + float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12, m1 = result.m1, m5 = result.m5, m9 = result.m9, m13 = result.m13, m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14; diff --git a/src/text.c b/src/text.c index c2340af5..0018363a 100644 --- a/src/text.c +++ b/src/text.c @@ -202,7 +202,7 @@ SpriteFont LoadSpriteFont(const char *fileName) Image image = LoadImage(fileName); // At this point we have a pixel array with all the data... - + #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) ConvertToPOT(&image, MAGENTA); #endif @@ -216,7 +216,7 @@ SpriteFont LoadSpriteFont(const char *fileName) spriteFont.numChars = numChars; spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture - + UnloadImage(image); } @@ -565,7 +565,7 @@ static SpriteFont LoadRBMF(const char *fileName) TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName); } - + fclose(rbmfFile); free(rbmfFileData); // Now we can free loaded data from RAM memory @@ -578,20 +578,20 @@ static SpriteFont LoadRBMF(const char *fileName) static SpriteFont LoadTTF(const char *fileName, int fontSize) { SpriteFont font; - + Image image; image.width = 512; image.height = 512; image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); - + unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25); - + // TODO: Load TTF and generate bitmap font and chars data -> REVIEW! - + stbtt_packedchar chardata[128]; // Num characters: 128 (?) -> REVIEW! - + unsigned char *tempBitmap = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); // One channel bitmap returned! - + // REFERENCE /* typedef struct @@ -601,15 +601,15 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) float xoff2,yoff2; } stbtt_packedchar; */ - + stbtt_pack_context pc; - + FILE *ttfFile = fopen(fileName, "rb"); - + fread(ttfBuffer, 1, 1<<25, ttfFile); stbtt_PackBegin(&pc, tempBitmap, image.width, image.height, 0, 1, NULL); - + //stbtt_PackSetOversampling(&pc, 1, 1); //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[0]+32); stbtt_PackSetOversampling(&pc, 2, 2); // Better results @@ -618,11 +618,11 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) //stbtt_PackFontRange(&pc, ttfBuffer, 0, fontSize, 32, 95, chardata[2]+32); stbtt_PackEnd(&pc); - + free(ttfBuffer); // Now we have image data in tempBitmap and chardata filled... - + for (int i = 0; i < 512*512; i++) { image.pixels[i].r = tempBitmap[i]; @@ -630,15 +630,15 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) image.pixels[i].b = tempBitmap[i]; image.pixels[i].a = 255; } - + free(tempBitmap); - + // REFERENCE EXAMPLE /* //To draw, provide *text, posX, posY //stbtt_aligned_quad letter; //stbtt_GetPackedQuad(chardata[0], BITMAP_W, BITMAP_H, *text++, &posX, &posY, &letter, font ? 0 : integer_align); - + void print(float x, float y, int fontNum, char *text) { glEnable(GL_TEXTURE_2D); @@ -651,14 +651,14 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) } glEnd(); } - + print(100,160, 0, "This is a test"); */ - + font.numChars = 95; font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); font.texture = LoadTextureFromImage(image, false); - + //stbtt_aligned_quad letter; //int x = 0, y = 0; @@ -673,8 +673,8 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) font.charSet[i].w = chardata[i + 32].x1 - chardata[i + 32].x0; font.charSet[i].h = chardata[i + 32].y1 - chardata[i + 32].y0; } - + UnloadImage(image); - + return font; } \ No newline at end of file diff --git a/src/textures.c b/src/textures.c index f701c380..e8bf6088 100644 --- a/src/textures.c +++ b/src/textures.c @@ -187,7 +187,7 @@ Image LoadImageFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (rresFile == NULL) + if (rresFile == NULL) { TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); } @@ -296,7 +296,7 @@ Image LoadImageFromRES(const char *rresName, int resId) Texture2D LoadTexture(const char *fileName) { Texture2D texture; - + // Init texture to default values texture.id = 0; texture.width = 0; @@ -340,7 +340,7 @@ Texture2D LoadTexture(const char *fileName) else { Image image = LoadImage(fileName); - + if (image.pixels != NULL) { #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) @@ -399,9 +399,9 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) Texture2D CreateTexture(Image image, bool genMipmaps) { Texture2D texture; - + texture = LoadTextureFromImage(image, genMipmaps); - + TraceLog(INFO, "Created texture id: %i", texture.id); return texture; @@ -589,7 +589,7 @@ static ImageEx LoadDDS(const char *fileName) ImageEx image; ddsHeader header; - + image.data = NULL; image.width = 0; image.height = 0; diff --git a/src/utils.c b/src/utils.c index dd08f5f8..8e42e533 100644 --- a/src/utils.c +++ b/src/utils.c @@ -235,18 +235,18 @@ void TraceLog(int msgType, const char *text, ...) } // Initialize asset manager from android app -void InitAssetManager(AAssetManager *manager) +void InitAssetManager(AAssetManager *manager) { assetManager = manager; } // Replacement for fopen -FILE *android_fopen(const char *fileName, const char *mode) +FILE *android_fopen(const char *fileName, const char *mode) { if (mode[0] == 'w') return NULL; AAsset *asset = AAssetManager_open(assetManager, fileName, 0); - + if(!asset) return NULL; return funopen(asset, android_read, android_write, android_seek, android_close); @@ -292,24 +292,24 @@ int GetNextPOT(int num) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) -static int android_read(void *cookie, char *buf, int size) +static int android_read(void *cookie, char *buf, int size) { return AAsset_read((AAsset *)cookie, buf, size); } -static int android_write(void *cookie, const char *buf, int size) +static int android_write(void *cookie, const char *buf, int size) { TraceLog(ERROR, "Can't provide write access to the APK"); return EACCES; } -static fpos_t android_seek(void *cookie, fpos_t offset, int whence) +static fpos_t android_seek(void *cookie, fpos_t offset, int whence) { return AAsset_seek((AAsset *)cookie, offset, whence); } -static int android_close(void *cookie) +static int android_close(void *cookie) { AAsset_close((AAsset *)cookie); return 0; -- cgit v1.2.3 From 42c92e4f2e2f12589434b91861fd98d11125e449 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 2 Feb 2015 00:57:08 +0100 Subject: Add support for custom shaders Custom shaders for models Postprocessig on FBO (in progress) Some useless spaces removed --- src/core.c | 113 +++++++----- src/raylib.h | 16 +- src/rlgl.c | 550 ++++++++++++++++++++++++++++++++++++++--------------------- src/rlgl.h | 5 + 4 files changed, 443 insertions(+), 241 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 2c5bad74..efff6206 100644 --- a/src/core.c +++ b/src/core.c @@ -153,8 +153,7 @@ static int gamepadStream = -1; // Gamepad device file descripto #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) static EGLDisplay display; // Native display device (physical screen connection) static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) -static EGLContext context; // Graphic context, mode in which drawing can be done - +static EGLContext context; // Graphic context, mode in which drawing can be done static uint64_t baseTime; // Base time measure for hi-res timer static bool windowShouldClose = false; // Flag to set window for closing #endif @@ -207,6 +206,12 @@ static double targetTime = 0.0; // Desired time for one frame, if 0 static char configFlags = 0; static bool showLogo = false; +static bool customCamera = true; +//static int cameraMode = CUSTOM; // FREE, FIRST_PERSON, THIRD_PERSON + +static bool enabledPostpro = false; +static unsigned int fboShader = 0; + //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) //---------------------------------------------------------------------------------- @@ -264,7 +269,7 @@ static void CommandCallback(struct android_app *app, int32_t cmd); // void InitWindow(int width, int height, const char *title) { TraceLog(INFO, "Initializing raylib (v1.2.2)"); - + // Store window title (could be useful...) windowTitle = title; @@ -303,7 +308,7 @@ void InitWindow(int width, int height, const char *title) void InitWindow(int width, int height, struct android_app *state) { TraceLog(INFO, "Initializing raylib (v1.2.2)"); - + app_dummy(); screenWidth = width; @@ -399,7 +404,7 @@ void CloseWindow(void) // Detect if KEY_ESCAPE pressed or Close icon pressed bool WindowShouldClose(void) -{ +{ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // While window minimized, stop loop execution while (windowMinimized) glfwPollEvents(); @@ -434,7 +439,7 @@ void SetCustomCursor(const char *cursorImage) cursor = LoadTexture(cursorImage); #if defined(PLATFORM_DESKTOP) - // NOTE: emscripten not implemented + // NOTE: emscripten not implemented glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); #endif customCursor = true; @@ -473,6 +478,8 @@ void BeginDrawing(void) currentTime = GetTime(); // Number of elapsed seconds since InitTimer() was called updateTime = currentTime - previousTime; previousTime = currentTime; + + if (enabledPostpro) rlEnableFBO(); rlClearScreenBuffers(); @@ -480,14 +487,17 @@ void BeginDrawing(void) rlMultMatrixf(GetMatrixVector(downscaleView)); // If downscale required, apply it here -// rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1 + //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1 // NOTE: Not required with OpenGL 3.3+ } // End canvas drawing and Swap Buffers (Double Buffering) void EndDrawing(void) { - rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) + + // TODO: Set postprocessing shader to be passed: SetPostproShader()? + if (enabledPostpro) rlglDrawPostpro(fboShader); // Draw postprocessing effect (shader) SwapBuffers(); // Copy back buffer to front buffer PollInputEvents(); // Poll user events @@ -515,7 +525,7 @@ void EndDrawing(void) // Initializes 3D mode for drawing (Camera setup) void Begin3dMode(Camera camera) { - rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlMatrixMode(RL_PROJECTION); // Switch to projection matrix @@ -533,14 +543,21 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (MODELVIEW) // Setup Camera view - Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up); - rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) + if (customCamera) + { + Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up); + rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) + } + else + { + // TODO: Add support for multiple automatic camera modes + } } // Ends 3D mode and returns to default 2D orthographic mode void End3dMode(void) { - rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlMatrixMode(RL_PROJECTION); // Switch to projection matrix rlPopMatrix(); // Restore previous matrix (PROJECTION) from matrix stack @@ -738,7 +755,7 @@ void SetMousePosition(Vector2 position) { mousePosition = position; #if defined(PLATFORM_DESKTOP) - // NOTE: emscripten not implemented + // NOTE: emscripten not implemented glfwSetCursorPos(window, position.x, position.y); #endif } @@ -900,7 +917,7 @@ Vector2 GetTouchPosition(void) //eventDrag int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] ); - + if (iIndex == -1) return false; float x = AMotionEvent_getX(eventDrag, iIndex); @@ -951,7 +968,7 @@ static void InitDisplay(int width, int height) displayWidth = screenWidth; displayHeight = screenHeight; #endif - + glfwDefaultWindowHints(); // Set default windows hints glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable @@ -961,7 +978,7 @@ static void InitDisplay(int width, int height) //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // Default OpenGL API to use. Alternative: GLFW_OPENGL_ES_API //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers - // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version + // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version // with forward compatibility to older OpenGL versions. // For example, if using OpenGL 1.1, driver can provide a 3.3 context fordward compatible. @@ -972,7 +989,7 @@ static void InitDisplay(int width, int height) glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 TraceLog(INFO, "Enabled MSAA x4"); } - + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.2 and above! @@ -1188,6 +1205,18 @@ void InitGraphics(void) #endif } +void InitPostShader(void) +{ + rlglInitPostpro(); + + enabledPostpro = true; +} + +void SetPostShader(unsigned int shader) +{ + fboShader = shader; +} + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) @@ -1233,7 +1262,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int static void CharCallback(GLFWwindow *window, unsigned int key) { lastKeyPressed = key; - + //TraceLog(INFO, "Char Callback Key pressed: %i\n", key); } @@ -1267,14 +1296,14 @@ static void WindowIconifyCallback(GLFWwindow* window, int iconified) { // The window was iconified PauseMusicStream(); - + windowMinimized = true; } else { // The window was restored ResumeMusicStream(); - + windowMinimized = false; } } @@ -1353,7 +1382,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) //size_t pointerCount = AMotionEvent_getPointerCount(event); //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1 //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area - + // Detect DOUBLE TAP event bool tapDetected = touchTap; @@ -1362,19 +1391,19 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) case AMOTION_EVENT_ACTION_DOWN: { int64_t eventTime = AMotionEvent_getEventTime(event); - + if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT) { float x = AMotionEvent_getX(event, 0) - lastTapX; float y = AMotionEvent_getY(event, 0) - lastTapY; - + float densityFactor = 1.0f; - + if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor)) { // Doubletap detected doubleTap = true; - + } } } break; @@ -1385,12 +1414,12 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) lastTapTime = AMotionEvent_getEventTime(event); lastTapX = AMotionEvent_getX(event, 0); lastTapY = AMotionEvent_getY(event, 0); - + } } break; } - - + + // Detect DRAG event //int32_t action = AMotionEvent_getAction(event); @@ -1407,7 +1436,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0); indexPosition++; TraceLog(INFO, "ACTION_DOWN"); - + //ret = GESTURE_STATE_START; } break; case AMOTION_EVENT_ACTION_POINTER_DOWN: @@ -1415,7 +1444,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) stdVector[indexPosition] = AMotionEvent_getPointerId(event, index); indexPosition++; TraceLog(INFO, "ACTION_POINTER_DOWN"); - + } break; case AMOTION_EVENT_ACTION_UP: { @@ -1423,12 +1452,12 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) indexPosition--; //ret = GESTURE_STATE_END; TraceLog(INFO, "ACTION_UP"); - + } break; case AMOTION_EVENT_ACTION_POINTER_UP: { int32_t releasedPointerId = AMotionEvent_getPointerId(event, index); - + int i = 0; for (i = 0; i < MAX_TOUCH_POINTS; i++) { @@ -1438,27 +1467,27 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) { stdVector[k] = stdVector[k + 1]; } - + //indexPosition--; indexPosition = 0; break; } } - + if (i <= 1) { // Reset pinch or drag //if (count == 2) //ret = GESTURE_STATE_START; } TraceLog(INFO, "ACTION_POINTER_UP"); - + } break; case AMOTION_EVENT_ACTION_MOVE: { if (count == 1) { //TraceLog(INFO, "DRAG gesture detected"); - + drag = true; //ret = GESTURE_STATE_MOVE; } else break; @@ -1470,7 +1499,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) } //-------------------------------------------------------------------- - + return 1; } else if (type == AINPUT_EVENT_TYPE_KEY) @@ -1672,13 +1701,13 @@ static void PollInputEvents(void) // Keyboard polling // Automatically managed by GLFW3 through callback lastKeyPressed = -1; - + // Register previous keys states for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; - + // Register previous mouse states for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; - + glfwPollEvents(); // Register keyboard/mouse events... and window events! #elif defined(PLATFORM_ANDROID) @@ -1702,8 +1731,8 @@ static void PollInputEvents(void) { // NOTE: Never close window, native activity is controlled by the system! //TraceLog(INFO, "Closing Window..."); - //windowShouldClose = true; - + //windowShouldClose = true; + //ANativeActivity_finish(app->activity); } } diff --git a/src/raylib.h b/src/raylib.h index ef487a30..3c849bdd 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -92,7 +92,7 @@ #define FLAG_MSAA_4X_HINT 16 #define FLAG_VSYNC_HINT 32 -// Keyboard Function Keys +// Keyboard Function Keys #define KEY_SPACE 32 #define KEY_ESCAPE 256 #define KEY_ENTER 257 @@ -268,9 +268,15 @@ typedef struct Model { unsigned int vaoId; unsigned int vboId[4]; unsigned int textureId; + unsigned int shaderId; //Matrix transform; } Model; +// Shader type +typedef struct Shader { + unsigned int id; +} Shader; + // Sound source type typedef struct Sound { unsigned int source; @@ -334,6 +340,9 @@ Color Fade(Color color, float alpha); // Color fade-in or void SetupFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) +void InitPostShader(void); +void SetPostShader(unsigned int shader); + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ @@ -449,7 +458,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires -void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad +void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) @@ -466,6 +475,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight); Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) void UnloadModel(Model model); // Unload 3d model from memory void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model +void SetModelShader(Model *model, unsigned int shader); void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint); // Draw a model with extended parameters @@ -474,6 +484,8 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color color); void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec +unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) + //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) //------------------------------------------------------------------------------------ diff --git a/src/rlgl.c b/src/rlgl.c index 7977ea84..67ef0a48 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -32,20 +32,20 @@ #include // Declares malloc() and free() for memory management, rand() #if defined(GRAPHICS_API_OPENGL_11) - #ifdef __APPLE__ // OpenGL include for OSX - #include - #else - #include // Basic OpenGL include - #endif + #ifdef __APPLE__ // OpenGL include for OSX + #include + #else + #include // Basic OpenGL include + #endif #endif #if defined(GRAPHICS_API_OPENGL_33) #define GLEW_STATIC - #ifdef __APPLE__ // OpenGL include for OSX + #ifdef __APPLE__ // OpenGL include for OSX #include - #else - #include // Extensions loading lib - //#include "glad.h" // TODO: Other extensions loading lib? --> REVIEW + #else + #include // Extensions loading lib + //#include "glad.h" // TODO: Other extensions loading lib? --> REVIEW #endif #endif @@ -164,6 +164,12 @@ static GLuint simpleVertexLoc, simpleTexcoordLoc, simpleNormalLoc, simpleColorLo static GLuint simpleProjectionMatrixLoc, simpleModelviewMatrixLoc; static GLuint simpleTextureLoc; +// Custom Shader program attibutes binding locations +static GLuint customVertexLoc, customTexcoordLoc, customNormalLoc, customColorLoc; +static GLuint customProjectionMatrixLoc, customModelviewMatrixLoc; +static GLuint customTextureLoc; +static bool customShader = false; + // Vertex Array Objects (VAO) static GLuint vaoLines, vaoTriangles, vaoQuads; @@ -182,6 +188,9 @@ static bool useTempBuffer = false; // Support for VAOs (OpenGL ES2 could not support VAO extensions) static bool vaoSupported = false; + +// Framebuffer object and texture +static GLuint fbo, fboColorTexture, fboDepthTexture, fboShader = 0; #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -208,7 +217,6 @@ static void InitializeBuffersGPU(void); static void UpdateBuffers(void); // Custom shader files loading (external) -static GLuint LoadCustomShader(char *vertexFileName, char *fragmentFileName); static char *TextFileRead(char *fn); #endif @@ -313,21 +321,21 @@ void rlRotatef(float angleDeg, float x, float y, float z) // TODO: Support rotation in multiple axes Matrix rot = MatrixIdentity(); - // OPTION 1: It works... + // OPTION 1: It works... if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD); else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD); else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD); - - // OPTION 2: Requires review... - //Vector3 vec = (Vector3){ 0, 1, 0 }; + + // OPTION 2: Requires review... + //Vector3 vec = (Vector3){ 0, 1, 0 }; //VectorNormalize(&vec); //rot = MatrixFromAxisAngle(vec, angleDeg*DEG2RAD); // Working? - + // OPTION 3: TODO: Review, it doesn't work! //Vector3 vec = (Vector3){ x, y, z }; //VectorNormalize(&vec); //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x); - + MatrixTranspose(&rot); *currentMatrix = MatrixMultiply(*currentMatrix, rot); @@ -696,6 +704,17 @@ void rlDeleteTextures(unsigned int id) glDeleteTextures(1, &id); } +// Unload shader from GPU memory +void rlDeleteShader(unsigned int id) +{ + glDeleteProgram(id); +} + +void rlEnableFBO(void) +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); +} + // Unload vertex data (VAO) from GPU memory void rlDeleteVertexArrays(unsigned int id) { @@ -786,7 +805,7 @@ void rlglInit(void) #endif #if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... #if !defined(PLATFORM_WEB) glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); @@ -851,7 +870,8 @@ void rlglInit(void) // Init default Shader (GLSL 110) -> Common for GL 3.3+ and ES2 defaultShaderProgram = LoadDefaultShader(); simpleShaderProgram = LoadSimpleShader(); - //customShaderProgram = LoadShaders("simple150.vert", "simple150.frag"); + //simpleShaderProgram = LoadCustomShader("custom.vs", "custom.fs"); // Works ok + //customShaderProgram = LoadCustomShader("simple150.vert", "simple150.frag"); // Get handles to GLSL input vars locations for defaultShaderProgram //------------------------------------------------------------------- @@ -866,7 +886,7 @@ void rlglInit(void) // Get handles to GLSL uniform vars locations (fragment-shader) defaultTextureLoc = glGetUniformLocation(defaultShaderProgram, "texture0"); //-------------------------------------------------------------------- - + // Get handles to GLSL input vars locations for simpleShaderProgram //------------------------------------------------------------------- simpleVertexLoc = glGetAttribLocation(simpleShaderProgram, "vertexPosition"); @@ -908,10 +928,53 @@ void rlglInit(void) } drawsCounter = 1; - draws[drawsCounter - 1].textureId = whiteTexture; + draws[drawsCounter - 1].textureId = whiteTexture; #endif } +// Init postpro system +void rlglInitPostpro(void) +{ + // Create the texture that will serve as the color attachment for the framebuffer + glGenTextures(1, &fboColorTexture); + glBindTexture(GL_TEXTURE_2D, fboColorTexture); + 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_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GetScreenWidth(), GetScreenHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + // Create the texture that will serve as the depth attachment for the framebuffer. + glGenTextures(1, &fboDepthTexture); + glBindTexture(GL_TEXTURE_2D, fboDepthTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GetScreenWidth(), GetScreenHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + // Create the framebuffer object + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + // Attach colort texture and depth texture to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboColorTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fboDepthTexture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) TraceLog(WARNING, "Framebuffer object could not be created..."); + else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", fbo); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + fboShader = 0; + + // TODO: Init simple quad VAO and data here? +} + // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) { @@ -950,6 +1013,7 @@ void rlglClose(void) //glDeleteShader(v); //glDeleteShader(f); glDeleteProgram(defaultShaderProgram); + glDeleteProgram(simpleShaderProgram); // Free vertex arrays memory free(lines.vertices); @@ -965,6 +1029,8 @@ void rlglClose(void) // Free GPU texture glDeleteTextures(1, &whiteTexture); + + if (fbo != 0) glDeleteFramebuffers(1, &fbo); free(draws); #endif @@ -977,13 +1043,16 @@ void rlglDraw(void) if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { - glUseProgram(defaultShaderProgram); // Use our shader + if (fbo == 0) glUseProgram(defaultShaderProgram); // Use our default shader + else glUseProgram(fboShader); // Use our postpro shader + glUseProgram(defaultShaderProgram); + glUniformMatrix4fv(defaultProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); glUniformMatrix4fv(defaultModelviewMatrixLoc, 1, false, GetMatrixVector(modelview)); glUniform1i(defaultTextureLoc, 0); } - + // NOTE: We draw in this order: triangle shapes, textured quads and lines if (triangles.vCounter > 0) @@ -1116,6 +1185,67 @@ void rlglDraw(void) #endif } +void rlglDrawPostpro(unsigned int shaderId) +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // TODO: Draw screen quad with texture +/* + const float quadPositions[] = { 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0, + -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0 }; + const float quadTexcoords[] = { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0 }; + + glBindBuffer(GL_ARRAY_BUFFER, quadVbo); + + glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), quadPositions); + glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), quadTexcoords); + + glEnableVertexAttribArray(ATTRIB_VERTEX); + glEnableVertexAttribArray(ATTRIB_TEXCOORD0); + + glBindTexture(GL_TEXTURE_2D, fboColorTexture); + + glDrawArrays(GL_TRIANGLES, 0, 2*3); + + // Quad render using triangle strip + glBindBuffer(GL_ARRAY_BUFFER, uiVBO[1]); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); +*/ + rlEnableTexture(fboColorTexture); + + rlPushMatrix(); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + // Bottom-left corner for texture and quad + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(0.0f, GetScreenHeight()); + + // Top-right corner for texture and quad + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f(GetScreenWidth(), GetScreenHeight()); + + // Top-left corner for texture and quad + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f(GetScreenWidth(), 0.0f); + rlEnd(); + rlPopMatrix(); + + fboShader = shaderId; + + rlglDraw(); +} + // Draw a 3d model void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires) { @@ -1159,23 +1289,35 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUseProgram(simpleShaderProgram); // Use our simple shader + if (customShader) glUseProgram(model.shaderId); + else glUseProgram(simpleShaderProgram); // Use our simple shader VectorScale(&rotation, DEG2RAD); - + // Get transform matrix (rotation -> scale -> translation) Matrix transform = MatrixTransform(position, rotation, scale); Matrix modelviewworld = MatrixMultiply(transform, modelview); // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader - glUniformMatrix4fv(simpleProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(simpleModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld)); - glUniform1i(simpleTextureLoc, 0); + if (customShader) + { + glUniformMatrix4fv(customProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(customModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld)); + glUniform1i(customTextureLoc, 0); + } + else + { + glUniformMatrix4fv(simpleProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(simpleModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld)); + glUniform1i(simpleTextureLoc, 0); + } // Apply color tinting to model // NOTE: Just update one uniform on fragment shader float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 }; - glUniform4fv(simpleColorLoc, 1, vColor); + + if (customShader) glUniform4fv(customColorLoc, 1, vColor); + else glUniform4fv(simpleColorLoc, 1, vColor); if (vaoSupported) { @@ -1206,7 +1348,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal if (vaoSupported) glBindVertexArray(0); // Unbind VAO else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs - + glUseProgram(0); #endif @@ -1376,7 +1518,7 @@ Model rlglLoadModel(VertexData mesh) model.vboId[2] = 0; // Normals VBO #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - model.textureId = 1; // Default whiteTexture + model.textureId = whiteTexture; // Default whiteTexture GLuint vaoModel = 0; // Vertex Array Objects (VAO) GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) @@ -1412,7 +1554,7 @@ Model rlglLoadModel(VertexData mesh) model.vboId[0] = vertexBuffer[0]; // Vertex position VBO model.vboId[1] = vertexBuffer[1]; // Texcoords VBO model.vboId[2] = vertexBuffer[2]; // Normals VBO - + if (vaoSupported) { if (vaoModel > 0) @@ -1510,6 +1652,102 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh return id; } +// Load a shader (vertex shader + fragment shader) from text data +unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) +{ + unsigned int program; + GLuint vertexShader; + GLuint fragmentShader; + + vertexShader = glCreateShader(GL_VERTEX_SHADER); + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + const char *pvs = vShaderStr; + const char *pfs = fShaderStr; + + glShaderSource(vertexShader, 1, &pvs, NULL); + glShaderSource(fragmentShader, 1, &pfs, NULL); + + GLint success = 0; + + glCompileShader(vertexShader); + + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) + { + TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader); + + int maxLength = 0; + int length; + + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); + + char log[maxLength]; + + glGetShaderInfoLog(vertexShader, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + } + else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); + + glCompileShader(fragmentShader); + + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) + { + TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader); + + int maxLength = 0; + int length; + + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); + + char log[maxLength]; + + glGetShaderInfoLog(fragmentShader, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + } + else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); + + program = glCreateProgram(); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program); + + int maxLength = 0; + int length; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + char log[maxLength]; + + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(INFO, "%s", log); + + glDeleteProgram(program); + + program = 0; + } + else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + return program; +} + // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { @@ -1534,6 +1772,66 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } +unsigned int LoadCustomShader(char *vsFileName, char *fsFileName) +{ + // Shaders loading from external text file + char *vShaderStr = TextFileRead(vsFileName); + char *fShaderStr = TextFileRead(fsFileName); + + unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); + + if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shaderId); + else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shaderId); + + return shaderId; + + // Shader strings must be freed + free(vShaderStr); + free(fShaderStr); + + return shaderId; +} + +// Link shader to model +void SetModelShader(Model *model, unsigned int shader) +{ + // Get handles to GLSL input vars locations for simpleShaderProgram + customVertexLoc = glGetAttribLocation(shader, "vertexPosition"); + customTexcoordLoc = glGetAttribLocation(shader, "vertexTexCoord"); + customNormalLoc = glGetAttribLocation(shader, "vertexNormal"); + + // Get handles to GLSL uniform vars locations (vertex-shader) + customModelviewMatrixLoc = glGetUniformLocation(shader, "modelviewMatrix"); + customProjectionMatrixLoc = glGetUniformLocation(shader, "projectionMatrix"); + + // Get handles to GLSL uniform vars locations (fragment-shader) + customTextureLoc = glGetUniformLocation(shader, "texture0"); + customColorLoc = glGetUniformLocation(shader, "fragColor"); + + if (vaoSupported) glBindVertexArray(model->vaoId); + + // Enable vertex attributes: position + glBindBuffer(GL_ARRAY_BUFFER, model->vboId[0]); + glEnableVertexAttribArray(customVertexLoc); + glVertexAttribPointer(customVertexLoc, 3, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: texcoords + glBindBuffer(GL_ARRAY_BUFFER, model->vboId[1]); + glEnableVertexAttribArray(customTexcoordLoc); + glVertexAttribPointer(customTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: normals + glBindBuffer(GL_ARRAY_BUFFER, model->vboId[2]); + glEnableVertexAttribArray(customNormalLoc); + glVertexAttribPointer(customNormalLoc, 3, GL_FLOAT, 0, 0, 0); + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + + model->shaderId = shader; + + customShader = true; +} + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix() @@ -1559,10 +1857,12 @@ void PrintModelviewMatrix() static GLuint LoadDefaultShader(void) { // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 + // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ + // Just defined #version 330 despite shader is #version 110 // Vertex shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 + char vShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! #endif @@ -1582,7 +1882,7 @@ static GLuint LoadDefaultShader(void) // Fragment shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 + char fShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! "precision mediump float; \n" // WebGL, required for emscripten @@ -1595,63 +1895,12 @@ static GLuint LoadDefaultShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" "} \n"; - GLuint program; - GLuint vertexShader; - GLuint fragmentShader; - - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - const char *pvs = vShaderStr; - const char *pfs = fShaderStr; - - glShaderSource(vertexShader, 1, &pvs, NULL); - glShaderSource(fragmentShader, 1, &pfs, NULL); - - GLint success = 0; - - glCompileShader(vertexShader); - - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - - if (success != GL_TRUE) TraceLog(WARNING, "[VSHDR ID %i] Failed to compile default vertex shader...", vertexShader); - else TraceLog(INFO, "[VSHDR ID %i] Default vertex shader compiled successfully", vertexShader); - - glCompileShader(fragmentShader); - - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - - if (success != GL_TRUE) TraceLog(WARNING, "[FSHDR ID %i] Failed to compile default fragment shader...", fragmentShader); - else TraceLog(INFO, "[FSHDR ID %i] Default fragment shader compiled successfully", fragmentShader); - - program = glCreateProgram(); - - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - - glLinkProgram(program); - - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) - { - int maxLength; - int length; + unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); - - char log[maxLength]; - - glGetProgramInfoLog(program, maxLength, &length, log); - - TraceLog(INFO, "Shader program fail log: %s", log); - } - else TraceLog(INFO, "[SHDR ID %i] Default shader program loaded successfully", program); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); + if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shaderId); + else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shaderId); - return program; + return shaderId; } // Load Simple Shader (Vertex and Fragment) @@ -1659,10 +1908,12 @@ static GLuint LoadDefaultShader(void) static GLuint LoadSimpleShader(void) { // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 + // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ + // Just defined #version 330 despite shader is #version 110 // Vertex shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 + char vShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! #endif @@ -1680,7 +1931,7 @@ static GLuint LoadSimpleShader(void) // Fragment shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 + char fShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) @@ -1693,117 +1944,22 @@ static GLuint LoadSimpleShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" "} \n"; - GLuint program; - GLuint vertexShader; - GLuint fragmentShader; - - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - const char *pvs = vShaderStr; - const char *pfs = fShaderStr; - - glShaderSource(vertexShader, 1, &pvs, NULL); - glShaderSource(fragmentShader, 1, &pfs, NULL); - - GLint success = 0; - - glCompileShader(vertexShader); - - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - - if (success != GL_TRUE) TraceLog(WARNING, "[VSHDR ID %i] Failed to compile simple vertex shader...", vertexShader); - else TraceLog(INFO, "[VSHDR ID %i] Simple vertex shader compiled successfully", vertexShader); - - glCompileShader(fragmentShader); - - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - - if (success != GL_TRUE) TraceLog(WARNING, "[FSHDR ID %i] Failed to compile simple fragment shader...", fragmentShader); - else TraceLog(INFO, "[FSHDR ID %i] Simple fragment shader compiled successfully", fragmentShader); - - program = glCreateProgram(); - - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - - glLinkProgram(program); - - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) - { - int maxLength; - int length; - - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); - - char log[maxLength]; - - glGetProgramInfoLog(program, maxLength, &length, log); - - TraceLog(INFO, "Shader program fail log: %s", log); - } - else TraceLog(INFO, "[SHDR ID %i] Simple shader program loaded successfully", program); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - return program; -} - -// Load shaders from text files -static GLuint LoadCustomShader(char *vertexFileName, char *fragmentFileName) -{ - // Shaders loading from external text file - char *vShaderStr = TextFileRead(vertexFileName); - char *fShaderStr = TextFileRead(fragmentFileName); - - GLuint program; - GLuint vertexShader; - GLuint fragmentShader; - - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - const char *pvs = vShaderStr; - const char *pfs = fShaderStr; - - glShaderSource(vertexShader, 1, &pvs, NULL); - glShaderSource(fragmentShader, 1, &pfs, NULL); - - glCompileShader(vertexShader); - glCompileShader(fragmentShader); - - TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); - TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); - - program = glCreateProgram(); - - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - - glLinkProgram(program); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - free(vShaderStr); - free(fShaderStr); + unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); - TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); + if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shaderId); + else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shaderId); - return program; + return shaderId; } -// Read shader text file +// Read text file // NOTE: text chars array should be freed manually static char *TextFileRead(char *fileName) { FILE *textFile; char *text = NULL; - int count=0; + int count = 0; if (fileName != NULL) { @@ -1817,7 +1973,7 @@ static char *TextFileRead(char *fileName) if (count > 0) { - text = (char *)malloc(sizeof(char) * (count+1)); + text = (char *)malloc(sizeof(char) * (count + 1)); count = fread(text, sizeof(char), count, textFile); text[count] = '\0'; } @@ -1826,7 +1982,7 @@ static char *TextFileRead(char *fileName) } else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); } - + return text; } @@ -1925,7 +2081,7 @@ static void InitializeBuffersGPU(void) glGenVertexArrays(1, &vaoTriangles); glBindVertexArray(vaoTriangles); } - + // Create buffers for our vertex data glGenBuffers(2, trianglesBuffer); @@ -1950,7 +2106,7 @@ static void InitializeBuffersGPU(void) glGenVertexArrays(1, &vaoQuads); glBindVertexArray(vaoQuads); } - + // Create buffers for our vertex data glGenBuffers(4, quadsBuffer); @@ -1994,7 +2150,7 @@ static void UpdateBuffers(void) { // Activate Lines VAO if (vaoSupported) glBindVertexArray(vaoLines); - + // Lines - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); @@ -2011,7 +2167,7 @@ static void UpdateBuffers(void) { // Activate Triangles VAO if (vaoSupported) glBindVertexArray(vaoTriangles); - + // Triangles - vertex positions buffer glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); diff --git a/src/rlgl.h b/src/rlgl.h index b42b388b..94cf6072 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -145,22 +145,27 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) void rlEnableTexture(unsigned int id); // Enable texture usage void rlDisableTexture(void); // Disable texture usage void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU +void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) int rlGetVersion(void); // Returns current OpenGL version +void rlEnableFBO(void); //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ void rlglInit(void); // Initialize rlgl (shaders, VAO, VBO...) +void rlglInitPostpro(void); // Initialize postprocessing system void rlglClose(void); // De-init rlgl void rlglDraw(void); // Draw VAO/VBO +void rlglDrawPostpro(unsigned int shaderId); // Draw with postpro shader void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); +unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires); -- cgit v1.2.3 From 9ab67182349aa7fd3e85c090bfe1afd8c5f61339 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 2 Feb 2015 16:39:30 +0100 Subject: Corrected bug, OGG loading on Android --- src/stb_vorbis.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h index eb3afe3b..8b91bd36 100644 --- a/src/stb_vorbis.h +++ b/src/stb_vorbis.h @@ -58,6 +58,11 @@ #include #endif +// NOTE: Added to work with raylib on Android +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + #ifdef __linux #include #endif -- cgit v1.2.3 From b926765ce009e102cad7c597a3cbae9a9bc15b47 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Mon, 2 Feb 2015 20:05:18 +0100 Subject: Added functions and improvements LoadCubicmap() supports custom texture rectangle (from texture atlas) for every cubic-face. CheckCollision*() for spheres and bounding-boxes. --- src/models.c | 229 +++++++++++++++++++++++++++++++++++++++++++---------------- src/raylib.h | 4 ++ 2 files changed, 172 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index dfe521d6..a53cf5c8 100644 --- a/src/models.c +++ b/src/models.c @@ -836,12 +836,43 @@ Model LoadCubicmap(Image cubesmap) float w = mapCubeSide; float h = mapCubeSide; - float h2 = mapCubeSide; + float h2 = mapCubeSide * 1.5; Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles * 3 * sizeof(Vector2)); Vector3 *mapNormals = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); - + + // Define the 6 normals of the cube, we will combine them accordingly later... + Vector3 n1 = { 1.0f, 0.0f, 0.0f }; + Vector3 n2 = { -1.0f, 0.0f, 0.0f }; + Vector3 n3 = { 0.0f, 1.0f, 0.0f }; + Vector3 n4 = { 0.0f, -1.0f, 0.0f }; + Vector3 n5 = { 0.0f, 0.0f, 1.0f }; + Vector3 n6 = { 0.0f, 0.0f, -1.0f }; + + // Define the 4 texture coordinates of the cube, we will combine them accordingly later... + // TODO: Use texture rectangles to define different textures for top-bottom-front-back-right-left (6) + /* + Vector2 vt2 = { 0.0f, 0.0f }; + Vector2 vt1 = { 0.0f, 1.0f }; + Vector2 vt4 = { 1.0f, 0.0f }; + Vector2 vt3 = { 1.0f, 1.0f }; + */ + + typedef struct RectangleF { + float x; + float y; + float width; + float height; + } RectangleF; + + RectangleF rightTexUV = { 0, 0, 0.5, 0.5 }; + RectangleF leftTexUV = { 0.5, 0, 0.25, 0.25 }; + RectangleF frontTexUV = { 0.75, 0, 0.25, 0.25 }; + RectangleF backTexUV = { 0.5, 0.25, 0.25, 0.25 }; + RectangleF topTexUV = { 0, 0.5, 0.5, 0.5 }; + RectangleF bottomTexUV = { 0.5, 0.5, 0.5, 0.5 }; + for (int z = 0; z < mapHeight; z += mapCubeSide) { for (int x = 0; x < mapWidth; x += mapCubeSide) @@ -856,21 +887,6 @@ Model LoadCubicmap(Image cubesmap) Vector3 v7 = { x - w/2, 0, z + h/2 }; Vector3 v8 = { x + w/2, 0, z + h/2 }; - // Define the 6 normals of the cube, we will combine them accordingly later... - Vector3 n1 = { 1.0f, 0.0f, 0.0f }; - Vector3 n2 = { -1.0f, 0.0f, 0.0f }; - Vector3 n3 = { 0.0f, 1.0f, 0.0f }; - Vector3 n4 = { 0.0f, -1.0f, 0.0f }; - Vector3 n5 = { 0.0f, 0.0f, 1.0f }; - Vector3 n6 = { 0.0f, 0.0f, -1.0f }; - - // Define the 4 texture coordinates of the cube, we will combine them accordingly later... - // TODO: Use texture rectangles to define different textures for top-bottom-front-back-right-left (6) - Vector2 vt2 = { 0.0f, 0.0f }; - Vector2 vt1 = { 0.0f, 1.0f }; - Vector2 vt4 = { 1.0f, 0.0f }; - Vector2 vt3 = { 1.0f, 1.0f }; - // We check pixel color to be WHITE, we will full cubes if ((cubesmap.pixels[z*cubesmap.width + x].r == 255) && (cubesmap.pixels[z*cubesmap.width + x].g == 255) && @@ -896,12 +912,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n3; nCounter += 6; - mapTexcoords[tcCounter] = vt2; - mapTexcoords[tcCounter + 1] = vt1; - mapTexcoords[tcCounter + 2] = vt3; - mapTexcoords[tcCounter + 3] = vt2; - mapTexcoords[tcCounter + 4] = vt3; - mapTexcoords[tcCounter + 5] = vt4; + mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; + mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; + mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y }; tcCounter += 6; // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) @@ -921,12 +937,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n4; nCounter += 6; - mapTexcoords[tcCounter] = vt4; - mapTexcoords[tcCounter + 1] = vt1; - mapTexcoords[tcCounter + 2] = vt3; - mapTexcoords[tcCounter + 3] = vt4; - mapTexcoords[tcCounter + 4] = vt2; - mapTexcoords[tcCounter + 5] = vt1; + mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height }; + mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y }; + mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; tcCounter += 6; if (((z < cubesmap.height - 1) && @@ -952,12 +968,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n6; nCounter += 6; - mapTexcoords[tcCounter] = vt2; - mapTexcoords[tcCounter + 1] = vt1; - mapTexcoords[tcCounter + 2] = vt4; - mapTexcoords[tcCounter + 3] = vt4; - mapTexcoords[tcCounter + 4] = vt1; - mapTexcoords[tcCounter + 5] = vt3; + mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y }; + mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height }; + mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height }; tcCounter += 6; } @@ -984,12 +1000,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n5; nCounter += 6; - mapTexcoords[tcCounter] = vt4; - mapTexcoords[tcCounter + 1] = vt1; - mapTexcoords[tcCounter + 2] = vt3; - mapTexcoords[tcCounter + 3] = vt4; - mapTexcoords[tcCounter + 4] = vt2; - mapTexcoords[tcCounter + 5] = vt1; + mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height }; + mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y }; + mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height }; tcCounter += 6; } @@ -1016,12 +1032,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n1; nCounter += 6; - mapTexcoords[tcCounter] = vt2; - mapTexcoords[tcCounter + 1] = vt1; - mapTexcoords[tcCounter + 2] = vt4; - mapTexcoords[tcCounter + 3] = vt4; - mapTexcoords[tcCounter + 4] = vt1; - mapTexcoords[tcCounter + 5] = vt3; + mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y }; + mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height }; + mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height }; tcCounter += 6; } @@ -1048,12 +1064,12 @@ Model LoadCubicmap(Image cubesmap) mapNormals[nCounter + 5] = n2; nCounter += 6; - mapTexcoords[tcCounter] = vt2; - mapTexcoords[tcCounter + 1] = vt3; - mapTexcoords[tcCounter + 2] = vt4; - mapTexcoords[tcCounter + 3] = vt2; - mapTexcoords[tcCounter + 4] = vt1; - mapTexcoords[tcCounter + 5] = vt3; + mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y }; + mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height }; + mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height }; tcCounter += 6; } } @@ -1062,11 +1078,55 @@ Model LoadCubicmap(Image cubesmap) (cubesmap.pixels[z*cubesmap.width + x].g == 0) && (cubesmap.pixels[z*cubesmap.width + x].b == 0)) { - // Define top triangles (2 tris, 6 vertex --> v1-v3-v2, v1-v4-v3) - // TODO: ... + // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) + mapVertices[vCounter] = v1; + mapVertices[vCounter + 1] = v3; + mapVertices[vCounter + 2] = v2; + mapVertices[vCounter + 3] = v1; + mapVertices[vCounter + 4] = v4; + mapVertices[vCounter + 5] = v3; + vCounter += 6; + + mapNormals[nCounter] = n4; + mapNormals[nCounter + 1] = n4; + mapNormals[nCounter + 2] = n4; + mapNormals[nCounter + 3] = n4; + mapNormals[nCounter + 4] = n4; + mapNormals[nCounter + 5] = n4; + nCounter += 6; + + mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height }; + mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y }; + mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; + tcCounter += 6; + + // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) + mapVertices[vCounter] = v6; + mapVertices[vCounter + 1] = v7; + mapVertices[vCounter + 2] = v8; + mapVertices[vCounter + 3] = v6; + mapVertices[vCounter + 4] = v8; + mapVertices[vCounter + 5] = v5; + vCounter += 6; - // Define bottom triangles (2 tris, 6 vertex --> v6-v7-v8, v6-v8-v5) - // TODO: ... + mapNormals[nCounter] = n3; + mapNormals[nCounter + 1] = n3; + mapNormals[nCounter + 2] = n3; + mapNormals[nCounter + 3] = n3; + mapNormals[nCounter + 4] = n3; + mapNormals[nCounter + 5] = n3; + nCounter += 6; + + mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; + mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height }; + mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; + mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; + mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; + mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y }; + tcCounter += 6; } } } @@ -1543,8 +1603,17 @@ static VertexData LoadOBJ(const char *fileName) bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) { + bool collision = false; + + float dx = centerA.x - centerB.x; // X distance between centers + float dy = centerA.y - centerB.y; // Y distance between centers + float dz = centerA.z - centerB.z; // Y distance between centers - return false; + float distance = sqrt(dx*dx + dy*dy + dz*dz); // Distance between centers + + if (distance <= (radiusA + radiusB)) collision = true; + + return collision; } bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2) @@ -1562,13 +1631,51 @@ bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, V bounds = new BoundingBox(minVertex, maxVertex); */ - return false; + + bool collision = true; + + if ((maxBBox1.x >= minBBox2.x) && (minBBox1.x <= maxBBox2.x)) + { + if ((maxBBox1.y < minBBox2.y) || (minBBox1.y > maxBBox2.y)) collision = false; + if ((maxBBox1.z < minBBox2.z) || (minBBox1.z > maxBBox2.z)) collision = false; + } + else collision = false; + + return collision; } -bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, Vector3 radiusSphere) +bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere) { + bool collision = false; + + if ((centerSphere.x - minBBox.x > radiusSphere) && (centerSphere.y - minBBox.y > radiusSphere) && (centerSphere.z - minBBox.z > radiusSphere) && + (maxBBox.x - centerSphere.x > radiusSphere) && (maxBBox.y - centerSphere.y > radiusSphere) && (maxBBox.z - centerSphere.z > radiusSphere)) + { + collision = true; + } + else + { + float dmin = 0; + + if (centerSphere.x - minBBox.x <= radiusSphere) + dmin += (centerSphere.x - minBBox.x) * (centerSphere.x - minBBox.x); + else if (maxBBox.x - centerSphere.x <= radiusSphere) + dmin += (centerSphere.x - maxBBox.x) * (centerSphere.x - maxBBox.x); + + if (centerSphere.y - minBBox.y <= radiusSphere) + dmin += (centerSphere.y - minBBox.y) * (centerSphere.y - minBBox.y); + else if (maxBBox.y - centerSphere.y <= radiusSphere) + dmin += (centerSphere.y - maxBBox.y) * (centerSphere.y - maxBBox.y); + + if (centerSphere.z - minBBox.z <= radiusSphere) + dmin += (centerSphere.z - minBBox.z) * (centerSphere.z - minBBox.z); + else if (maxBBox.z - centerSphere.z <= radiusSphere) + dmin += (centerSphere.z - maxBBox.z) * (centerSphere.z - maxBBox.z); + + if (dmin <= radiusSphere * radiusSphere) collision = true; + } - return false; + return collision; } //BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2) \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 3c849bdd..6ee78226 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -486,6 +486,10 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) +bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); +bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); +bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); + //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) //------------------------------------------------------------------------------------ -- cgit v1.2.3 From ab12193f10575d8375839eed35c9bc51ce6054b7 Mon Sep 17 00:00:00 2001 From: Constantine Tarasenkov Date: Tue, 3 Feb 2015 00:06:50 +0300 Subject: Fixes for C++ --- src/raylib.h | 2 ++ src/utils.c | 1 + 2 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/raylib.h b/src/raylib.h index 6ee78226..1be3f9a6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -178,8 +178,10 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- +#ifndef __cplusplus // Boolean type typedef enum { false, true } bool; +#endif // byte type typedef unsigned char byte; diff --git a/src/utils.c b/src/utils.c index 8e42e533..d3d57106 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,6 +27,7 @@ * **********************************************************************************************/ +#include "raylib.h" #include "utils.h" #if defined(PLATFORM_ANDROID) -- cgit v1.2.3 From cc6cf9a7411d01b9fe6dd59f455c454f8b392fb4 Mon Sep 17 00:00:00 2001 From: Constantine Tarasenkov Date: Tue, 3 Feb 2015 05:47:28 +0300 Subject: Adding functions to show and hide mouse cursor --- src/core.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/raylib.h | 3 +++ 2 files changed, 45 insertions(+) (limited to 'src') diff --git a/src/core.c b/src/core.c index efff6206..06523d50 100644 --- a/src/core.c +++ b/src/core.c @@ -52,6 +52,11 @@ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) #include // GLFW3 library: Windows, OpenGL context and Input management + #ifdef __linux + #define GLFW_EXPOSE_NATIVE_X11 // Linux specific definitions for getting + #define GLFW_EXPOSE_NATIVE_GLX // native functions like glfwGetX11Window + #include // which are required for hiding mouse + #endif //#include // OpenGL functions (GLFW3 already includes gl.h) //#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! #endif @@ -176,6 +181,7 @@ static bool cursorOnScreen = false; // Tracks if cursor is inside client static Texture2D cursor; // Cursor texture static Vector2 mousePosition; +static bool mouseHidden; static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once @@ -771,6 +777,42 @@ int GetMouseWheelMove(void) } #endif +void HideMouse() +{ +#if defined(PLATFORM_DESKTOP) + #ifdef __linux + XColor Col; + const char Nil[] = {0}; + + Pixmap Pix = XCreateBitmapFromData(glfwGetX11Display(), glfwGetX11Window(window), Nil, 1, 1); + Cursor Cur = XCreatePixmapCursor(glfwGetX11Display(), Pix, Pix, &Col, &Col, 0, 0); + + XDefineCursor(glfwGetX11Display(), glfwGetX11Window(window), Cur); + XFreeCursor(glfwGetX11Display(), Cur); + #else + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + #endif +#endif + mouseHidden = true; +} + +void ShowMouse() +{ +#if defined(PLATFORM_DESKTOP) + #ifdef __linux + XUndefineCursor(glfwGetX11Display(), glfwGetX11Window(window)); + #else + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + #endif +#endif + mouseHidden = false; +} + +bool IsMouseHidden() +{ + return mouseHidden; +} + // TODO: Enable gamepad usage on Rapsberry Pi // NOTE: emscripten not implemented #if defined(PLATFORM_DESKTOP) diff --git a/src/raylib.h b/src/raylib.h index 6ee78226..ec0551e6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -362,6 +362,9 @@ int GetMouseY(void); // Returns mouse positio Vector2 GetMousePosition(void); // Returns mouse position XY void SetMousePosition(Vector2 position); // Set mouse position XY int GetMouseWheelMove(void); // Returns mouse wheel movement Y +void ShowMouse(void); // Shows mouse cursor +void HideMouse(void); // Hides mouse cursor +bool IsMouseHidden(void); // Returns true if mouse cursor is not visible #endif #if defined(PLATFORM_DESKTOP) -- cgit v1.2.3 From 765870fe8fa246cf1cbca96ff0d399fb37273ba8 Mon Sep 17 00:00:00 2001 From: Constantine Tarasenkov Date: Tue, 3 Feb 2015 20:16:27 +0300 Subject: No need to include raylib.h --- src/utils.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/utils.c b/src/utils.c index d3d57106..8e42e533 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,7 +27,6 @@ * **********************************************************************************************/ -#include "raylib.h" #include "utils.h" #if defined(PLATFORM_ANDROID) -- cgit v1.2.3 From b25cdf7f7dcfef070ba4eef27995532ed7d86c04 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 4 Feb 2015 20:23:43 +0100 Subject: Added support for multiple camera modes --- src/core.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/raylib.h | 7 +- 2 files changed, 286 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index efff6206..869b7e95 100644 --- a/src/core.c +++ b/src/core.c @@ -94,6 +94,45 @@ //---------------------------------------------------------------------------------- #define MAX_TOUCH_POINTS 256 +// CAMERA_GENERIC +#define CAMERA_SCROLL_SENSITIVITY 1.5 + +// FREE_CAMERA +#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 +#define FREE_CAMERA_DISTANCE_CLAMP 0.3 +#define FREE_CAMERA_MIN_CLAMP 85 +#define FREE_CAMERA_MAX_CLAMP -85 +#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 +#define FREE_CAMERA_PANNING_DIVIDER 5.1 + +// ORBITAL_CAMERA +#define ORBITAL_CAMERA_SPEED 0.01 + +// FIRST_PERSON +#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003 +#define FIRST_PERSON_FOCUS_DISTANCE 25 +#define FIRST_PERSON_MIN_CLAMP 5 +#define FIRST_PERSON_MAX_CLAMP -85 + +#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0 +#define FIRST_PERSON_STEP_DIVIDER 30.0 +#define FIRST_PERSON_WAVING_DIVIDER 200.0 + +#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85 + +// THIRD_PERSON +#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003 +#define THIRD_PERSON_DISTANCE_CLAMP 1.2 +#define THIRD_PERSON_MIN_CLAMP 5 +#define THIRD_PERSON_MAX_CLAMP -85 +#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 } + +// PLAYER (used by camera) +#define PLAYER_WIDTH 0.4 +#define PLAYER_HEIGHT 0.9 +#define PLAYER_DEPTH 0.4 +#define PLAYER_MOVEMENT_DIVIDER 20.0 + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -206,9 +245,19 @@ static double targetTime = 0.0; // Desired time for one frame, if 0 static char configFlags = 0; static bool showLogo = false; -static bool customCamera = true; -//static int cameraMode = CUSTOM; // FREE, FIRST_PERSON, THIRD_PERSON - +// Camera variables +static int cameraMode = CAMERA_CUSTOM; +static Camera currentCamera; +static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}}; +static Vector2 cameraAngle = { 0, 0 }; +static float cameraTargetDistance = 5; +static Vector2 cameraMousePosition = { 0, 0 }; +static Vector2 cameraMouseVariation = { 0, 0 }; +static int cameraMovementCounter = 0; +static bool cameraUseGravity = true; +static Vector3 cameraPosition = { 2, 0, 2 }; // Player + +// Shaders variables static bool enabledPostpro = false; static unsigned int fboShader = 0; @@ -261,6 +310,8 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event); // static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands #endif +static void ProcessCamera(Camera *camera); + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- @@ -479,6 +530,9 @@ void BeginDrawing(void) updateTime = currentTime - previousTime; previousTime = currentTime; + // Calculate camera + if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera); + if (enabledPostpro) rlEnableFBO(); rlClearScreenBuffers(); @@ -543,15 +597,11 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (MODELVIEW) // Setup Camera view - if (customCamera) - { - Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up); - rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) - } - else - { - // TODO: Add support for multiple automatic camera modes - } + if (cameraMode == CAMERA_CUSTOM) currentCamera = camera; + else currentCamera = internalCamera; + + Matrix matLookAt = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) } // Ends 3D mode and returns to default 2D orthographic mode @@ -636,7 +686,7 @@ Color Fade(Color color, float alpha) // Enable some window configurations (SetWindowFlags()?) // TODO: Review function name and usage -void SetupFlags(char flags) +void SetConfigFlags(char flags) { configFlags = flags; @@ -650,6 +700,11 @@ void ShowLogo(void) showLogo = true; } +void SetCameraMode(int mode) +{ + cameraMode = mode; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- @@ -2019,13 +2074,13 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) if (widthRatio <= heightRatio) { renderWidth = displayWidth; - renderHeight = (int)((float)screenHeight*widthRatio); + renderHeight = (int)round((float)screenHeight*widthRatio); renderOffsetX = 0; renderOffsetY = (displayHeight - renderHeight); } else { - renderWidth = (int)((float)screenWidth*heightRatio); + renderWidth = (int)round((float)screenWidth*heightRatio); renderHeight = displayHeight; renderOffsetX = (displayWidth - renderWidth); renderOffsetY = 0; @@ -2055,13 +2110,13 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) if (displayRatio <= screenRatio) { renderWidth = screenWidth; - renderHeight = (int)((float)screenWidth/displayRatio); + renderHeight = (int)round((float)screenWidth/displayRatio); renderOffsetX = 0; renderOffsetY = (renderHeight - screenHeight); } else { - renderWidth = (int)((float)screenHeight*displayRatio); + renderWidth = (int)round((float)screenHeight*displayRatio); renderHeight = screenHeight; renderOffsetX = (renderWidth - screenWidth); renderOffsetY = 0; @@ -2202,3 +2257,211 @@ static void LogoAnimation(void) showLogo = false; // Prevent for repeating when reloading window (Android) } +// Process desired camera mode and controls +static void ProcessCamera(Camera *camera) +{ + // Mouse movement detection + if (fullscreen) + { + if (GetMousePosition().x < 100) SetMousePosition((Vector2){ screenWidth - 100, GetMousePosition().y}); + else if (GetMousePosition().y < 100) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - 100}); + else if (GetMousePosition().x > screenWidth - 100) SetMousePosition((Vector2) { 100, GetMousePosition().y}); + else if (GetMousePosition().y > screenHeight - 100) SetMousePosition((Vector2){ GetMousePosition().x, 100}); + else + { + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + } + else + { + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + + cameraMousePosition = GetMousePosition(); + + // Support for multiple automatic camera modes + switch (cameraMode) + { + case CAMERA_FREE: + { + // Pass to orbiting camera + if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_CLAMP; + + if (IsKeyDown(KEY_LEFT_ALT)) + { + if (IsKeyDown(KEY_LEFT_CONTROL)) + { + // Camera smooth zoom + if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY); + } + // Camera orientation calculation + else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD; + } + } + // Paning + else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) + { + camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + } + + // Focus to center + if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_ORBITAL: + { + // Pass to free camera + if (IsKeyPressed('O')) cameraMode = CAMERA_FREE; + + cameraAngle.x += ORBITAL_CAMERA_SPEED; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Focus to center + if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_FIRST_PERSON: + case CAMERA_THIRD_PERSON: + { + bool isMoving = false; + + // Keyboard inputs + if (IsKeyDown('W')) + { + cameraPosition.x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown('S')) + { + cameraPosition.x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown('A')) + { + cameraPosition.x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown('D')) + { + cameraPosition.x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + cameraPosition.z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown('E')) + { + if (!cameraUseGravity) cameraPosition.y += 1 / PLAYER_MOVEMENT_DIVIDER; + } + else if (IsKeyDown('Q')) + { + if (!cameraUseGravity) cameraPosition.y -= 1 / PLAYER_MOVEMENT_DIVIDER; + } + + if (cameraMode == CAMERA_THIRD_PERSON) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -THIRD_PERSON_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -THIRD_PERSON_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Camera is always looking at player + camera->target.x = cameraPosition.x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); + camera->target.y = cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; + camera->target.z = cameraPosition.z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + } + else + { + if (isMoving) cameraMovementCounter++; + + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -FIRST_PERSON_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -FIRST_PERSON_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera is always looking at player + camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + + camera->position.x = cameraPosition.x; + camera->position.y = (cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; + camera->position.z = cameraPosition.z; + + camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + } + } break; + default: break; + } +} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 6ee78226..63a2b21f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -252,6 +252,9 @@ typedef struct Camera { Vector3 up; } Camera; +// Camera modes +typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; + // Vertex data definning a mesh typedef struct VertexData { int vertexCount; @@ -337,7 +340,8 @@ int GetHexValue(Color color); // Returns hexadecim int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -void SetupFlags(char flags); // Enable some window configurations +void SetCameraMode(int mode); // Multiple camera modes available +void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) void InitPostShader(void); @@ -489,6 +493,7 @@ unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); +//void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition); //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) -- cgit v1.2.3 From 77eda6494e0959adf8b0f70883a3304d623c9d62 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 4 Feb 2015 20:24:23 +0100 Subject: Added cubicmap collision detection and resolution --- src/models.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 156 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index a53cf5c8..96f98c91 100644 --- a/src/models.c +++ b/src/models.c @@ -867,9 +867,9 @@ Model LoadCubicmap(Image cubesmap) } RectangleF; RectangleF rightTexUV = { 0, 0, 0.5, 0.5 }; - RectangleF leftTexUV = { 0.5, 0, 0.25, 0.25 }; - RectangleF frontTexUV = { 0.75, 0, 0.25, 0.25 }; - RectangleF backTexUV = { 0.5, 0.25, 0.25, 0.25 }; + RectangleF leftTexUV = { 0.5, 0, 0.5, 0.5 }; + RectangleF frontTexUV = { 0, 0, 0.5, 0.5 }; + RectangleF backTexUV = { 0.5, 0, 0.5, 0.5 }; RectangleF topTexUV = { 0, 0.5, 0.5, 0.5 }; RectangleF bottomTexUV = { 0.5, 0.5, 0.5, 0.5 }; @@ -1678,4 +1678,156 @@ bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSph return collision; } -//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2) \ No newline at end of file +// TODO +//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2) + +// Detect and resolve cubicmap collisions +// NOTE: player position (or camera) is modified inside this function +void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition) +{ + // Detect the cell where the player is located + int locationCellX = 0; + int locationCellY = 0; + + locationCellX = floor(playerPosition->x + mapPosition.x + 0.5); + locationCellY = floor(playerPosition->z + mapPosition.z + 0.5); + + // Multiple Axis -------------------------------------------------------------------------------------------- + + // Axis x-, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + playerPosition->x = locationCellX - 0.2; + playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x-, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + playerPosition->x = locationCellX - 0.2; + playerPosition->z = locationCellY + 0.2; + } + } + + // Axis x+, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + playerPosition->x = locationCellX + 0.2; + playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x+, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + playerPosition->x = locationCellX + 0.2f; + playerPosition->z = locationCellY + 0.2f; + } + } + + // Single Axis --------------------------------------------------------------------------------------------------- + + // Axis x- + if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) + { + if ((playerPosition->x + 0.5f) - locationCellX < 0.3) + { + playerPosition->x = locationCellX - 0.2; + } + } + // Axis x+ + if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) + { + if ((playerPosition->x + 0.5f) - locationCellX > 0.7) + { + playerPosition->x = locationCellX + 0.2; + } + } + // Axis y- + if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z + 0.5f) - locationCellY < 0.3) + { + playerPosition->z = locationCellY - 0.2; + } + } + // Axis y+ + if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z + 0.5f) - locationCellY > 0.7) + { + playerPosition->z = locationCellY + 0.2; + } + } + + // Diagonals ------------------------------------------------------------------------------------------------------- + + // Axis x-, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + if (((playerPosition->x + 0.5f) - locationCellX) > ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX - 0.2; + else playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x-, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + if (((playerPosition->x + 0.5f) - locationCellX) > (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX - 0.2; + else playerPosition->z = locationCellY + 0.2; + } + } + + // Axis x+, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + if (((playerPosition->x + 0.5f) - locationCellX) < (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX + 0.2; + else playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x+, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + if (((playerPosition->x + 0.5f) - locationCellX) < ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX + 0.2; + else playerPosition->z = locationCellY + 0.2; + } + } +} \ No newline at end of file -- cgit v1.2.3 From e1353b9f7d5f29b4706b305245946b2c5cab6a12 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 9 Feb 2015 18:29:32 +0100 Subject: Removed some TODO already done --- src/core.c | 3 +-- src/models.c | 10 +--------- src/rlgl.c | 27 ++++++++++++++++++--------- src/textures.c | 2 -- 4 files changed, 20 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index f0a5af91..a1dc9633 100644 --- a/src/core.c +++ b/src/core.c @@ -690,8 +690,7 @@ Color Fade(Color color, float alpha) return (Color){color.r, color.g, color.b, color.a*alpha}; } -// Enable some window configurations (SetWindowFlags()?) -// TODO: Review function name and usage +// Enable some window/system configurations void SetConfigFlags(char flags) { configFlags = flags; diff --git a/src/models.c b/src/models.c index 96f98c91..5d76fd65 100644 --- a/src/models.c +++ b/src/models.c @@ -850,15 +850,7 @@ Model LoadCubicmap(Image cubesmap) Vector3 n5 = { 0.0f, 0.0f, 1.0f }; Vector3 n6 = { 0.0f, 0.0f, -1.0f }; - // Define the 4 texture coordinates of the cube, we will combine them accordingly later... - // TODO: Use texture rectangles to define different textures for top-bottom-front-back-right-left (6) - /* - Vector2 vt2 = { 0.0f, 0.0f }; - Vector2 vt1 = { 0.0f, 1.0f }; - Vector2 vt4 = { 1.0f, 0.0f }; - Vector2 vt3 = { 1.0f, 1.0f }; - */ - + // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6) typedef struct RectangleF { float x; float y; diff --git a/src/rlgl.c b/src/rlgl.c index 67ef0a48..a30cfbb7 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -353,7 +353,7 @@ void rlScalef(float x, float y, float z) // Multiply the current matrix by another matrix void rlMultMatrixf(float *m) { - // TODO: review Matrix creation from array + // Matrix creation from array Matrix mat = { m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], @@ -511,7 +511,7 @@ void rlEnd(void) } } - // TODO: Make sure normals count match vertex count + // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P } break; default: break; @@ -1417,6 +1417,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge glBindTexture(GL_TEXTURE_2D, id); // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used! + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repead on x-axis glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repead on y-axis @@ -1433,6 +1434,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge } // If mipmaps are being used, we configure mag-min filters accordingly + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so only GL_LINEAR or GL_NEAREST can be used if (genMipmaps) { // Trilinear filtering with mipmaps @@ -1476,16 +1478,23 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); #endif -#if defined(GRAPHICS_API_OPENGL_33) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) + // NOTE: On embedded systems, we let the driver choose the best internal format //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // WebGL -#elif defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: On embedded systems, we let the driver choose the best internal format - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); -#endif + + // TODO: Add support for multiple color modes (16bit color modes and grayscale) + // Ref: https://www.khronos.org/opengles/sdk/docs/man3/html/glTexImage2D.xhtml + // On WebGL, internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + // (sized)internalFormat format type + // GL_R GL_RED GL_UNSIGNED_BYTE + // GL_RGB565 GL_RGB GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5 + // GL_RGB5_A1 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_5_5_1 + // GL_RGBA4 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4 + // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE + // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (genMipmaps) { glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically @@ -2142,7 +2151,7 @@ static void InitializeBuffersGPU(void) } // Update VBOs with vertex array data -// TODO: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) +// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) // TODO: If no data changed on the CPU arrays --> No need to update GPU arrays every frame! static void UpdateBuffers(void) { diff --git a/src/textures.c b/src/textures.c index e8bf6088..768ead78 100644 --- a/src/textures.c +++ b/src/textures.c @@ -103,8 +103,6 @@ Image LoadImage(const char *fileName) // Force loading to 4 components (RGBA) byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); - // TODO: Check if file could be loaded! (imgData == NULL)? - if (imgData != NULL) { // Convert array to pixel array for working convenience -- cgit v1.2.3 From 3cb4edcbc3921b3fe7e3be5b75b49df8b14112a2 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Mon, 9 Feb 2015 18:35:25 +0100 Subject: Corrected camera system --- src/core.c | 133 ++++++++++------- src/models.c | 467 ++++++++++++++++++++++++++++++----------------------------- src/raylib.h | 4 +- 3 files changed, 322 insertions(+), 282 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index a1dc9633..8d8795cd 100644 --- a/src/core.c +++ b/src/core.c @@ -261,7 +261,6 @@ static Vector2 cameraMousePosition = { 0, 0 }; static Vector2 cameraMouseVariation = { 0, 0 }; static int cameraMovementCounter = 0; static bool cameraUseGravity = true; -static Vector3 cameraPosition = { 2, 0, 2 }; // Player // Shaders variables static bool enabledPostpro = false; @@ -316,7 +315,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event); // static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands #endif -static void ProcessCamera(Camera *camera); +static void ProcessCamera(Camera *camera, Vector3 *playerPosition); //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions @@ -535,10 +534,7 @@ void BeginDrawing(void) currentTime = GetTime(); // Number of elapsed seconds since InitTimer() was called updateTime = currentTime - previousTime; previousTime = currentTime; - - // Calculate camera - if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera); - + if (enabledPostpro) rlEnableFBO(); rlClearScreenBuffers(); @@ -707,9 +703,46 @@ void ShowLogo(void) void SetCameraMode(int mode) { + if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + cameraAngle.y = -40 * DEG2RAD; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + cameraAngle.y = -40 * DEG2RAD; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + internalCamera.position = (Vector3){ -1, 1, -1 }; + internalCamera.target = (Vector3){ 3, 0, 3}; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + cameraAngle.x = 45 * DEG2RAD; + cameraAngle.y = -20 * DEG2RAD; + ProcessCamera(&internalCamera, &internalCamera.position); + } + cameraMode = mode; } +void UpdateCamera(Vector3 *playerPosition) +{ + // Calculate camera + if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, playerPosition); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- @@ -1026,6 +1059,32 @@ Vector2 GetTouchPosition(void) }*/ #endif +// Initialize OpenGL graphics +void InitGraphics(void) +{ + rlglInit(); // Init rlgl + + rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff) + + ClearBackground(RAYWHITE); // Default background color for raylib games :P + +#if defined(PLATFORM_ANDROID) + windowReady = true; // IMPORTANT! +#endif +} + +void InitPostShader(void) +{ + rlglInitPostpro(); + + enabledPostpro = true; +} + +void SetPostShader(unsigned int shader) +{ + fboShader = shader; +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -1287,32 +1346,6 @@ static void InitDisplay(int width, int height) #endif } -// Initialize OpenGL graphics -void InitGraphics(void) -{ - rlglInit(); // Init rlgl - - rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff) - - ClearBackground(RAYWHITE); // Default background color for raylib games :P - -#if defined(PLATFORM_ANDROID) - windowReady = true; // IMPORTANT! -#endif -} - -void InitPostShader(void) -{ - rlglInitPostpro(); - - enabledPostpro = true; -} - -void SetPostShader(unsigned int shader) -{ - fboShader = shader; -} - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) @@ -2299,8 +2332,8 @@ static void LogoAnimation(void) } // Process desired camera mode and controls -static void ProcessCamera(Camera *camera) -{ +static void ProcessCamera(Camera *camera, Vector3 *playerPosition) +{ // Mouse movement detection if (fullscreen) { @@ -2408,16 +2441,16 @@ static void ProcessCamera(Camera *camera) // Keyboard inputs if (IsKeyDown('W')) { - cameraPosition.x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - cameraPosition.z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; isMoving = true; } else if (IsKeyDown('S')) { - cameraPosition.x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - cameraPosition.z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; isMoving = true; @@ -2425,26 +2458,26 @@ static void ProcessCamera(Camera *camera) if (IsKeyDown('A')) { - cameraPosition.x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - cameraPosition.z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; isMoving = true; } else if (IsKeyDown('D')) { - cameraPosition.x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - cameraPosition.z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; isMoving = true; } if (IsKeyDown('E')) { - if (!cameraUseGravity) cameraPosition.y += 1 / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER; } else if (IsKeyDown('Q')) { - if (!cameraUseGravity) cameraPosition.y -= 1 / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER; } if (cameraMode == CAMERA_THIRD_PERSON) @@ -2465,9 +2498,9 @@ static void ProcessCamera(Camera *camera) if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; // Camera is always looking at player - camera->target.x = cameraPosition.x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); - camera->target.y = cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; - camera->target.z = cameraPosition.z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); + camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); + camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; + camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); // Camera position update camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; @@ -2495,9 +2528,9 @@ static void ProcessCamera(Camera *camera) camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; - camera->position.x = cameraPosition.x; - camera->position.y = (cameraPosition.y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; - camera->position.z = cameraPosition.z; + camera->position.x = playerPosition->x; + camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; + camera->position.z = playerPosition->z; camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; diff --git a/src/models.c b/src/models.c index 5d76fd65..3749f8e5 100644 --- a/src/models.c +++ b/src/models.c @@ -1335,6 +1335,242 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec rlDisableTexture(); } + +bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) +{ + bool collision = false; + + float dx = centerA.x - centerB.x; // X distance between centers + float dy = centerA.y - centerB.y; // Y distance between centers + float dz = centerA.z - centerB.z; // Y distance between centers + + float distance = sqrt(dx*dx + dy*dy + dz*dz); // Distance between centers + + if (distance <= (radiusA + radiusB)) collision = true; + + return collision; +} + +bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2) +{ + /* + // Get min and max vertex to construct bounds (AABB) + Vector3 minVertex = tempVertices[0]; + Vector3 maxVertex = tempVertices[0]; + + for (int i = 1; i < tempVertices.Count; i++) + { + minVertex = Vector3.Min(minVertex, tempVertices[i]); + maxVertex = Vector3.Max(maxVertex, tempVertices[i]); + } + + bounds = new BoundingBox(minVertex, maxVertex); + */ + + bool collision = true; + + if ((maxBBox1.x >= minBBox2.x) && (minBBox1.x <= maxBBox2.x)) + { + if ((maxBBox1.y < minBBox2.y) || (minBBox1.y > maxBBox2.y)) collision = false; + if ((maxBBox1.z < minBBox2.z) || (minBBox1.z > maxBBox2.z)) collision = false; + } + else collision = false; + + return collision; +} + +bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere) +{ + bool collision = false; + + if ((centerSphere.x - minBBox.x > radiusSphere) && (centerSphere.y - minBBox.y > radiusSphere) && (centerSphere.z - minBBox.z > radiusSphere) && + (maxBBox.x - centerSphere.x > radiusSphere) && (maxBBox.y - centerSphere.y > radiusSphere) && (maxBBox.z - centerSphere.z > radiusSphere)) + { + collision = true; + } + else + { + float dmin = 0; + + if (centerSphere.x - minBBox.x <= radiusSphere) + dmin += (centerSphere.x - minBBox.x) * (centerSphere.x - minBBox.x); + else if (maxBBox.x - centerSphere.x <= radiusSphere) + dmin += (centerSphere.x - maxBBox.x) * (centerSphere.x - maxBBox.x); + + if (centerSphere.y - minBBox.y <= radiusSphere) + dmin += (centerSphere.y - minBBox.y) * (centerSphere.y - minBBox.y); + else if (maxBBox.y - centerSphere.y <= radiusSphere) + dmin += (centerSphere.y - maxBBox.y) * (centerSphere.y - maxBBox.y); + + if (centerSphere.z - minBBox.z <= radiusSphere) + dmin += (centerSphere.z - minBBox.z) * (centerSphere.z - minBBox.z); + else if (maxBBox.z - centerSphere.z <= radiusSphere) + dmin += (centerSphere.z - maxBBox.z) * (centerSphere.z - maxBBox.z); + + if (dmin <= radiusSphere * radiusSphere) collision = true; + } + + return collision; +} + +// TODO +//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2) + +// Detect and resolve cubicmap collisions +// NOTE: player position (or camera) is modified inside this function +void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition) +{ + // Detect the cell where the player is located + int locationCellX = 0; + int locationCellY = 0; + + locationCellX = floor(playerPosition->x + mapPosition.x + 0.5); + locationCellY = floor(playerPosition->z + mapPosition.z + 0.5); + + // Multiple Axis -------------------------------------------------------------------------------------------- + + // Axis x-, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + playerPosition->x = locationCellX - 0.2; + playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x-, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + playerPosition->x = locationCellX - 0.2; + playerPosition->z = locationCellY + 0.2; + } + } + + // Axis x+, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + playerPosition->x = locationCellX + 0.2; + playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x+, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + playerPosition->x = locationCellX + 0.2f; + playerPosition->z = locationCellY + 0.2f; + } + } + + // Single Axis --------------------------------------------------------------------------------------------------- + + // Axis x- + if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) + { + if ((playerPosition->x + 0.5f) - locationCellX < 0.3) + { + playerPosition->x = locationCellX - 0.2; + } + } + // Axis x+ + if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) + { + if ((playerPosition->x + 0.5f) - locationCellX > 0.7) + { + playerPosition->x = locationCellX + 0.2; + } + } + // Axis y- + if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z + 0.5f) - locationCellY < 0.3) + { + playerPosition->z = locationCellY - 0.2; + } + } + // Axis y+ + if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z + 0.5f) - locationCellY > 0.7) + { + playerPosition->z = locationCellY + 0.2; + } + } + + // Diagonals ------------------------------------------------------------------------------------------------------- + + // Axis x-, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + if (((playerPosition->x + 0.5f) - locationCellX) > ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX - 0.2; + else playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x-, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + if (((playerPosition->x + 0.5f) - locationCellX) > (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX - 0.2; + else playerPosition->z = locationCellY + 0.2; + } + } + + // Axis x+, y- + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + { + if (((playerPosition->x + 0.5f) - locationCellX) < (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX + 0.2; + else playerPosition->z = locationCellY - 0.2; + } + } + + // Axis x+, y+ + if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + { + if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && + ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + { + if (((playerPosition->x + 0.5f) - locationCellX) < ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX + 0.2; + else playerPosition->z = locationCellY + 0.2; + } + } +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + // Get current vertex y altitude (proportional to pixel colors in grayscale) static float GetHeightValue(Color pixel) { @@ -1592,234 +1828,3 @@ static VertexData LoadOBJ(const char *fileName) return vData; } - -bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) -{ - bool collision = false; - - float dx = centerA.x - centerB.x; // X distance between centers - float dy = centerA.y - centerB.y; // Y distance between centers - float dz = centerA.z - centerB.z; // Y distance between centers - - float distance = sqrt(dx*dx + dy*dy + dz*dz); // Distance between centers - - if (distance <= (radiusA + radiusB)) collision = true; - - return collision; -} - -bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2) -{ - /* - // Get min and max vertex to construct bounds (AABB) - Vector3 minVertex = tempVertices[0]; - Vector3 maxVertex = tempVertices[0]; - - for (int i = 1; i < tempVertices.Count; i++) - { - minVertex = Vector3.Min(minVertex, tempVertices[i]); - maxVertex = Vector3.Max(maxVertex, tempVertices[i]); - } - - bounds = new BoundingBox(minVertex, maxVertex); - */ - - bool collision = true; - - if ((maxBBox1.x >= minBBox2.x) && (minBBox1.x <= maxBBox2.x)) - { - if ((maxBBox1.y < minBBox2.y) || (minBBox1.y > maxBBox2.y)) collision = false; - if ((maxBBox1.z < minBBox2.z) || (minBBox1.z > maxBBox2.z)) collision = false; - } - else collision = false; - - return collision; -} - -bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere) -{ - bool collision = false; - - if ((centerSphere.x - minBBox.x > radiusSphere) && (centerSphere.y - minBBox.y > radiusSphere) && (centerSphere.z - minBBox.z > radiusSphere) && - (maxBBox.x - centerSphere.x > radiusSphere) && (maxBBox.y - centerSphere.y > radiusSphere) && (maxBBox.z - centerSphere.z > radiusSphere)) - { - collision = true; - } - else - { - float dmin = 0; - - if (centerSphere.x - minBBox.x <= radiusSphere) - dmin += (centerSphere.x - minBBox.x) * (centerSphere.x - minBBox.x); - else if (maxBBox.x - centerSphere.x <= radiusSphere) - dmin += (centerSphere.x - maxBBox.x) * (centerSphere.x - maxBBox.x); - - if (centerSphere.y - minBBox.y <= radiusSphere) - dmin += (centerSphere.y - minBBox.y) * (centerSphere.y - minBBox.y); - else if (maxBBox.y - centerSphere.y <= radiusSphere) - dmin += (centerSphere.y - maxBBox.y) * (centerSphere.y - maxBBox.y); - - if (centerSphere.z - minBBox.z <= radiusSphere) - dmin += (centerSphere.z - minBBox.z) * (centerSphere.z - minBBox.z); - else if (maxBBox.z - centerSphere.z <= radiusSphere) - dmin += (centerSphere.z - maxBBox.z) * (centerSphere.z - maxBBox.z); - - if (dmin <= radiusSphere * radiusSphere) collision = true; - } - - return collision; -} - -// TODO -//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2) - -// Detect and resolve cubicmap collisions -// NOTE: player position (or camera) is modified inside this function -void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition) -{ - // Detect the cell where the player is located - int locationCellX = 0; - int locationCellY = 0; - - locationCellX = floor(playerPosition->x + mapPosition.x + 0.5); - locationCellY = floor(playerPosition->z + mapPosition.z + 0.5); - - // Multiple Axis -------------------------------------------------------------------------------------------- - - // Axis x-, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) - { - playerPosition->x = locationCellX - 0.2; - playerPosition->z = locationCellY - 0.2; - } - } - - // Axis x-, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) - { - playerPosition->x = locationCellX - 0.2; - playerPosition->z = locationCellY + 0.2; - } - } - - // Axis x+, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) - { - playerPosition->x = locationCellX + 0.2; - playerPosition->z = locationCellY - 0.2; - } - } - - // Axis x+, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) - { - playerPosition->x = locationCellX + 0.2f; - playerPosition->z = locationCellY + 0.2f; - } - } - - // Single Axis --------------------------------------------------------------------------------------------------- - - // Axis x- - if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) - { - if ((playerPosition->x + 0.5f) - locationCellX < 0.3) - { - playerPosition->x = locationCellX - 0.2; - } - } - // Axis x+ - if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) - { - if ((playerPosition->x + 0.5f) - locationCellX > 0.7) - { - playerPosition->x = locationCellX + 0.2; - } - } - // Axis y- - if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) - { - if ((playerPosition->z + 0.5f) - locationCellY < 0.3) - { - playerPosition->z = locationCellY - 0.2; - } - } - // Axis y+ - if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) - { - if ((playerPosition->z + 0.5f) - locationCellY > 0.7) - { - playerPosition->z = locationCellY + 0.2; - } - } - - // Diagonals ------------------------------------------------------------------------------------------------------- - - // Axis x-, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) - { - if (((playerPosition->x + 0.5f) - locationCellX) > ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX - 0.2; - else playerPosition->z = locationCellY - 0.2; - } - } - - // Axis x-, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) - { - if (((playerPosition->x + 0.5f) - locationCellX) > (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX - 0.2; - else playerPosition->z = locationCellY + 0.2; - } - } - - // Axis x+, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) - { - if (((playerPosition->x + 0.5f) - locationCellX) < (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX + 0.2; - else playerPosition->z = locationCellY - 0.2; - } - } - - // Axis x+, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) - { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) - { - if (((playerPosition->x + 0.5f) - locationCellX) < ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX + 0.2; - else playerPosition->z = locationCellY + 0.2; - } - } -} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 79052c26..4f199b90 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -343,6 +343,8 @@ int GetRandomValue(int min, int max); // Returns a random Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void SetCameraMode(int mode); // Multiple camera modes available +void UpdateCamera(Vector3 *playerPosition); + void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) @@ -498,7 +500,7 @@ unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); -//void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition); +void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition); //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) -- cgit v1.2.3 From ee4b553c2a7bb0784b53583988c60e023f340220 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 26 Feb 2015 13:52:03 +0100 Subject: Renamed some functions --- src/core.c | 43 ++++++++++++++++++++++--------------------- src/models.c | 2 +- src/raylib.h | 7 ++++--- 3 files changed, 27 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 8d8795cd..bfd24a85 100644 --- a/src/core.c +++ b/src/core.c @@ -99,6 +99,8 @@ //---------------------------------------------------------------------------------- #define MAX_TOUCH_POINTS 256 +// Camera System configuration +//---------------------------------------------------------------------------------- // CAMERA_GENERIC #define CAMERA_SCROLL_SENSITIVITY 1.5 @@ -220,7 +222,7 @@ static bool cursorOnScreen = false; // Tracks if cursor is inside client static Texture2D cursor; // Cursor texture static Vector2 mousePosition; -static bool mouseHidden; +static bool cursorHidden; static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once @@ -864,7 +866,7 @@ int GetMouseWheelMove(void) } #endif -void HideMouse() +void HideCursor() { #if defined(PLATFORM_DESKTOP) #ifdef __linux @@ -880,10 +882,10 @@ void HideMouse() glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); #endif #endif - mouseHidden = true; + cursorHidden = true; } -void ShowMouse() +void ShowCursor() { #if defined(PLATFORM_DESKTOP) #ifdef __linux @@ -892,12 +894,12 @@ void ShowMouse() glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); #endif #endif - mouseHidden = false; + cursorHidden = false; } -bool IsMouseHidden() +bool IsCursorHidden() { - return mouseHidden; + return cursorHidden; } // TODO: Enable gamepad usage on Rapsberry Pi @@ -1059,20 +1061,6 @@ Vector2 GetTouchPosition(void) }*/ #endif -// Initialize OpenGL graphics -void InitGraphics(void) -{ - rlglInit(); // Init rlgl - - rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff) - - ClearBackground(RAYWHITE); // Default background color for raylib games :P - -#if defined(PLATFORM_ANDROID) - windowReady = true; // IMPORTANT! -#endif -} - void InitPostShader(void) { rlglInitPostpro(); @@ -1346,6 +1334,19 @@ static void InitDisplay(int width, int height) #endif } +// Initialize OpenGL graphics +static void InitGraphics(void) +{ + rlglInit(); // Init rlgl + + rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff) + + ClearBackground(RAYWHITE); // Default background color for raylib games :P + +#if defined(PLATFORM_ANDROID) + windowReady = true; // IMPORTANT! +#endif +} #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) diff --git a/src/models.c b/src/models.c index 3749f8e5..3a4e38f1 100644 --- a/src/models.c +++ b/src/models.c @@ -836,7 +836,7 @@ Model LoadCubicmap(Image cubesmap) float w = mapCubeSide; float h = mapCubeSide; - float h2 = mapCubeSide * 1.5; + float h2 = mapCubeSide * 1.5; // TODO: Review walls height... Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles * 3 * sizeof(Vector2)); diff --git a/src/raylib.h b/src/raylib.h index 4f199b90..c480802c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -370,9 +370,10 @@ int GetMouseY(void); // Returns mouse positio Vector2 GetMousePosition(void); // Returns mouse position XY void SetMousePosition(Vector2 position); // Set mouse position XY int GetMouseWheelMove(void); // Returns mouse wheel movement Y -void ShowMouse(void); // Shows mouse cursor -void HideMouse(void); // Hides mouse cursor -bool IsMouseHidden(void); // Returns true if mouse cursor is not visible + +void ShowCursor(void); // Shows cursor +void HideCursor(void); // Hides cursor +bool IsCursorHidden(void); // Returns true if cursor is not visible #endif #if defined(PLATFORM_DESKTOP) -- cgit v1.2.3 From c062f8d4fe3b600ea63a7a7f783963cf3d6aca84 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 1 Mar 2015 16:00:52 +0100 Subject: Redesign shader system and more Shader system has been completely redesigned Added support for multiple texture color modes --- src/core.c | 1 + src/models.c | 8 +- src/raylib.h | 36 ++++-- src/rlgl.c | 352 +++++++++++++++++++++++++++++---------------------------- src/rlgl.h | 42 ++++++- src/textures.c | 6 +- 6 files changed, 248 insertions(+), 197 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index bfd24a85..c61381fd 100644 --- a/src/core.c +++ b/src/core.c @@ -1347,6 +1347,7 @@ static void InitGraphics(void) windowReady = true; // IMPORTANT! #endif } + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) diff --git a/src/models.c b/src/models.c index 3a4e38f1..a8bfe37f 100644 --- a/src/models.c +++ b/src/models.c @@ -1202,14 +1202,14 @@ void UnloadModel(Model model) rlDeleteBuffers(model.vboId[2]); rlDeleteVertexArrays(model.vaoId); - rlDeleteTextures(model.textureId); - rlDeleteShader(model.shaderId); + rlDeleteTextures(model.texture.id); + rlDeleteShader(model.shader.id); } void SetModelTexture(Model *model, Texture2D texture) { - if (texture.id <= 0) model->textureId = whiteTexture; // Default white texture (use mesh color) - else model->textureId = texture.id; + if (texture.id <= 0) model->texture.id = whiteTexture; // Default white texture (use mesh color) + else model->texture = texture; } // Draw a model (with texture if set) diff --git a/src/raylib.h b/src/raylib.h index c480802c..3b6f9320 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -266,22 +266,34 @@ typedef struct VertexData { unsigned char *colors; // 4 components per vertex } VertexData; +// Shader type +typedef struct Shader { + unsigned int id; // Shader program id + + // Variable attributes + unsigned int vertexLoc; // Vertex attribute location point (vertex shader) + unsigned int texcoordLoc; // Texcoord attribute location point (vertex shader) + unsigned int normalLoc; // Normal attribute location point (vertex shader) + unsigned int colorLoc; // Color attibute location point (vertex shader) + + // Uniforms + unsigned int projectionLoc; // Projection matrix uniform location point (vertex shader) + unsigned int modelviewLoc; // ModeView matrix uniform location point (vertex shader) + unsigned int textureLoc; // Texture uniform location point (fragment shader) + unsigned int tintColorLoc; // Color uniform location point (fragment shader) +} Shader; + // 3d Model type // NOTE: If using OpenGL 1.1, loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId) typedef struct Model { VertexData mesh; unsigned int vaoId; unsigned int vboId[4]; - unsigned int textureId; - unsigned int shaderId; + Texture2D texture; + Shader shader; //Matrix transform; } Model; -// Shader type -typedef struct Shader { - unsigned int id; -} Shader; - // Sound source type typedef struct Sound { unsigned int source; @@ -343,13 +355,13 @@ int GetRandomValue(int min, int max); // Returns a random Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void SetCameraMode(int mode); // Multiple camera modes available -void UpdateCamera(Vector3 *playerPosition); +void UpdateCamera(Vector3 *playerPosition); // Update camera with player position (when using internal camera) void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) -void InitPostShader(void); -void SetPostShader(unsigned int shader); +void InitPostShader(void); // Initialize fullscreen postproduction shaders system +void SetPostShader(unsigned int shader); // Set fullscreen postproduction shader //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) @@ -487,7 +499,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight); Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) void UnloadModel(Model model); // Unload 3d model from memory void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model -void SetModelShader(Model *model, unsigned int shader); +void SetModelShader(Model *model, Shader shader); // Link a shader to a model (not available on OpenGL 1.1) void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint); // Draw a model with extended parameters @@ -496,7 +508,7 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color color); void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec -unsigned int LoadCustomShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) +Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); diff --git a/src/rlgl.c b/src/rlgl.c index a30cfbb7..1664c5c2 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -151,24 +151,8 @@ static VertexPositionColorBuffer lines; // No texture support static VertexPositionColorBuffer triangles; // No texture support static VertexPositionColorTextureIndexBuffer quads; -// Vetex-Fragment Shader Program ID -static GLuint defaultShaderProgram, simpleShaderProgram; - -// Default Shader program attibutes binding locations -static GLuint defaultVertexLoc, defaultTexcoordLoc, defaultColorLoc; -static GLuint defaultProjectionMatrixLoc, defaultModelviewMatrixLoc; -static GLuint defaultTextureLoc; - -// Simple Shader program attibutes binding locations -static GLuint simpleVertexLoc, simpleTexcoordLoc, simpleNormalLoc, simpleColorLoc; -static GLuint simpleProjectionMatrixLoc, simpleModelviewMatrixLoc; -static GLuint simpleTextureLoc; - -// Custom Shader program attibutes binding locations -static GLuint customVertexLoc, customTexcoordLoc, customNormalLoc, customColorLoc; -static GLuint customProjectionMatrixLoc, customModelviewMatrixLoc; -static GLuint customTextureLoc; -static bool customShader = false; +// Shader Programs +static Shader defaultShader, simpleShader; // Vertex Array Objects (VAO) static GLuint vaoLines, vaoTriangles, vaoQuads; @@ -210,8 +194,8 @@ unsigned int whiteTexture; // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static GLuint LoadDefaultShader(void); -static GLuint LoadSimpleShader(void); +static Shader LoadDefaultShader(void); +static Shader LoadSimpleShader(void); static void InitializeBuffers(void); static void InitializeBuffersGPU(void); static void UpdateBuffers(void); @@ -704,6 +688,7 @@ void rlDeleteTextures(unsigned int id) glDeleteTextures(1, &id); } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Unload shader from GPU memory void rlDeleteShader(unsigned int id) { @@ -714,6 +699,7 @@ void rlEnableFBO(void) { glBindFramebuffer(GL_FRAMEBUFFER, fbo); } +#endif // Unload vertex data (VAO) from GPU memory void rlDeleteVertexArrays(unsigned int id) @@ -868,39 +854,9 @@ void rlglInit(void) for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); // Init default Shader (GLSL 110) -> Common for GL 3.3+ and ES2 - defaultShaderProgram = LoadDefaultShader(); - simpleShaderProgram = LoadSimpleShader(); - //simpleShaderProgram = LoadCustomShader("custom.vs", "custom.fs"); // Works ok - //customShaderProgram = LoadCustomShader("simple150.vert", "simple150.frag"); - - // Get handles to GLSL input vars locations for defaultShaderProgram - //------------------------------------------------------------------- - defaultVertexLoc = glGetAttribLocation(defaultShaderProgram, "vertexPosition"); - defaultTexcoordLoc = glGetAttribLocation(defaultShaderProgram, "vertexTexCoord"); - defaultColorLoc = glGetAttribLocation(defaultShaderProgram, "vertexColor"); - - // Get handles to GLSL uniform vars locations (vertex-shader) - defaultModelviewMatrixLoc = glGetUniformLocation(defaultShaderProgram, "modelviewMatrix"); - defaultProjectionMatrixLoc = glGetUniformLocation(defaultShaderProgram, "projectionMatrix"); - - // Get handles to GLSL uniform vars locations (fragment-shader) - defaultTextureLoc = glGetUniformLocation(defaultShaderProgram, "texture0"); - //-------------------------------------------------------------------- - - // Get handles to GLSL input vars locations for simpleShaderProgram - //------------------------------------------------------------------- - simpleVertexLoc = glGetAttribLocation(simpleShaderProgram, "vertexPosition"); - simpleTexcoordLoc = glGetAttribLocation(simpleShaderProgram, "vertexTexCoord"); - simpleNormalLoc = glGetAttribLocation(defaultShaderProgram, "vertexNormal"); - - // Get handles to GLSL uniform vars locations (vertex-shader) - simpleModelviewMatrixLoc = glGetUniformLocation(simpleShaderProgram, "modelviewMatrix"); - simpleProjectionMatrixLoc = glGetUniformLocation(simpleShaderProgram, "projectionMatrix"); - - // Get handles to GLSL uniform vars locations (fragment-shader) - simpleTextureLoc = glGetUniformLocation(simpleShaderProgram, "texture0"); - simpleColorLoc = glGetUniformLocation(simpleShaderProgram, "fragColor"); - //-------------------------------------------------------------------- + defaultShader = LoadDefaultShader(); + simpleShader = LoadSimpleShader(); + //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok InitializeBuffers(); // Init vertex arrays InitializeBuffersGPU(); // Init VBO and VAO @@ -913,7 +869,7 @@ void rlglInit(void) // Create default white texture for plain colors (required by shader) unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglLoadTexture(pixels, 1, 1, false); + whiteTexture = rlglLoadTexture(pixels, 1, 1, R8G8B8A8, false); if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); else TraceLog(WARNING, "Base white texture could not be loaded"); @@ -932,6 +888,7 @@ void rlglInit(void) #endif } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Init postpro system void rlglInitPostpro(void) { @@ -959,7 +916,7 @@ void rlglInitPostpro(void) glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - // Attach colort texture and depth texture to FBO + // Attach color texture and depth texture to FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboColorTexture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fboDepthTexture, 0); @@ -974,6 +931,7 @@ void rlglInitPostpro(void) // TODO: Init simple quad VAO and data here? } +#endif // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) @@ -1012,8 +970,8 @@ void rlglClose(void) //glDetachShader(defaultShaderProgram, f); //glDeleteShader(v); //glDeleteShader(f); - glDeleteProgram(defaultShaderProgram); - glDeleteProgram(simpleShaderProgram); + glDeleteProgram(defaultShader.id); + glDeleteProgram(simpleShader.id); // Free vertex arrays memory free(lines.vertices); @@ -1043,14 +1001,11 @@ void rlglDraw(void) if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { - if (fbo == 0) glUseProgram(defaultShaderProgram); // Use our default shader - else glUseProgram(fboShader); // Use our postpro shader - - glUseProgram(defaultShaderProgram); + glUseProgram(defaultShader.id); - glUniformMatrix4fv(defaultProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(defaultModelviewMatrixLoc, 1, false, GetMatrixVector(modelview)); - glUniform1i(defaultTextureLoc, 0); + glUniformMatrix4fv(defaultShader.projectionLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(defaultShader.modelviewLoc, 1, false, GetMatrixVector(modelview)); + glUniform1i(defaultShader.textureLoc, 0); } // NOTE: We draw in this order: triangle shapes, textured quads and lines @@ -1066,12 +1021,12 @@ void rlglDraw(void) else { glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultVertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultColorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); } glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); @@ -1094,16 +1049,16 @@ void rlglDraw(void) { // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultVertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); - glVertexAttribPointer(defaultTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultTexcoordLoc); + glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.texcoordLoc); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultColorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); } @@ -1151,12 +1106,12 @@ void rlglDraw(void) else { glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultVertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultColorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); } glDrawArrays(GL_LINES, 0, lines.vCounter); @@ -1167,6 +1122,8 @@ void rlglDraw(void) if (vaoSupported) glBindVertexArray(0); // Unbind VAO + glUseProgram(0); // Unbind shader program + // Reset draws counter drawsCounter = 1; draws[0].textureId = whiteTexture; @@ -1185,6 +1142,7 @@ void rlglDraw(void) #endif } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void rlglDrawPostpro(unsigned int shaderId) { glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1245,6 +1203,7 @@ void rlglDrawPostpro(unsigned int shaderId) rlglDraw(); } +#endif // Draw a 3d model void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires) @@ -1289,35 +1248,26 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (customShader) glUseProgram(model.shaderId); - else glUseProgram(simpleShaderProgram); // Use our simple shader + glUseProgram(model.shader.id); VectorScale(&rotation, DEG2RAD); // Get transform matrix (rotation -> scale -> translation) - Matrix transform = MatrixTransform(position, rotation, scale); - Matrix modelviewworld = MatrixMultiply(transform, modelview); + Matrix transform = MatrixTransform(position, rotation, scale); // Object-space transformation + Matrix modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation + + // Projection: Screen-space transformation // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader - if (customShader) - { - glUniformMatrix4fv(customProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(customModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld)); - glUniform1i(customTextureLoc, 0); - } - else - { - glUniformMatrix4fv(simpleProjectionMatrixLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(simpleModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld)); - glUniform1i(simpleTextureLoc, 0); - } + glUniformMatrix4fv(model.shader.projectionLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(model.shader.modelviewLoc, 1, false, GetMatrixVector(modelviewworld)); + glUniform1i(model.shader.textureLoc, 0); // Apply color tinting to model // NOTE: Just update one uniform on fragment shader float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 }; - if (customShader) glUniform4fv(customColorLoc, 1, vColor); - else glUniform4fv(simpleColorLoc, 1, vColor); + glUniform4fv(model.shader.tintColorLoc, 1, vColor); if (vaoSupported) { @@ -1327,20 +1277,20 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal { // Bind model VBOs data glBindBuffer(GL_ARRAY_BUFFER, model.vboId[0]); - glVertexAttribPointer(simpleVertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(simpleVertexLoc); + glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(model.shader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, model.vboId[1]); - glVertexAttribPointer(simpleTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(simpleTexcoordLoc); + glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(model.shader.texcoordLoc); // Add normals support glBindBuffer(GL_ARRAY_BUFFER, model.vboId[2]); - glVertexAttribPointer(simpleNormalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(simpleNormalLoc); + glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(model.shader.normalLoc); } - glBindTexture(GL_TEXTURE_2D, model.textureId); + glBindTexture(GL_TEXTURE_2D, model.texture.id); glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); @@ -1406,7 +1356,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) } // Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps) +unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps) { glBindTexture(GL_TEXTURE_2D,0); // Free any old binding @@ -1482,7 +1432,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) // NOTE: On embedded systems, we let the driver choose the best internal format //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // WebGL + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // WebGL // TODO: Add support for multiple color modes (16bit color modes and grayscale) // Ref: https://www.khronos.org/opengles/sdk/docs/man3/html/glTexImage2D.xhtml @@ -1494,6 +1444,17 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge // GL_RGBA4 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4 // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE + + switch (colorMode) + { + case GRAYSCALE: glTexImage2D(GL_TEXTURE_2D, 0, GL_R, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data); break; + case R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); break; + case R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); break; + case R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data); break; + case R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); break; + case R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; + default: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; + } if (genMipmaps) { @@ -1520,17 +1481,19 @@ Model rlglLoadModel(VertexData mesh) model.mesh = mesh; #if defined(GRAPHICS_API_OPENGL_11) - model.textureId = 0; // No texture required + model.texture.id = 0; // No texture required model.vaoId = 0; // Vertex Array Object model.vboId[0] = 0; // Vertex position VBO model.vboId[1] = 0; // Texcoords VBO model.vboId[2] = 0; // Normals VBO #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - model.textureId = whiteTexture; // Default whiteTexture + model.texture.id = whiteTexture; // Default whiteTexture + model.texture.width = 1; // Default whiteTexture width + model.texture.height = 1; // Default whiteTexture height - GLuint vaoModel = 0; // Vertex Array Objects (VAO) - GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) + GLuint vaoModel = 0; // Vertex Array Objects (VAO) + GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) if (vaoSupported) { @@ -1545,20 +1508,22 @@ Model rlglLoadModel(VertexData mesh) // Enable vertex attributes: position glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(simpleVertexLoc); - glVertexAttribPointer(simpleVertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(simpleShader.vertexLoc); + glVertexAttribPointer(simpleShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: texcoords glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW); - glEnableVertexAttribArray(simpleTexcoordLoc); - glVertexAttribPointer(simpleTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(simpleShader.texcoordLoc); + glVertexAttribPointer(simpleShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: normals glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW); - glEnableVertexAttribArray(simpleNormalLoc); - glVertexAttribPointer(simpleNormalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(simpleShader.normalLoc); + glVertexAttribPointer(simpleShader.normalLoc, 3, GL_FLOAT, 0, 0, 0); + + model.shader = simpleShader; // By default, simple shader will be used model.vboId[0] = vertexBuffer[0]; // Vertex position VBO model.vboId[1] = vertexBuffer[1]; // Texcoords VBO @@ -1661,6 +1626,7 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh return id; } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load a shader (vertex shader + fragment shader) from text data unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) { @@ -1756,6 +1722,7 @@ unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) return program; } +#endif // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) @@ -1781,68 +1748,69 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -unsigned int LoadCustomShader(char *vsFileName, char *fsFileName) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Load a shader (vertex shader + fragment shader) from text data +Shader LoadShader(char *vsFileName, char *fsFileName) { + Shader shader; + // Shaders loading from external text file char *vShaderStr = TextFileRead(vsFileName); char *fShaderStr = TextFileRead(fsFileName); - unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShader(vShaderStr, fShaderStr); - if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shaderId); - else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shaderId); + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shader.id); - return shaderId; - // Shader strings must be freed free(vShaderStr); free(fShaderStr); + + // Get handles to GLSL input attibute locations + //------------------------------------------------------------------- + shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); + shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); + // NOTE: custom shader does not use colorLoc - return shaderId; + // Get handles to GLSL uniform locations (vertex shader) + shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); + shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); + + // Get handles to GLSL uniform locations (fragment shader) + shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); + shader.tintColorLoc = glGetUniformLocation(shader.id, "tintColor"); + //-------------------------------------------------------------------- + + return shader; } // Link shader to model -void SetModelShader(Model *model, unsigned int shader) +void SetModelShader(Model *model, Shader shader) { - // Get handles to GLSL input vars locations for simpleShaderProgram - customVertexLoc = glGetAttribLocation(shader, "vertexPosition"); - customTexcoordLoc = glGetAttribLocation(shader, "vertexTexCoord"); - customNormalLoc = glGetAttribLocation(shader, "vertexNormal"); - - // Get handles to GLSL uniform vars locations (vertex-shader) - customModelviewMatrixLoc = glGetUniformLocation(shader, "modelviewMatrix"); - customProjectionMatrixLoc = glGetUniformLocation(shader, "projectionMatrix"); - - // Get handles to GLSL uniform vars locations (fragment-shader) - customTextureLoc = glGetUniformLocation(shader, "texture0"); - customColorLoc = glGetUniformLocation(shader, "fragColor"); - + model->shader = shader; + if (vaoSupported) glBindVertexArray(model->vaoId); // Enable vertex attributes: position glBindBuffer(GL_ARRAY_BUFFER, model->vboId[0]); - glEnableVertexAttribArray(customVertexLoc); - glVertexAttribPointer(customVertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shader.vertexLoc); + glVertexAttribPointer(shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: texcoords glBindBuffer(GL_ARRAY_BUFFER, model->vboId[1]); - glEnableVertexAttribArray(customTexcoordLoc); - glVertexAttribPointer(customTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shader.texcoordLoc); + glVertexAttribPointer(shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: normals glBindBuffer(GL_ARRAY_BUFFER, model->vboId[2]); - glEnableVertexAttribArray(customNormalLoc); - glVertexAttribPointer(customNormalLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shader.normalLoc); + glVertexAttribPointer(shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); if (vaoSupported) glBindVertexArray(0); // Unbind VAO - - model->shaderId = shader; - - customShader = true; } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - void PrintProjectionMatrix() { PrintMatrix(projection); @@ -1863,8 +1831,10 @@ void PrintModelviewMatrix() // Load Shader (Vertex and Fragment) // NOTE: This shader program is used only for batch buffers (lines, triangles, quads) -static GLuint LoadDefaultShader(void) +static Shader LoadDefaultShader(void) { + Shader shader; + // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ // Just defined #version 330 despite shader is #version 110 @@ -1904,18 +1874,35 @@ static GLuint LoadDefaultShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" "} \n"; - unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShader(vShaderStr, fShaderStr); + + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); + + // Get handles to GLSL input attibute locations + //------------------------------------------------------------------- + shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); + shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.colorLoc = glGetAttribLocation(shader.id, "vertexColor"); + // NOTE: default shader does not use normalLoc + + // Get handles to GLSL uniform locations (vertex shader) + shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); + shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); - if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shaderId); - else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shaderId); + // Get handles to GLSL uniform locations (fragment shader) + shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); + //-------------------------------------------------------------------- - return shaderId; + return shader; } // Load Simple Shader (Vertex and Fragment) // NOTE: This shader program is used to render models -static GLuint LoadSimpleShader(void) +static Shader LoadSimpleShader(void) { + Shader shader; + // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ // Just defined #version 330 despite shader is #version 110 @@ -1947,18 +1934,34 @@ static GLuint LoadSimpleShader(void) #endif "uniform sampler2D texture0; \n" "varying vec2 fragTexCoord; \n" - "uniform vec4 fragColor; \n" + "uniform vec4 tintColor; \n" "void main() \n" "{ \n" - " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" + " gl_FragColor = texture2D(texture0, fragTexCoord) * tintColor; \n" "} \n"; - unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShader(vShaderStr, fShaderStr); + + if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id); + else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id); + + // Get handles to GLSL input attibute locations + //------------------------------------------------------------------- + shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); + shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); + // NOTE: simple shader does not use colorLoc + + // Get handles to GLSL uniform locations (vertex shader) + shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); + shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); - if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shaderId); - else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shaderId); + // Get handles to GLSL uniform locations (fragment shader) + shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); + shader.tintColorLoc = glGetUniformLocation(shader.id, "tintColor"); + //-------------------------------------------------------------------- - return shaderId; + return shader; } // Read text file @@ -2056,6 +2059,7 @@ static void InitializeBuffers(void) } // Initialize Vertex Array Objects (Contain VBO) +// NOTE: lines, triangles and quads buffers use defaultShader static void InitializeBuffersGPU(void) { if (vaoSupported) @@ -2071,14 +2075,14 @@ static void InitializeBuffersGPU(void) // Lines - Vertex positions buffer binding and attributes enable glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultVertexLoc); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); // Lines - colors buffer glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultColorLoc); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines); else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]); @@ -2097,13 +2101,13 @@ static void InitializeBuffersGPU(void) // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultVertexLoc); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultColorLoc); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles); else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]); @@ -2122,18 +2126,18 @@ static void InitializeBuffersGPU(void) // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultVertexLoc); - glVertexAttribPointer(defaultVertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultTexcoordLoc); - glVertexAttribPointer(defaultTexcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(defaultShader.texcoordLoc); + glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultColorLoc); - glVertexAttribPointer(defaultColorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); // Fill index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); diff --git a/src/rlgl.h b/src/rlgl.h index 94cf6072..ff2bcd4e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -32,7 +32,7 @@ //#define RLGL_STANDALONE // NOTE: To use rlgl as standalone lib, just uncomment this line #ifndef RLGL_STANDALONE - #include "raylib.h" // Required for typedef: Model + #include "raylib.h" // Required for typedef(s): Model, Shader, Texture2D #include "utils.h" // Required for function TraceLog() #endif @@ -88,20 +88,50 @@ typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; +typedef enum { GRAYSCALE = 0, R5G6B5, R8G8B8, R5G5B5A1, R4G4B4A4, R8G8B8A8 } ColorMode; + #ifdef RLGL_STANDALONE - typedef struct { + // VertexData type + typedef struct VertexData { int vertexCount; float *vertices; // 3 components per vertex float *texcoords; // 2 components per vertex float *normals; // 3 components per vertex unsigned char *colors; } VertexData; - + + // Texture2D type + typedef struct Texture2D { + unsigned int id; // Texture id + int width; + int height; + } Texture2D; + + // Shader type + typedef struct Shader { + unsigned int id; // Shader program id + + // Variable attributes + unsigned int vertexLoc; // Vertex attribute location point (vertex shader) + unsigned int texcoordLoc; // Texcoord attribute location point (vertex shader) + unsigned int normalLoc; // Normal attribute location point (vertex shader) + unsigned int colorLoc; // Color attibute location point (vertex shader) + + // Uniforms + unsigned int projectionLoc; // Projection matrix uniform location point (vertex shader) + unsigned int modelviewLoc; // ModeView matrix uniform location point (vertex shader) + unsigned int textureLoc; // Texture uniform location point (fragment shader) + unsigned int tintColorLoc; // Color uniform location point (fragment shader) + } Shader; + + // 3d Model type + // NOTE: If using OpenGL 1.1, loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId) typedef struct Model { VertexData mesh; unsigned int vaoId; unsigned int vboId[4]; - unsigned int textureId; + Texture2D texture; + Shader shader; //Matrix transform; } Model; #endif @@ -163,9 +193,11 @@ void rlglDraw(void); // Draw VAO/VBO void rlglDrawPostpro(unsigned int shaderId); // Draw with postpro shader void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) -unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture +unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps); // Load in GPU OpenGL texture unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data +#endif Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires); diff --git a/src/textures.c b/src/textures.c index 768ead78..c56816e0 100644 --- a/src/textures.c +++ b/src/textures.c @@ -306,7 +306,7 @@ Texture2D LoadTexture(const char *fileName) if (image.compFormat == 0) { - texture.id = rlglLoadTexture(image.data, image.width, image.height, false); + texture.id = rlglLoadTexture(image.data, image.width, image.height, R8G8B8A8, false); } else { @@ -352,6 +352,8 @@ Texture2D LoadTexture(const char *fileName) return texture; } +// TODO: Texture2D LoadTextureEx(const char *imageData, int width, int height, int colorMode) + // Load a texture from image data // NOTE: image is not unloaded, it must be done manually Texture2D LoadTextureFromImage(Image image, bool genMipmaps) @@ -380,7 +382,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) } // NOTE: rlglLoadTexture() can generate mipmaps (POT image required) - texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps); + texture.id = rlglLoadTexture(imgData, image.width, image.height, R8G8B8A8, genMipmaps); texture.width = image.width; texture.height = image.height; -- cgit v1.2.3 From 3cad094edd94f859540b16bbdafcf2cfb9d02016 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Mon, 2 Mar 2015 20:52:58 +0100 Subject: Changes integration (some WIP, view details) Corrected GetMouseWheelMove() Corrected camera system Reviewed cubicmap resolution (collision detection) Added QuaternionTransform() WIP: Raycast system (not working) WIP: Reviewing axis-angle rotations... --- src/core.c | 154 +++++++++++++++++++++++++++++++++++++++-------- src/models.c | 172 ++++++++++++++++++++++++++++++++++++----------------- src/raylib.h | 15 ++++- src/raymath.c | 20 +++++-- src/raymath.h | 3 +- src/rlgl.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/rlgl.h | 3 +- 7 files changed, 452 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index c61381fd..a2344395 100644 --- a/src/core.c +++ b/src/core.c @@ -106,7 +106,8 @@ // FREE_CAMERA #define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 -#define FREE_CAMERA_DISTANCE_CLAMP 0.3 +#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3 +#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12 #define FREE_CAMERA_MIN_CLAMP 85 #define FREE_CAMERA_MAX_CLAMP -85 #define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 @@ -118,7 +119,7 @@ // FIRST_PERSON #define FIRST_PERSON_MOUSE_SENSITIVITY 0.003 #define FIRST_PERSON_FOCUS_DISTANCE 25 -#define FIRST_PERSON_MIN_CLAMP 5 +#define FIRST_PERSON_MIN_CLAMP 85 #define FIRST_PERSON_MAX_CLAMP -85 #define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0 @@ -604,8 +605,8 @@ void Begin3dMode(Camera camera) if (cameraMode == CAMERA_CUSTOM) currentCamera = camera; else currentCamera = internalCamera; - Matrix matLookAt = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); - rlMultMatrixf(GetMatrixVector(matLookAt)); // Multiply MODELVIEW matrix by view matrix (camera) + Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + rlMultMatrixf(GetMatrixVector(view)); // Multiply MODELVIEW matrix by view matrix (camera) } // Ends 3D mode and returns to default 2D orthographic mode @@ -703,6 +704,60 @@ void ShowLogo(void) showLogo = true; } +Ray GetMouseRay(Vector2 mousePosition, Camera camera) +{ + Ray ray; + + Matrix proj = MatrixIdentity(); + Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + + float aspect = (GLfloat)GetScreenWidth()/(GLfloat)GetScreenHeight(); + double top = 0.1f*tanf(45.0f*PI / 360.0f); + double right = top*aspect; + + proj = MatrixFrustum(-right, right, -top, top, 0.01f, 1000.0f); + MatrixTranspose(&proj); + + float realy = (float)GetScreenHeight() - mousePosition.y; + + float z; + // glReadPixels(mousePosition.x, mousePosition.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); + //http://www.bfilipek.com/2012/06/select-mouse-opengl.html + + Vector3 nearPoint = { mousePosition.x, realy, 0.0f }; + Vector3 farPoint = { mousePosition.x, realy, 1.0f }; + + nearPoint = internalCamera.position; + farPoint = rlglUnproject(farPoint, proj, view); + + Vector3 direction = VectorSubtract(farPoint, nearPoint); + VectorNormalize(&direction); + + ray.position = nearPoint; + ray.direction = direction; + + // Test + Vector2 screenPos; + screenPos.x = (mousePosition.x / (float)GetScreenWidth() * 2.0) - 1.0f; + screenPos.y = (mousePosition.y / (float)GetScreenHeight() * 2.0) - 1.0f; + + direction = VectorSubtract(internalCamera.target, internalCamera.position); + + printf("/nScreenPos %f, %f", screenPos.x, screenPos.y); + + Matrix rotate; + rotate = MatrixIdentity(); + rotate = MatrixRotate(0, 45*DEG2RAD*screenPos.y, 0); + VectorTransform(&direction, rotate); + + VectorNormalize(&direction); + + ray.position = internalCamera.position; + ray.direction = direction; + + return ray; +} + void SetCameraMode(int mode) { if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE)) @@ -721,28 +776,30 @@ void SetCameraMode(int mode) } else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE)) { - cameraMode = CAMERA_THIRD_PERSON; - cameraTargetDistance = 5; - internalCamera.position = (Vector3){ -1, 1, -1 }; - internalCamera.target = (Vector3){ 3, 0, 3}; + cameraTargetDistance = 10; + cameraAngle.x = 45 * DEG2RAD; + cameraAngle.y = -40 * DEG2RAD; + internalCamera.target = (Vector3){ 0, 0, 0}; ProcessCamera(&internalCamera, &internalCamera.position); } else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL)) { - cameraMode = CAMERA_THIRD_PERSON; - cameraTargetDistance = 5; - cameraAngle.x = 45 * DEG2RAD; - cameraAngle.y = -20 * DEG2RAD; + cameraTargetDistance = 10; + cameraAngle.x = 225 * DEG2RAD; + cameraAngle.y = -40 * DEG2RAD; + internalCamera.target = (Vector3){ 3, 0, 3}; ProcessCamera(&internalCamera, &internalCamera.position); } cameraMode = mode; } -void UpdateCamera(Vector3 *playerPosition) +Camera UpdateCamera(Vector3 *position) { // Calculate camera - if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, playerPosition); + if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position); + + return internalCamera; } //---------------------------------------------------------------------------------- @@ -858,10 +915,6 @@ void SetMousePosition(Vector2 position) // Returns mouse wheel movement Y int GetMouseWheelMove(void) { - previousMouseWheelY = currentMouseWheelY; - - currentMouseWheelY = 0; - return previousMouseWheelY; } #endif @@ -1839,6 +1892,9 @@ static void PollInputEvents(void) // Register previous mouse states for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; + previousMouseWheelY = currentMouseWheelY; + currentMouseWheelY = 0; + glfwPollEvents(); // Register keyboard/mouse events... and window events! #elif defined(PLATFORM_ANDROID) @@ -2337,12 +2393,13 @@ static void LogoAnimation(void) static void ProcessCamera(Camera *camera, Vector3 *playerPosition) { // Mouse movement detection - if (fullscreen) + if (cameraMode != CAMERA_FREE) { - if (GetMousePosition().x < 100) SetMousePosition((Vector2){ screenWidth - 100, GetMousePosition().y}); - else if (GetMousePosition().y < 100) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - 100}); - else if (GetMousePosition().x > screenWidth - 100) SetMousePosition((Vector2) { 100, GetMousePosition().y}); - else if (GetMousePosition().y > screenHeight - 100) SetMousePosition((Vector2){ GetMousePosition().x, 100}); + HideCursor(); + if (GetMousePosition().x < GetScreenHeight() / 3) SetMousePosition((Vector2){ screenWidth - GetScreenHeight() / 3, GetMousePosition().y}); + else if (GetMousePosition().y < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - GetScreenHeight() / 3}); + else if (GetMousePosition().x > screenWidth - GetScreenHeight() / 3) SetMousePosition((Vector2) { GetScreenHeight() / 3, GetMousePosition().y}); + else if (GetMousePosition().y > screenHeight - GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() / 3}); else { cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; @@ -2351,6 +2408,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) } else { + ShowCursor(); cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; } @@ -2366,11 +2424,55 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL; // Camera zoom - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP; + } + // Camera looking down + else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + + if (camera->target.y < 0) camera->target.y = -0.001; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; + } + // Camera looking up + else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + + if (camera->target.y > 0) camera->target.y = 0.001; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; + } - // Camera distance clamp - if (cameraTargetDistance < FREE_CAMERA_DISTANCE_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_CLAMP; + // Inputs if (IsKeyDown(KEY_LEFT_ALT)) { if (IsKeyDown(KEY_LEFT_CONTROL)) diff --git a/src/models.c b/src/models.c index a8bfe37f..771d25a4 100644 --- a/src/models.c +++ b/src/models.c @@ -40,7 +40,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -// Nop... +#define CUBIC_MAP_HALF_BLOCK_SIZE 0.5 //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -285,7 +285,6 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); - //rlRotatef(rotation, 0, 1, 0); rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -325,8 +324,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); - //rlRotatef(rotation, 0, 1, 0); - + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -548,6 +546,19 @@ void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, rlPopMatrix(); } +void DrawRay(Ray ray, Color color) +{ + float scale = 10000; + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex3f(ray.position.x, ray.position.y, ray.position.z); + rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale); + rlEnd(); +} + // Draw a grid centered at (0, 0, 0) void DrawGrid(int slices, float spacing) { @@ -1216,26 +1227,28 @@ void SetModelTexture(Model *model, Texture2D texture) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; - Vector3 rotation = { 0, 0, 0 }; + float rotAngle = 0.0f; + Vector3 rotAxis = { 0, 0, 0 }; - rlglDrawModel(model, position, rotation, vScale, tint, false); + rlglDrawModel(model, position, rotAngle, rotAxis, vScale, tint, false); } // Draw a model with extended parameters -void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint) +void DrawModelEx(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color tint) { // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() - rlglDrawModel(model, position, rotation, scale, tint, false); + rlglDrawModel(model, position, rotationAngle, rotationAxis, scale, tint, false); } // Draw a model wires (with texture if set) void DrawModelWires(Model model, Vector3 position, float scale, Color color) { Vector3 vScale = { scale, scale, scale }; - Vector3 rotation = { 0, 0, 0 }; + float rotAngle = 0.0f; + Vector3 rotAxis = { 0, 0, 0 }; - rlglDrawModel(model, position, rotation, vScale, color, true); + rlglDrawModel(model, position, rotAngle, rotAxis, vScale, color, true); } // Draw a billboard @@ -1418,14 +1431,16 @@ bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSph // Detect and resolve cubicmap collisions // NOTE: player position (or camera) is modified inside this function -void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition) +Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius) { // Detect the cell where the player is located + Vector3 impactDirection = { 0, 0, 0 }; + int locationCellX = 0; int locationCellY = 0; - locationCellX = floor(playerPosition->x + mapPosition.x + 0.5); - locationCellY = floor(playerPosition->z + mapPosition.z + 0.5); + locationCellX = floor(playerPosition->x + mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE); + locationCellY = floor(playerPosition->z + mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE); // Multiple Axis -------------------------------------------------------------------------------------------- @@ -1433,11 +1448,12 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { - playerPosition->x = locationCellX - 0.2; - playerPosition->z = locationCellY - 0.2; + playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; } } @@ -1445,11 +1461,12 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { - playerPosition->x = locationCellX - 0.2; - playerPosition->z = locationCellY + 0.2; + playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; } } @@ -1457,11 +1474,12 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { - playerPosition->x = locationCellX + 0.2; - playerPosition->z = locationCellY - 0.2; + playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; } } @@ -1469,11 +1487,12 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { - playerPosition->x = locationCellX + 0.2f; - playerPosition->z = locationCellY + 0.2f; + playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; } } @@ -1482,33 +1501,37 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play // Axis x- if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) { - if ((playerPosition->x + 0.5f) - locationCellX < 0.3) + if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) { - playerPosition->x = locationCellX - 0.2; + playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 0}; } } // Axis x+ if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) { - if ((playerPosition->x + 0.5f) - locationCellX > 0.7) + if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) { - playerPosition->x = locationCellX + 0.2; + playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 0}; } } // Axis y- if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) { - if ((playerPosition->z + 0.5f) - locationCellY < 0.3) + if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius) { - playerPosition->z = locationCellY - 0.2; + playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 0, 0, 1}; } } // Axis y+ if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) { - if ((playerPosition->z + 0.5f) - locationCellY > 0.7) + if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius) { - playerPosition->z = locationCellY + 0.2; + playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 0, 0, 1}; } } @@ -1519,11 +1542,18 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { - if (((playerPosition->x + 0.5f) - locationCellX) > ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX - 0.2; - else playerPosition->z = locationCellY - 0.2; + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } } } @@ -1532,11 +1562,18 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX < 0.3) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { - if (((playerPosition->x + 0.5f) - locationCellX) > (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX - 0.2; - else playerPosition->z = locationCellY + 0.2; + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } } } @@ -1545,11 +1582,18 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY < 0.3)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { - if (((playerPosition->x + 0.5f) - locationCellX) < (1 - ((playerPosition->z + 0.5f) - locationCellY))) playerPosition->x = locationCellX + 0.2; - else playerPosition->z = locationCellY - 0.2; + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } } } @@ -1558,13 +1602,35 @@ void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *play (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - if (((playerPosition->x + 0.5f) - locationCellX > 0.7) && - ((playerPosition->z + 0.5f) - locationCellY > 0.7)) + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { - if (((playerPosition->x + 0.5f) - locationCellX) < ((playerPosition->z + 0.5f) - locationCellY)) playerPosition->x = locationCellX + 0.2; - else playerPosition->z = locationCellY + 0.2; + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } } } + + // Floor collision + if (playerPosition->y <= radius) + { + playerPosition->y = radius + 0.01; + impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z}; + } + // Roof collision + else if (playerPosition->y >= 1.5 - radius) + { + playerPosition->y = (1.5 - radius) - 0.01; + impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z}; + } + + return impactDirection; } //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index 3b6f9320..c6c3e32a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -294,6 +294,12 @@ typedef struct Model { //Matrix transform; } Model; +// Ray type (useful for raycast) +typedef struct Ray { + Vector3 position; + Vector3 direction; +} Ray; + // Sound source type typedef struct Sound { unsigned int source; @@ -355,7 +361,7 @@ int GetRandomValue(int min, int max); // Returns a random Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void SetCameraMode(int mode); // Multiple camera modes available -void UpdateCamera(Vector3 *playerPosition); // Update camera with player position (when using internal camera) +Camera UpdateCamera(Vector3 *position); // Update camera with position (when using internal camera) void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) @@ -363,6 +369,8 @@ void ShowLogo(void); // Activates raylib void InitPostShader(void); // Initialize fullscreen postproduction shaders system void SetPostShader(unsigned int shader); // Set fullscreen postproduction shader +Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Gives the rayTrace from mouse position + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ @@ -485,6 +493,7 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions +void DrawRay(Ray ray, Color color); void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) void DrawGizmo(Vector3 position); // Draw simple gizmo void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); // Draw gizmo with extended parameters @@ -502,7 +511,7 @@ void SetModelTexture(Model *model, Texture2D texture); void SetModelShader(Model *model, Shader shader); // Link a shader to a model (not available on OpenGL 1.1) void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) -void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint); // Draw a model with extended parameters +void DrawModelEx(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color tint); // Draw a model with extended parameters void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires (with texture if set) void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture @@ -513,7 +522,7 @@ Shader LoadShader(char *vsFileName, char *fsFileName); bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); -void ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition); +Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Return the normal vector of the impacted surface //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) diff --git a/src/raymath.c b/src/raymath.c index df098c6a..8ad50312 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -346,8 +346,6 @@ void MatrixInvert(Matrix *mat) temp.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; temp.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - PrintMatrix(temp); - *mat = temp; } @@ -671,8 +669,8 @@ Matrix MatrixRotateY(float angle) { Matrix result = MatrixIdentity(); - float cosres = (float)cos(angle); - float sinres = (float)sin(angle); + float cosres = cosf(angle); + float sinres = sinf(angle); result.m0 = cosres; result.m2 = sinres; @@ -1097,4 +1095,18 @@ void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) *outAxis = resAxis; *outAngle = resAngle; +} + +// Transform a quaternion given a transformation matrix +void QuaternionTransform(Quaternion *q, Matrix mat) +{ + float x = q->x; + float y = q->y; + float z = q->z; + float w = q->w; + + q->x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12*w; + q->y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13*w; + q->z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14*w; + q->w = mat.m3*x + mat.m7*y + mat.m11*z + mat.m15*w; } \ No newline at end of file diff --git a/src/raymath.h b/src/raymath.h index c8c1a26c..b3676ed9 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -91,7 +91,7 @@ void VectorNormalize(Vector3 *v); // Normalize provided ve float VectorDistance(Vector3 v1, Vector3 v2); // Calculate distance between two points Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount); // Calculate linear interpolation between two vectors Vector3 VectorReflect(Vector3 vector, Vector3 normal); // Calculate reflected vector to normal -void VectorTransform(Vector3 *v, Matrix mat); // Transforms a Vector3 with a given Matrix +void VectorTransform(Vector3 *v, Matrix mat); // Transforms a Vector3 by a given Matrix Vector3 VectorZero(void); // Return a Vector3 init to zero //------------------------------------------------------------------------------------ @@ -134,6 +134,7 @@ Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a qua Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle); // Returns rotation quaternion for an angle around an axis Matrix QuaternionToMatrix(Quaternion q); // Calculates the matrix from the given quaternion void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle); // Returns the axis and the angle for a given quaternion +void QuaternionTransform(Quaternion *q, Matrix mat); // Transform a quaternion given a transformation matrix #ifdef __cplusplus } diff --git a/src/rlgl.c b/src/rlgl.c index 1664c5c2..7d947d66 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -303,26 +303,26 @@ void rlTranslatef(float x, float y, float z) void rlRotatef(float angleDeg, float x, float y, float z) { // TODO: Support rotation in multiple axes - Matrix rot = MatrixIdentity(); + Matrix rotation = MatrixIdentity(); // OPTION 1: It works... - if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD); - else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD); - else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD); + //if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD); + //else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD); + //else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD); // OPTION 2: Requires review... - //Vector3 vec = (Vector3){ 0, 1, 0 }; - //VectorNormalize(&vec); - //rot = MatrixFromAxisAngle(vec, angleDeg*DEG2RAD); // Working? + Vector3 axis = (Vector3){ x, y, z }; + VectorNormalize(&axis); + rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD); // OPTION 3: TODO: Review, it doesn't work! //Vector3 vec = (Vector3){ x, y, z }; //VectorNormalize(&vec); //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x); - MatrixTranspose(&rot); + MatrixTranspose(&rotation); - *currentMatrix = MatrixMultiply(*currentMatrix, rot); + *currentMatrix = MatrixMultiply(*currentMatrix, rotation); } // Multiply the current matrix by a scaling matrix @@ -1206,7 +1206,7 @@ void rlglDrawPostpro(unsigned int shaderId) #endif // Draw a 3d model -void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires) +void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires) { #if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glPolygonMode() not available on OpenGL ES @@ -1215,7 +1215,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal #if defined(GRAPHICS_API_OPENGL_11) glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, model.textureId); + glBindTexture(GL_TEXTURE_2D, model.texture.id); // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array @@ -1230,7 +1230,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlScalef(scale.x, scale.y, scale.z); - rlRotatef(rotation.y, 0, 1, 0); + rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); // TODO: If rotate in multiple axis, get rotation matrix and use rlMultMatrix() @@ -1250,7 +1250,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(model.shader.id); - VectorScale(&rotation, DEG2RAD); + Vector3 rotation = { 0.0f, 0.0f, 0.0f }; // Get transform matrix (rotation -> scale -> translation) Matrix transform = MatrixTransform(position, rotation, scale); // Object-space transformation @@ -1335,7 +1335,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix rlLoadIdentity(); // Reset current matrix (PROJECTION) - rlOrtho(0, width - offsetX, height - offsetY, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0) + rlOrtho(0, width - offsetX, height - offsetY, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0) rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) @@ -1355,6 +1355,165 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) TraceLog(INFO, "OpenGL Graphics initialized successfully"); } +// Get world coordinates from screen coordinates +Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) +{ + //GLint viewport[4]; + //glGetIntegerv(GL_VIEWPORT, viewport); + + // Viewport data + int x = 0; + int y = 0; + int width = GetScreenWidth(); + int height = GetScreenHeight(); + float minDepth = 0.0f; + float maxDepth = 1.0f; +/* + Matrix modelviewprojection = MatrixMultiply(modelview, projection); + MatrixInvert(&modelviewprojection); + + Vector3 vector; + + vector.x = (((source.x - x) / ((float)width)) * 2.0f) - 1.0f; + vector.y = -((((source.y - y) / ((float)height)) * 2.0f) - 1.0f); + vector.z = (source.z - minDepth) / (maxDepth - minDepth); + + //float a = (((vector.x * matrix.M14) + (vector.y * matrix.M24)) + (vector.z * matrix.M34)) + matrix.M44; + //float a = (((vector.x * modelviewprojection.m3) + (vector.y * modelviewprojection.m7)) + (vector.z * modelviewprojection.m11)) + modelviewprojection.m15; + VectorTransform(&vector, modelviewprojection); + + //if (!MathUtil.IsOne(a)) vector = (vector / a); + //VectorScale(&vector, 1/a); + + return vector; +*/ +/* + Vector3 worldPoint; + + // Transformation matrices + Matrix modelviewprojection = MatrixIdentity(); + Quaternion quat; + + // Calculation for inverting a matrix, compute projection x modelview + modelviewprojection = MatrixMultiply(proj, view); + MatrixInvert(&modelviewprojection); + + // Transformation of normalized coordinates between -1 and 1 + quat.x = ((source.x - (float)x)/(float)width*2.0) - 1.0f; + quat.y = ((source.y - (float)y)/(float)height*2.0) - 1.0f; + quat.z = 2.0*source.z - 1.0; + quat.w = 1.0; + + // Objects coordinates + QuaternionTransform(&quat, modelviewprojection); + + //if (quat.w == 0.0) return 0; + + worldPoint.x = quat.x/quat.w; + worldPoint.y = quat.y/quat.w; + worldPoint.z = quat.z/quat.w; + + return worldPoint; + */ +/* + Quaternion quat; + Vector3 vec; + + quat.x = 2.0f * GetMousePosition().x / (float)width - 1; + quat.y = -(2.0f * GetMousePosition().y / (float)height - 1); + quat.z = 0; + quat.w = 1; + + Matrix invView; + MatrixInvert(&view); + Matrix invProj; + MatrixInvert(&proj); + + quat.x = invProj.m0 * quat.x + invProj.m4 * quat.y + invProj.m8 * quat.z + invProj.m12 * quat.w; + quat.y = invProj.m1 * quat.x + invProj.m5 * quat.y + invProj.m9 * quat.z + invProj.m13 * quat.w; + quat.z = invProj.m2 * quat.x + invProj.m6 * quat.y + invProj.m10 * quat.z + invProj.m14 * quat.w; + quat.w = invProj.m3 * quat.x + invProj.m7 * quat.y + invProj.m11 * quat.z + invProj.m15 * quat.w; + + quat.x = invView.m0 * quat.x + invView.m4 * quat.y + invView.m8 * quat.z + invView.m12 * quat.w; + quat.y = invView.m1 * quat.x + invView.m5 * quat.y + invView.m9 * quat.z + invView.m13 * quat.w; + quat.z = invView.m2 * quat.x + invView.m6 * quat.y + invView.m10 * quat.z + invView.m14 * quat.w; + quat.w = invView.m3 * quat.x + invView.m7 * quat.y + invView.m11 * quat.z + invView.m15 * quat.w; + + vec.x /= quat.w; + vec.y /= quat.w; + vec.z /= quat.w; + + return vec; + */ +/* + Vector3 worldPoint; + + // Transformation matrices + Matrix modelviewprojection; + Quaternion quat; + + // Calculation for inverting a matrix, compute projection x modelview + modelviewprojection = MatrixMultiply(view, proj); + + // Now compute the inverse of matrix A + MatrixInvert(&modelviewprojection); + + // Transformation of normalized coordinates between -1 and 1 + quat.x = ((source.x - (float)x)/(float)width*2.0) - 1.0f; + quat.y = ((source.y - (float)y)/(float)height*2.0) - 1.0f; + quat.z = 2.0*source.z - 1.0; + quat.w = 1.0; + + // Traspose quaternion and multiply + Quaternion result; + result.x = modelviewprojection.m0 * quad.x + modelviewprojection.m4 * quad.y + modelviewprojection.m8 * quad.z + modelviewprojection.m12 * quad.w; + result.y = modelviewprojection.m1 * quad.x + modelviewprojection.m5 * quad.y + modelviewprojection.m9 * quad.z + modelviewprojection.m13 * quad.w; + result.z = modelviewprojection.m2 * quad.x + modelviewprojection.m6 * quad.y + modelviewprojection.m10 * quad.z + modelviewprojection.m14 * quad.w; + result.w = modelviewprojection.m3 * quad.x + modelviewprojection.m7 * quad.y + modelviewprojection.m11 * quad.z + modelviewprojection.m15 * quad.w; + + // Invert + result.w = 1.0f / result.w; + + //if (quat.w == 0.0) return 0; + + worldPoint.x = quat.x * quat.w; + worldPoint.y = quat.y * quat.w; + worldPoint.z = quat.z * quat.w; + + return worldPoint; + */ +/* + // Needed Vectors + Vector3 normalDeviceCoordinates; + Quaternion rayClip; + Quaternion rayEye; + Vector3 rayWorld; + + // Getting normal device coordinates + float x = (2.0 * mousePosition.x) / GetScreenWidth() - 1.0; + float y = 1.0 - (2.0 * mousePosition.y) / GetScreenHeight(); + float z = 1.0; + normalDeviceCoordinates = (Vector3){ x, y, z }; + + // Getting clip vector + rayClip = (Quaternion){ normalDeviceCoordinates.x, normalDeviceCoordinates.y, -1, 1 }; + + Matrix invProjection = projection; + MatrixInvert(&invProjection); + + rayEye = MatrixQuaternionMultiply(invProjection, rayClip); + rayEye = (Quaternion){ rayEye.x, rayEye.y, -1, 0 }; + + Matrix invModelview = modelview; + MatrixInvert(&invModelview); + + rayWorld = MatrixVector3Multiply(invModelview, (Vector3){rayEye.x, rayEye.y, rayEye.z} ); + VectorNormalize(&rayWorld); + + return rayWorld; +*/ +} + // Convert image data to OpenGL texture (returns OpenGL valid Id) unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps) { @@ -2352,7 +2511,6 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight) return mipmap; } - #endif #if defined(RLGL_STANDALONE) diff --git a/src/rlgl.h b/src/rlgl.h index ff2bcd4e..e277b250 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -192,6 +192,7 @@ void rlglClose(void); // De-init rlgl void rlglDraw(void); // Draw VAO/VBO void rlglDrawPostpro(unsigned int shaderId); // Draw with postpro shader void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) +Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps); // Load in GPU OpenGL texture unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); @@ -200,7 +201,7 @@ unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shade #endif Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids -void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires); +void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -- cgit v1.2.3 From 2b4a1f295a3b790397aec542457a4ea1103ee696 Mon Sep 17 00:00:00 2001 From: nicobwah Date: Mon, 9 Mar 2015 17:13:42 +0100 Subject: Added support for Latin-1 Supplement characters Extended default raylib font system to support Latin-1 Supplement characters --- src/text.c | 117 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/text.c b/src/text.c index 0018363a..86d58b1a 100644 --- a/src/text.c +++ b/src/text.c @@ -80,45 +80,73 @@ extern void UnloadDefaultFont(void); //---------------------------------------------------------------------------------- extern void LoadDefaultFont(void) { - defaultFont.numChars = 96; // We know our default font has 94 chars + // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement + // http://www.utf8-chartable.de/unicode-utf8-table.pl + + defaultFont.numChars = 224; // Number of chars included in our default font Image image; image.width = 128; // We know our default font image is 128 pixels width - image.height = 64; // We know our default font image is 64 pixels height + image.height = 128; // We know our default font image is 128 pixels height // Default font is directly defined here (data generated from a sprite font image) // This way, we reconstruct SpriteFont without creating large global variables // This data is automatically allocated to Stack and automatically deallocated at the end of this function - int defaultFontData[256] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, - 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, - 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, - 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, - 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, - 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, - 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, - 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, - 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, - 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, - 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, - 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x00000000, - 0x24fa28a2, 0x00000901, 0x00000000, 0x00000000, 0x2242252a, 0x00000952, 0x00000000, 0x00000000, 0x2422222a, 0x00000929, 0x00000000, 0x00000000, - 0x2412252a, 0x00000901, 0x00000000, 0x00000000, 0x24fbe8be, 0x00000901, 0x00000000, 0x00000000, 0xac020000, 0x00000f01, 0x00000000, 0x00000000, - 0x0003e000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + int defaultFontData[512] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, + 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, + 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, + 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, + 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, + 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, + 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, + 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, + 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, + 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, + 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, + 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x06000000, 0x24000000, 0x00000901, 0x00000000, 0x09108000, + 0x24fa28a2, 0x00000901, 0x00000000, 0x013e0000, 0x2242252a, 0x00000952, 0x00000000, 0x038a8000, 0x2422222a, 0x00000929, 0x00000000, 0x010a8000, + 0x2412252a, 0x00000901, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000901, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, + 0x0003e000, 0x00000000, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, + 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202, + 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002, + 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002, + 0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010, + 0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7, + 0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a, + 0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b, + 0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210, + 0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe, + 0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2, + 0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000, + 0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, + 0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000, + 0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; int charsHeight = 10; int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically - int charsWidth[96] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, - 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, - 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4 }; + int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, + 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, + 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 5, 5, 7, 1, 5, 3, 7, 3, 5, 4, 1, 7, 4, 3, 5, 3, 3, 2, 5, 6, 1, 2, 2, 3, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 3, 3, 3, 3, 7, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 4, 6, + 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 2, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 5 }; // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- @@ -138,8 +166,12 @@ extern void LoadDefaultFont(void) counter++; - if (counter > 256) counter = 0; // Security check... + if (counter > 512) counter = 0; // Security check... } + + //FILE *myimage = fopen("default_font.raw", "wb"); + //fwrite(image.pixels, 1, 128*128*4, myimage); + //fclose(myimage); defaultFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture UnloadImage(image); @@ -254,7 +286,8 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f int length = strlen(text); int positionX = (int)position.x; float scaleFactor; - + unsigned char letter; + Character c; if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; @@ -265,8 +298,20 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f rlBegin(RL_QUADS); for(int i = 0; i < length; i++) { - c = spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR]; - + if ((unsigned char)text[i] == 0xc2) + { + letter = (unsigned char)text[i + 1]; + c = spriteFont.charSet[letter - FONT_FIRST_CHAR]; + i++; + } + else if ((unsigned char)text[i] == 0xc3) + { + letter = (unsigned char)text[i + 1]; + c = spriteFont.charSet[letter - FONT_FIRST_CHAR + 64]; + i++; + } + else c = spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR]; + rlColor4ub(tint.r, tint.g, tint.b, tint.a); rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer @@ -274,15 +319,15 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f rlVertex2f(positionX, position.y); rlTexCoord2f((float)c.x / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); - rlVertex2f(positionX, position.y + (c.h) * scaleFactor); + rlVertex2f(positionX, position.y + c.h*scaleFactor); rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); - rlVertex2f(positionX + (c.w) * scaleFactor, position.y + (c.h) * scaleFactor); + rlVertex2f(positionX + c.w*scaleFactor, position.y + (c.h) * scaleFactor); rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); - rlVertex2f(positionX + (c.w) * scaleFactor, position.y); + rlVertex2f(positionX + c.w*scaleFactor, position.y); - positionX += ((spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR].w) * scaleFactor + spacing); + positionX += (c.w*scaleFactor + spacing); } rlEnd(); -- cgit v1.2.3 From a632a04a300a60232f6b9a941e73af08d74c6e6a Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 6 Apr 2015 14:02:29 +0200 Subject: Big batch of changes! Check description: Support multiple texture formats (uncompressed and compressed) Postprocessing shaders support Model struct redefined and improved Extended loading functions for Textures and Models Simplified functions: DrawPlane(), DrawQuad() Deleted functions: DrawPlaneEx(), DrawGizmoEx() Now Text module only depends on Textures module Shapes: Reviewed functions to low lines/triangles usage Removed useless tabs and spaces around code --- src/core.c | 153 ++++++++--------- src/models.c | 281 +++++++++---------------------- src/raylib.h | 55 ++++-- src/raymath.h | 16 +- src/rlgl.c | 514 +++++++++++++++++++++++++++++++-------------------------- src/rlgl.h | 63 ++++--- src/shapes.c | 46 +++--- src/text.c | 170 +++++++++---------- src/textures.c | 85 ++++------ 9 files changed, 661 insertions(+), 722 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index a2344395..fbc4838a 100644 --- a/src/core.c +++ b/src/core.c @@ -267,7 +267,6 @@ static bool cameraUseGravity = true; // Shaders variables static bool enabledPostpro = false; -static unsigned int fboShader = 0; //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) @@ -327,7 +326,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition); // Initialize Window and Graphics Context (OpenGL) void InitWindow(int width, int height, const char *title) { - TraceLog(INFO, "Initializing raylib (v1.2.2)"); + TraceLog(INFO, "Initializing raylib (v1.3.0)"); // Store window title (could be useful...) windowTitle = title; @@ -366,7 +365,7 @@ void InitWindow(int width, int height, const char *title) // Android activity initialization void InitWindow(int width, int height, struct android_app *state) { - TraceLog(INFO, "Initializing raylib (v1.2.2)"); + TraceLog(INFO, "Initializing raylib (v1.3.0)"); app_dummy(); @@ -554,9 +553,8 @@ void BeginDrawing(void) void EndDrawing(void) { rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) - - // TODO: Set postprocessing shader to be passed: SetPostproShader()? - if (enabledPostpro) rlglDrawPostpro(fboShader); // Draw postprocessing effect (shader) + + if (enabledPostpro) rlglDrawPostpro(); // Draw postprocessing effect (shader) SwapBuffers(); // Copy back buffer to front buffer PollInputEvents(); // Poll user events @@ -604,7 +602,7 @@ void Begin3dMode(Camera camera) // Setup Camera view if (cameraMode == CAMERA_CUSTOM) currentCamera = camera; else currentCamera = internalCamera; - + Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); rlMultMatrixf(GetMatrixVector(view)); // Multiply MODELVIEW matrix by view matrix (camera) } @@ -707,54 +705,54 @@ void ShowLogo(void) Ray GetMouseRay(Vector2 mousePosition, Camera camera) { Ray ray; - + Matrix proj = MatrixIdentity(); Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); - + float aspect = (GLfloat)GetScreenWidth()/(GLfloat)GetScreenHeight(); double top = 0.1f*tanf(45.0f*PI / 360.0f); double right = top*aspect; - + proj = MatrixFrustum(-right, right, -top, top, 0.01f, 1000.0f); MatrixTranspose(&proj); float realy = (float)GetScreenHeight() - mousePosition.y; - + float z; // glReadPixels(mousePosition.x, mousePosition.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); //http://www.bfilipek.com/2012/06/select-mouse-opengl.html Vector3 nearPoint = { mousePosition.x, realy, 0.0f }; Vector3 farPoint = { mousePosition.x, realy, 1.0f }; - + nearPoint = internalCamera.position; farPoint = rlglUnproject(farPoint, proj, view); - + Vector3 direction = VectorSubtract(farPoint, nearPoint); VectorNormalize(&direction); - - ray.position = nearPoint; + + ray.position = nearPoint; ray.direction = direction; - + // Test Vector2 screenPos; screenPos.x = (mousePosition.x / (float)GetScreenWidth() * 2.0) - 1.0f; screenPos.y = (mousePosition.y / (float)GetScreenHeight() * 2.0) - 1.0f; - + direction = VectorSubtract(internalCamera.target, internalCamera.position); - + printf("/nScreenPos %f, %f", screenPos.x, screenPos.y); - + Matrix rotate; rotate = MatrixIdentity(); rotate = MatrixRotate(0, 45*DEG2RAD*screenPos.y, 0); VectorTransform(&direction, rotate); - + VectorNormalize(&direction); - - ray.position = internalCamera.position; + + ray.position = internalCamera.position; ray.direction = direction; - + return ray; } @@ -790,7 +788,7 @@ void SetCameraMode(int mode) internalCamera.target = (Vector3){ 3, 0, 3}; ProcessCamera(&internalCamera, &internalCamera.position); } - + cameraMode = mode; } @@ -798,7 +796,7 @@ Camera UpdateCamera(Vector3 *position) { // Calculate camera if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position); - + return internalCamera; } @@ -1114,16 +1112,23 @@ Vector2 GetTouchPosition(void) }*/ #endif -void InitPostShader(void) +// Set postprocessing shader +void SetPostproShader(Shader shader) { - rlglInitPostpro(); - - enabledPostpro = true; -} - -void SetPostShader(unsigned int shader) -{ - fboShader = shader; + if (rlGetVersion() == OPENGL_11) TraceLog(WARNING, "Postprocessing shaders not supported on OpenGL 1.1"); + else + { + if (!enabledPostpro) + { + enabledPostpro = true; + rlglInitPostpro(); + rlglSetPostproShader(shader); + } + else + { + rlglSetPostproShader(shader); + } + } } //---------------------------------------------------------------------------------- @@ -1894,7 +1899,7 @@ static void PollInputEvents(void) previousMouseWheelY = currentMouseWheelY; currentMouseWheelY = 0; - + glfwPollEvents(); // Register keyboard/mouse events... and window events! #elif defined(PLATFORM_ANDROID) @@ -2412,7 +2417,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; } - + cameraMousePosition = GetMousePosition(); // Support for multiple automatic camera modes @@ -2422,12 +2427,12 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) { // Pass to orbiting camera if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL; - + // Camera zoom if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) { cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - + if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP; } // Camera looking down @@ -2442,7 +2447,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - + if (camera->target.y < 0) camera->target.y = -0.001; } else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0)) @@ -2462,7 +2467,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - + if (camera->target.y > 0) camera->target.y = 0.001; } else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0)) @@ -2470,8 +2475,8 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; } - - + + // Inputs if (IsKeyDown(KEY_LEFT_ALT)) { @@ -2487,7 +2492,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) // Get the mouse sensitivity cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY; cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY; - + // Angle clamp if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD; else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD; @@ -2500,55 +2505,55 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); } - + // Focus to center if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; - + // Camera position update camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; - + } break; case CAMERA_ORBITAL: { // Pass to free camera if (IsKeyPressed('O')) cameraMode = CAMERA_FREE; - + cameraAngle.x += ORBITAL_CAMERA_SPEED; - + // Camera zoom - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); // Camera distance clamp if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; - + // Focus to center if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; - + // Camera position update camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; - + } break; case CAMERA_FIRST_PERSON: case CAMERA_THIRD_PERSON: { bool isMoving = false; - + // Keyboard inputs if (IsKeyDown('W')) { playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; - + isMoving = true; } else if (IsKeyDown('S')) @@ -2556,25 +2561,25 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; - + isMoving = true; } - + if (IsKeyDown('A')) { playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - + isMoving = true; } else if (IsKeyDown('D')) { playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - + isMoving = true; } - + if (IsKeyDown('E')) { if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER; @@ -2583,32 +2588,32 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) { if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER; } - + if (cameraMode == CAMERA_THIRD_PERSON) { // Camera orientation calculation // Get the mouse sensitivity cameraAngle.x += cameraMouseVariation.x * -THIRD_PERSON_MOUSE_SENSITIVITY; cameraAngle.y += cameraMouseVariation.y * -THIRD_PERSON_MOUSE_SENSITIVITY; - + // Angle clamp if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD; else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD; - + // Camera zoom - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + // Camera distance clamp if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; - + // Camera is always looking at player camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); - + // Camera position update camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; @@ -2617,25 +2622,25 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) else { if (isMoving) cameraMovementCounter++; - + // Camera orientation calculation // Get the mouse sensitivity cameraAngle.x += cameraMouseVariation.x * -FIRST_PERSON_MOUSE_SENSITIVITY; cameraAngle.y += cameraMouseVariation.y * -FIRST_PERSON_MOUSE_SENSITIVITY; - + // Angle clamp if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD; else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD; - + // Camera is always looking at player camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; - + camera->position.x = playerPosition->x; camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; camera->position.z = playerPosition->z; - + camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; } diff --git a/src/models.c b/src/models.c index 771d25a4..f4f83fd8 100644 --- a/src/models.c +++ b/src/models.c @@ -324,7 +324,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); - + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -444,48 +444,17 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl rlPopMatrix(); } -// Draw a quad -void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]) -{ - rlBegin(RL_QUADS); - rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); - rlNormal3f(normals[0].x, normals[0].y, normals[0].z); - rlTexCoord2f(textcoords[0].x, textcoords[0].y); - rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); - - rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); - rlNormal3f(normals[1].x, normals[1].y, normals[1].z); - rlTexCoord2f(textcoords[1].x, textcoords[1].y); - rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); - - rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); - rlNormal3f(normals[2].x, normals[2].y, normals[2].z); - rlTexCoord2f(textcoords[2].x, textcoords[2].y); - rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); - - rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); - rlNormal3f(normals[3].x, normals[3].y, normals[3].z); - rlTexCoord2f(textcoords[3].x, textcoords[3].y); - rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); - rlEnd(); -} - // Draw a plane -void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) +void DrawPlane(Vector3 centerPos, Vector2 size, Color color) { // NOTE: QUADS usage require defining a texture on OpenGL 3.3+ if (rlGetVersion() != OPENGL_11) rlEnableTexture(whiteTexture); // Default white texture - // NOTE: Plane is always created on XZ ground and then rotated + // NOTE: Plane is always created on XZ ground rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(size.x, 1.0f, size.y); - // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... - rlRotatef(rotation.x, 1, 0, 0); - rlRotatef(rotation.y, 0, 1, 0); - rlRotatef(rotation.z, 0, 0, 1); - rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 1.0f, 0.0f); @@ -499,57 +468,27 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) if (rlGetVersion() != OPENGL_11) rlDisableTexture(); } -// Draw a plane with divisions -// TODO: Test this function -void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color) +// Draw a quad +void DrawQuad(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color color) { - float quadWidth = size.x / slicesX; - float quadLenght = size.y / slicesZ; - - float texPieceW = 1 / size.x; - float texPieceH = 1 / size.y; - - // NOTE: Plane is always created on XZ ground and then rotated - rlPushMatrix(); - rlTranslatef(-size.x / 2, 0.0f, -size.y / 2); - rlTranslatef(centerPos.x, centerPos.y, centerPos.z); - - // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... - rlRotatef(rotation.x, 1, 0, 0); - rlRotatef(rotation.y, 0, 1, 0); - rlRotatef(rotation.z, 0, 0, 1); - - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - rlNormal3f(0.0f, 1.0f, 0.0f); - - for (int z = 0; z < slicesZ; z++) - { - for (int x = 0; x < slicesX; x++) - { - // Draw the plane quad by quad (with textcoords) - rlTexCoord2f((float)x * texPieceW, (float)z * texPieceH); - rlVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght); - - rlTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH); - rlVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght); - - rlTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH + texPieceH); - rlVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght + quadLenght); - - rlTexCoord2f((float)x * texPieceW, (float)z * texPieceH + texPieceH); - rlVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght + quadLenght); - } - } - rlEnd(); + // TODO: Calculate normals from vertex position + + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + //rlNormal3f(0.0f, 0.0f, 0.0f); - rlPopMatrix(); + rlVertex3f(v1.x, v1.y, v1.z); + rlVertex3f(v2.x, v2.y, v2.z); + rlVertex3f(v3.x, v3.y, v3.z); + rlVertex3f(v4.x, v4.y, v4.z); + rlEnd(); } +// Draw a ray line void DrawRay(Ray ray, Color color) { float scale = 10000; - + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a); @@ -615,76 +554,7 @@ void DrawGizmo(Vector3 position) rlPopMatrix(); } -void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale) -{ - // NOTE: RGB = XYZ - rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - rlScalef(scale, scale, scale); - rlRotatef(rotation.y, 0, 1, 0); - - rlBegin(RL_LINES); - // X Axis - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x, position.y, position.z); - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1, position.y, position.z); - - // ArrowX - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z + .1); - - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z); - rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z - .1); - - // Y Axis - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y, position.z); - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1, position.z); - - // ArrowY - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + .1, position.y + .9, position.z); - - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z); - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x - .1, position.y + .9, position.z); - - // Z Axis - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z); - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1); - - // ArrowZ - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + .1, position.y, position.z - .9); - - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1); - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9); - - // Extra - int n = 3; - - // X Axis - for (int i=0; i < 360; i += 6) - { - rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n); - rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n); - } - - // Y Axis - for (int i=0; i < 360; i += 6) - { - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n); - rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n); - } - - // Z Axis - for (int i=0; i < 360; i += 6) - { - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0); - rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0); - } - rlEnd(); - rlPopMatrix(); -} - -// Load a 3d model +// Load a 3d model (from file) Model LoadModel(const char *fileName) { VertexData vData; @@ -694,6 +564,7 @@ Model LoadModel(const char *fileName) // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct + // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel() Model model = rlglLoadModel(vData); // Upload vertex data to GPU // Now that vertex data is uploaded to GPU, we can free arrays @@ -708,6 +579,19 @@ Model LoadModel(const char *fileName) return model; } +// Load a 3d model (from vertex data) +Model LoadModelEx(VertexData data) +{ + Model model; + + // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel() + model = rlglLoadModel(data); // Upload vertex data to GPU + + // NOTE: Vertex data is managed externally, must be deallocated manually + + return model; +} + // Load a heightmap image as a 3d model Model LoadHeightmap(Image heightmap, float maxHeight) { @@ -847,12 +731,12 @@ Model LoadCubicmap(Image cubesmap) float w = mapCubeSide; float h = mapCubeSide; - float h2 = mapCubeSide * 1.5; // TODO: Review walls height... + float h2 = mapCubeSide * 1.5; // TODO: Review walls height... Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles * 3 * sizeof(Vector2)); Vector3 *mapNormals = (Vector3 *)malloc(maxTriangles * 3 * sizeof(Vector3)); - + // Define the 6 normals of the cube, we will combine them accordingly later... Vector3 n1 = { 1.0f, 0.0f, 0.0f }; Vector3 n2 = { -1.0f, 0.0f, 0.0f }; @@ -868,14 +752,14 @@ Model LoadCubicmap(Image cubesmap) float width; float height; } RectangleF; - + RectangleF rightTexUV = { 0, 0, 0.5, 0.5 }; RectangleF leftTexUV = { 0.5, 0, 0.5, 0.5 }; RectangleF frontTexUV = { 0, 0, 0.5, 0.5 }; RectangleF backTexUV = { 0.5, 0, 0.5, 0.5 }; RectangleF topTexUV = { 0, 0.5, 0.5, 0.5 }; RectangleF bottomTexUV = { 0.5, 0.5, 0.5, 0.5 }; - + for (int z = 0; z < mapHeight; z += mapCubeSide) { for (int x = 0; x < mapWidth; x += mapCubeSide) @@ -1105,7 +989,7 @@ Model LoadCubicmap(Image cubesmap) mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y }; mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; tcCounter += 6; - + // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) mapVertices[vCounter] = v6; mapVertices[vCounter + 1] = v7; @@ -1208,11 +1092,11 @@ void UnloadModel(Model model) free(model.mesh.normals); } - rlDeleteBuffers(model.vboId[0]); - rlDeleteBuffers(model.vboId[1]); - rlDeleteBuffers(model.vboId[2]); + rlDeleteBuffers(model.mesh.vboId[0]); + rlDeleteBuffers(model.mesh.vboId[1]); + rlDeleteBuffers(model.mesh.vboId[2]); - rlDeleteVertexArrays(model.vaoId); + rlDeleteVertexArrays(model.mesh.vaoId); rlDeleteTextures(model.texture.id); rlDeleteShader(model.shader.id); } @@ -1227,17 +1111,15 @@ void SetModelTexture(Model *model, Texture2D texture) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; - float rotAngle = 0.0f; - Vector3 rotAxis = { 0, 0, 0 }; + Vector3 rotationAxis = { 0, 0, 0 }; - rlglDrawModel(model, position, rotAngle, rotAxis, vScale, tint, false); + DrawModelEx(model, position, 0.0f, rotationAxis, vScale, tint); } // Draw a model with extended parameters void DrawModelEx(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color tint) { // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel() - rlglDrawModel(model, position, rotationAngle, rotationAxis, scale, tint, false); } @@ -1245,10 +1127,9 @@ void DrawModelEx(Model model, Vector3 position, float rotationAngle, Vector3 rot void DrawModelWires(Model model, Vector3 position, float scale, Color color) { Vector3 vScale = { scale, scale, scale }; - float rotAngle = 0.0f; - Vector3 rotAxis = { 0, 0, 0 }; + Vector3 rotationAxis = { 0, 0, 0 }; - rlglDrawModel(model, position, rotAngle, rotAxis, vScale, color, true); + rlglDrawModel(model, position, 0.0f, rotationAxis, vScale, color, true); } // Draw a billboard @@ -1360,7 +1241,7 @@ bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, floa float distance = sqrt(dx*dx + dy*dy + dz*dz); // Distance between centers if (distance <= (radiusA + radiusB)) collision = true; - + return collision; } @@ -1379,9 +1260,9 @@ bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, V bounds = new BoundingBox(minVertex, maxVertex); */ - + bool collision = true; - + if ((maxBBox1.x >= minBBox2.x) && (minBBox1.x <= maxBBox2.x)) { if ((maxBBox1.y < minBBox2.y) || (minBBox1.y > maxBBox2.y)) collision = false; @@ -1395,7 +1276,7 @@ bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, V bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere) { bool collision = false; - + if ((centerSphere.x - minBBox.x > radiusSphere) && (centerSphere.y - minBBox.y > radiusSphere) && (centerSphere.z - minBBox.z > radiusSphere) && (maxBBox.x - centerSphere.x > radiusSphere) && (maxBBox.y - centerSphere.y > radiusSphere) && (maxBBox.z - centerSphere.z > radiusSphere)) { @@ -1435,20 +1316,20 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p { // Detect the cell where the player is located Vector3 impactDirection = { 0, 0, 0 }; - + int locationCellX = 0; int locationCellY = 0; - + locationCellX = floor(playerPosition->x + mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE); locationCellY = floor(playerPosition->z + mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE); - + // Multiple Axis -------------------------------------------------------------------------------------------- - + // Axis x-, y- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); @@ -1456,12 +1337,12 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p impactDirection = (Vector3) { 1, 0, 1}; } } - + // Axis x-, y+ if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); @@ -1469,12 +1350,12 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p impactDirection = (Vector3) { 1, 0, 1}; } } - + // Axis x+, y- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); @@ -1482,12 +1363,12 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p impactDirection = (Vector3) { 1, 0, 1}; } } - + // Axis x+, y+ if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); @@ -1495,9 +1376,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p impactDirection = (Vector3) { 1, 0, 1}; } } - + // Single Axis --------------------------------------------------------------------------------------------------- - + // Axis x- if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) { @@ -1534,89 +1415,89 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p impactDirection = (Vector3) { 0, 0, 1}; } } - + // Diagonals ------------------------------------------------------------------------------------------------------- - + // Axis x-, y- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - + // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) { impactDirection = (Vector3) { 1, 0, 1}; } } } - + // Axis x-, y+ if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - + // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) { impactDirection = (Vector3) { 1, 0, 1}; } } } - + // Axis x+, y- if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - + // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) { impactDirection = (Vector3) { 1, 0, 1}; } } } - + // Axis x+, y+ if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - + // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) { impactDirection = (Vector3) { 1, 0, 1}; } } } - + // Floor collision if (playerPosition->y <= radius) { @@ -1629,7 +1510,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p playerPosition->y = (1.5 - radius) - 0.01; impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z}; } - + return impactDirection; } diff --git a/src/raylib.h b/src/raylib.h index c6c3e32a..0e532ebe 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib 1.2 (www.raylib.com) +* raylib 1.3.0 (www.raylib.com) * * A simple and easy-to-use library to learn videogames programming * @@ -199,6 +199,14 @@ typedef struct Vector3 { float z; } Vector3; +// Matrix type (OpenGL style 4x4 - right handed, column major) +typedef struct Matrix { + float m0, m4, m8, m12; + float m1, m5, m9, m13; + float m2, m6, m10, m14; + float m3, m7, m11, m15; +} Matrix; + // Color type, RGBA (32bit) typedef struct Color { unsigned char r; @@ -258,24 +266,27 @@ typedef struct Camera { typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; // Vertex data definning a mesh +// NOTE: If using OpenGL 1.1, data loaded in CPU; if OpenGL 3.3+ data loaded in GPU (vaoId) typedef struct VertexData { int vertexCount; float *vertices; // 3 components per vertex float *texcoords; // 2 components per vertex float *normals; // 3 components per vertex unsigned char *colors; // 4 components per vertex + unsigned int vaoId; + unsigned int vboId[4]; } VertexData; // Shader type typedef struct Shader { unsigned int id; // Shader program id - + // Variable attributes unsigned int vertexLoc; // Vertex attribute location point (vertex shader) unsigned int texcoordLoc; // Texcoord attribute location point (vertex shader) unsigned int normalLoc; // Normal attribute location point (vertex shader) unsigned int colorLoc; // Color attibute location point (vertex shader) - + // Uniforms unsigned int projectionLoc; // Projection matrix uniform location point (vertex shader) unsigned int modelviewLoc; // ModeView matrix uniform location point (vertex shader) @@ -284,14 +295,11 @@ typedef struct Shader { } Shader; // 3d Model type -// NOTE: If using OpenGL 1.1, loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId) typedef struct Model { VertexData mesh; - unsigned int vaoId; - unsigned int vboId[4]; + Matrix transform; Texture2D texture; Shader shader; - //Matrix transform; } Model; // Ray type (useful for raycast) @@ -315,6 +323,24 @@ typedef struct Wave { short channels; } Wave; +// Texture formats (support depends on OpenGL version) +typedef enum { + UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + UNCOMPRESSED_R5G6B5, // 16 bpp + UNCOMPRESSED_R8G8B8, // 24 bpp + UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + UNCOMPRESSED_R8G8B8A8, // 32 bpp + COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + COMPRESSED_DXT3_RGBA, // 8 bpp + COMPRESSED_DXT5_RGBA, // 8 bpp + COMPRESSED_ETC1_RGB, // 4 bpp + COMPRESSED_ETC2_RGB, // 4 bpp + COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp +} TextureFormat; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -366,8 +392,7 @@ Camera UpdateCamera(Vector3 *position); // Update camera wit void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) -void InitPostShader(void); // Initialize fullscreen postproduction shaders system -void SetPostShader(unsigned int shader); // Set fullscreen postproduction shader +void SetPostproShader(Shader shader); // Set fullscreen postproduction shader Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Gives the rayTrace from mouse position @@ -448,6 +473,7 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load a texture from raw data into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() @@ -490,19 +516,18 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires -void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad -void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane -void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions -void DrawRay(Ray ray, Color color); +void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ +void DrawQuad(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color color); // Draw a quad +void DrawRay(Ray ray, Color color); // Draw a ray line void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) void DrawGizmo(Vector3 position); // Draw simple gizmo -void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); // Draw gizmo with extended parameters //DrawTorus(), DrawTeapot() are useless... //------------------------------------------------------------------------------------ // Model 3d Loading and Drawing Functions (Module: models) //------------------------------------------------------------------------------------ Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) +Model LoadModelEx(VertexData data); // Load a 3d model (from vertex data) //Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) Model LoadHeightmap(Image heightmap, float maxHeight); // Load a heightmap image as a 3d model Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) @@ -517,7 +542,7 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color color); void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec -Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) +Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); diff --git a/src/raymath.h b/src/raymath.h index b3676ed9..133e1881 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -53,15 +53,15 @@ float y; float z; } Vector3; -#endif -// Matrix type (OpenGL style 4x4 - right handed, column major) -typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; -} Matrix; + // Matrix type (OpenGL style 4x4 - right handed, column major) + typedef struct Matrix { + float m0, m4, m8, m12; + float m1, m5, m9, m13; + float m2, m6, m10, m14; + float m3, m7, m11, m15; + } Matrix; +#endif // Quaternion type typedef struct Quaternion { diff --git a/src/rlgl.c b/src/rlgl.c index 7d947d66..c4deda8f 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -63,6 +63,28 @@ #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -174,7 +196,8 @@ static bool useTempBuffer = false; static bool vaoSupported = false; // Framebuffer object and texture -static GLuint fbo, fboColorTexture, fboDepthTexture, fboShader = 0; +static GLuint fbo, fboColorTexture, fboDepthTexture; +static Model postproQuad; #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -199,6 +222,7 @@ static Shader LoadSimpleShader(void); static void InitializeBuffers(void); static void InitializeBuffersGPU(void); static void UpdateBuffers(void); +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); // Custom shader files loading (external) static char *TextFileRead(char *fn); @@ -688,18 +712,21 @@ void rlDeleteTextures(unsigned int id) glDeleteTextures(1, &id); } +// Enable rendering to postprocessing FBO +void rlEnableFBO(void) +{ #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, fbo); +#endif +} + // Unload shader from GPU memory void rlDeleteShader(unsigned int id) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glDeleteProgram(id); -} - -void rlEnableFBO(void) -{ - glBindFramebuffer(GL_FRAMEBUFFER, fbo); -} #endif +} // Unload vertex data (VAO) from GPU memory void rlDeleteVertexArrays(unsigned int id) @@ -840,7 +867,18 @@ void rlglInit(void) TraceLog(INFO, "Supported extension: %s", extensions); #endif */ +/* + GLint numComp = 0; + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); + + GLint format[32] = { 0 }; + glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); + for (int i = 0; i < numComp; i++) + { + TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); + } +*/ #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Set default draw mode currentDrawMode = RL_TRIANGLES; @@ -869,7 +907,7 @@ void rlglInit(void) // Create default white texture for plain colors (required by shader) unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglLoadTexture(pixels, 1, 1, R8G8B8A8, false); + whiteTexture = rlglLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1, false); if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); else TraceLog(WARNING, "Base white texture could not be loaded"); @@ -884,14 +922,14 @@ void rlglInit(void) } drawsCounter = 1; - draws[drawsCounter - 1].textureId = whiteTexture; + draws[drawsCounter - 1].textureId = whiteTexture; #endif } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Init postpro system void rlglInitPostpro(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Create the texture that will serve as the color attachment for the framebuffer glGenTextures(1, &fboColorTexture); glBindTexture(GL_TEXTURE_2D, fboColorTexture); @@ -901,7 +939,7 @@ void rlglInitPostpro(void) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GetScreenWidth(), GetScreenHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); - + // Create the texture that will serve as the depth attachment for the framebuffer. glGenTextures(1, &fboDepthTexture); glBindTexture(GL_TEXTURE_2D, fboDepthTexture); @@ -921,17 +959,48 @@ void rlglInitPostpro(void) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fboDepthTexture, 0); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - + if (status != GL_FRAMEBUFFER_COMPLETE) TraceLog(WARNING, "Framebuffer object could not be created..."); else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", fbo); glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Create a simple quad model to render fbo texture + VertexData quadData; + + quadData.vertexCount = 6; + + float w = GetScreenWidth(); + float h = GetScreenHeight(); + + float quadPositions[6*3] = { w, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, h, 0.0, 0, h, 0.0, w, h, 0.0, w, 0.0, 0.0 }; + float quadTexcoords[6*2] = { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0 }; + float quadNormals[6*3] = { 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 }; + unsigned char quadColors[6*4] = { 255 }; - fboShader = 0; + quadData.vertices = quadPositions; + quadData.texcoords = quadTexcoords; + quadData.normals = quadNormals; + quadData.colors = quadColors; - // TODO: Init simple quad VAO and data here? + postproQuad = rlglLoadModel(quadData); + + Texture2D texture; + texture.id = fboColorTexture; + texture.width = w; + texture.height = h; + + SetModelTexture(&postproQuad, texture); +#endif } + +// Set postprocessing shader +void rlglSetPostproShader(Shader shader) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + SetModelShader(&postproQuad, shader); #endif +} // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) @@ -987,8 +1056,13 @@ void rlglClose(void) // Free GPU texture glDeleteTextures(1, &whiteTexture); - - if (fbo != 0) glDeleteFramebuffers(1, &fbo); + + if (fbo != 0) + { + glDeleteFramebuffers(1, &fbo); + + UnloadModel(postproQuad); + } free(draws); #endif @@ -1002,7 +1076,7 @@ void rlglDraw(void) if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { glUseProgram(defaultShader.id); - + glUniformMatrix4fv(defaultShader.projectionLoc, 1, false, GetMatrixVector(projection)); glUniformMatrix4fv(defaultShader.modelviewLoc, 1, false, GetMatrixVector(modelview)); glUniform1i(defaultShader.textureLoc, 0); @@ -1142,68 +1216,15 @@ void rlglDraw(void) #endif } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void rlglDrawPostpro(unsigned int shaderId) +// Draw with postprocessing shader +void rlglDrawPostpro(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // TODO: Draw screen quad with texture -/* - const float quadPositions[] = { 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0, - -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0 }; - const float quadTexcoords[] = { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0 }; - - glBindBuffer(GL_ARRAY_BUFFER, quadVbo); - - glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), quadPositions); - glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), quadTexcoords); - - glEnableVertexAttribArray(ATTRIB_VERTEX); - glEnableVertexAttribArray(ATTRIB_TEXCOORD0); - glBindTexture(GL_TEXTURE_2D, fboColorTexture); - - glDrawArrays(GL_TRIANGLES, 0, 2*3); - - // Quad render using triangle strip - glBindBuffer(GL_ARRAY_BUFFER, uiVBO[1]); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - glUseProgram(0); -*/ - rlEnableTexture(fboColorTexture); - - rlPushMatrix(); - rlBegin(RL_QUADS); - rlColor4ub(255, 255, 255, 255); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer - - // Bottom-left corner for texture and quad - rlTexCoord2f(0.0f, 1.0f); - rlVertex2f(0.0f, 0.0f); - - // Bottom-right corner for texture and quad - rlTexCoord2f(0.0f, 0.0f); - rlVertex2f(0.0f, GetScreenHeight()); - - // Top-right corner for texture and quad - rlTexCoord2f(1.0f, 0.0f); - rlVertex2f(GetScreenWidth(), GetScreenHeight()); - - // Top-left corner for texture and quad - rlTexCoord2f(1.0f, 1.0f); - rlVertex2f(GetScreenWidth(), 0.0f); - rlEnd(); - rlPopMatrix(); - - fboShader = shaderId; - - rlglDraw(); -} + rlglDrawModel(postproQuad, (Vector3){0,0,0}, 0.0f, (Vector3){0,0,0}, (Vector3){1.0f, 1.0f, 1.0f}, WHITE, false); #endif +} // Draw a 3d model void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires) @@ -1250,12 +1271,14 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(model.shader.id); - Vector3 rotation = { 0.0f, 0.0f, 0.0f }; + // TODO: Use model.transform matrix + + Vector3 rotation = { 0.0f, 0.0f, 0.0f }; // Get transform matrix (rotation -> scale -> translation) Matrix transform = MatrixTransform(position, rotation, scale); // Object-space transformation Matrix modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation - + // Projection: Screen-space transformation // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader @@ -1271,21 +1294,21 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r if (vaoSupported) { - glBindVertexArray(model.vaoId); + glBindVertexArray(model.mesh.vaoId); } else { // Bind model VBOs data - glBindBuffer(GL_ARRAY_BUFFER, model.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[0]); glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(model.shader.vertexLoc); - glBindBuffer(GL_ARRAY_BUFFER, model.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[1]); glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(model.shader.texcoordLoc); // Add normals support - glBindBuffer(GL_ARRAY_BUFFER, model.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[2]); glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(model.shader.normalLoc); } @@ -1357,10 +1380,10 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // Get world coordinates from screen coordinates Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) -{ +{ //GLint viewport[4]; //glGetIntegerv(GL_VIEWPORT, viewport); - + // Viewport data int x = 0; int y = 0; @@ -1368,7 +1391,7 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) int height = GetScreenHeight(); float minDepth = 0.0f; float maxDepth = 1.0f; -/* +/* Matrix modelviewprojection = MatrixMultiply(modelview, projection); MatrixInvert(&modelviewprojection); @@ -1384,7 +1407,7 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) //if (!MathUtil.IsOne(a)) vector = (vector / a); //VectorScale(&vector, 1/a); - + return vector; */ /* @@ -1418,31 +1441,31 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) /* Quaternion quat; Vector3 vec; - + quat.x = 2.0f * GetMousePosition().x / (float)width - 1; quat.y = -(2.0f * GetMousePosition().y / (float)height - 1); quat.z = 0; quat.w = 1; - - Matrix invView; + + Matrix invView; MatrixInvert(&view); Matrix invProj; MatrixInvert(&proj); - + quat.x = invProj.m0 * quat.x + invProj.m4 * quat.y + invProj.m8 * quat.z + invProj.m12 * quat.w; quat.y = invProj.m1 * quat.x + invProj.m5 * quat.y + invProj.m9 * quat.z + invProj.m13 * quat.w; quat.z = invProj.m2 * quat.x + invProj.m6 * quat.y + invProj.m10 * quat.z + invProj.m14 * quat.w; quat.w = invProj.m3 * quat.x + invProj.m7 * quat.y + invProj.m11 * quat.z + invProj.m15 * quat.w; - + quat.x = invView.m0 * quat.x + invView.m4 * quat.y + invView.m8 * quat.z + invView.m12 * quat.w; quat.y = invView.m1 * quat.x + invView.m5 * quat.y + invView.m9 * quat.z + invView.m13 * quat.w; quat.z = invView.m2 * quat.x + invView.m6 * quat.y + invView.m10 * quat.z + invView.m14 * quat.w; quat.w = invView.m3 * quat.x + invView.m7 * quat.y + invView.m11 * quat.z + invView.m15 * quat.w; - + vec.x /= quat.w; vec.y /= quat.w; vec.z /= quat.w; - + return vec; */ /* @@ -1463,14 +1486,14 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) quat.y = ((source.y - (float)y)/(float)height*2.0) - 1.0f; quat.z = 2.0*source.z - 1.0; quat.w = 1.0; - + // Traspose quaternion and multiply Quaternion result; result.x = modelviewprojection.m0 * quad.x + modelviewprojection.m4 * quad.y + modelviewprojection.m8 * quad.z + modelviewprojection.m12 * quad.w; result.y = modelviewprojection.m1 * quad.x + modelviewprojection.m5 * quad.y + modelviewprojection.m9 * quad.z + modelviewprojection.m13 * quad.w; result.z = modelviewprojection.m2 * quad.x + modelviewprojection.m6 * quad.y + modelviewprojection.m10 * quad.z + modelviewprojection.m14 * quad.w; result.w = modelviewprojection.m3 * quad.x + modelviewprojection.m7 * quad.y + modelviewprojection.m11 * quad.z + modelviewprojection.m15 * quad.w; - + // Invert result.w = 1.0f / result.w; @@ -1488,47 +1511,65 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) Quaternion rayClip; Quaternion rayEye; Vector3 rayWorld; - + // Getting normal device coordinates float x = (2.0 * mousePosition.x) / GetScreenWidth() - 1.0; float y = 1.0 - (2.0 * mousePosition.y) / GetScreenHeight(); float z = 1.0; normalDeviceCoordinates = (Vector3){ x, y, z }; - + // Getting clip vector rayClip = (Quaternion){ normalDeviceCoordinates.x, normalDeviceCoordinates.y, -1, 1 }; - + Matrix invProjection = projection; MatrixInvert(&invProjection); - + rayEye = MatrixQuaternionMultiply(invProjection, rayClip); rayEye = (Quaternion){ rayEye.x, rayEye.y, -1, 0 }; - + Matrix invModelview = modelview; MatrixInvert(&invModelview); - + rayWorld = MatrixVector3Multiply(invModelview, (Vector3){rayEye.x, rayEye.y, rayEye.z} ); VectorNormalize(&rayWorld); - + return rayWorld; */ + return (Vector3){ 0, 0, 0 }; } // Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps) +unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps) { - glBindTexture(GL_TEXTURE_2D,0); // Free any old binding + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding GLuint id; - glGenTextures(1, &id); // Generate Pointer to the texture + + // TODO: Review compressed textures support by OpenGL version + /* (rlGetVersion() == OPENGL_11) + if ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA) || + (textureFormat == COMPRESSED_ETC1_RGB8) || (textureFormat == COMPRESSED_ETC2_RGB8) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA8)) + { + id = 0; + TraceLog(WARNING, "GPU compressed textures not supported on OpenGL 1.1"); + return id; + } + */ + + glGenTextures(1, &id); // Generate Pointer to the texture //glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, id); // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used! +#if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repead on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repead on y-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif bool texIsPOT = false; @@ -1542,6 +1583,8 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int col genMipmaps = false; } + // TODO: Support mipmaps --> if (mipmapCount > 1) + // If mipmaps are being used, we configure mag-min filters accordingly // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so only GL_LINEAR or GL_NEAREST can be used if (genMipmaps) @@ -1587,35 +1630,76 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int col else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); #endif -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) // NOTE: On embedded systems, we let the driver choose the best internal format - //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL - //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // WebGL - - // TODO: Add support for multiple color modes (16bit color modes and grayscale) - // Ref: https://www.khronos.org/opengles/sdk/docs/man3/html/glTexImage2D.xhtml - // On WebGL, internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - // (sized)internalFormat format type + + // Support for multiple color modes (16bit color modes and grayscale) + // (sized)internalFormat format type // GL_R GL_RED GL_UNSIGNED_BYTE // GL_RGB565 GL_RGB GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5 - // GL_RGB5_A1 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_5_5_1 + // GL_RGB5_A1 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_5_5_1 // GL_RGBA4 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4 - // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE - // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE + // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE + // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE - switch (colorMode) + switch (textureFormat) { - case GRAYSCALE: glTexImage2D(GL_TEXTURE_2D, 0, GL_R, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data); break; - case R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); break; - case R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); break; - case R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data); break; - case R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); break; - case R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; - default: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; + case UNCOMPRESSED_GRAYSCALE: + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); + + // With swizzleMask we define how a one channel texture will be mapped to RGBA + // Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, 1.0f }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + + TraceLog(INFO, "Grayscale texture loaded and swizzled!"); + } break; + case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; + case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; + case COMPRESSED_ETC1_RGB: TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break;//TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break;//TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + //case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + default: TraceLog(WARNING, "Texture format not recognized"); break; } - if (genMipmaps) + if ((mipmapCount == 1) && (genMipmaps)) + { + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); + } +#elif defined(GRAPHICS_API_OPENGL_ES2) + + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + switch (textureFormat) + { + case UNCOMPRESSED_GRAYSCALE: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_ETC1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; + case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + //case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + default: TraceLog(WARNING, "Texture format not recognized"); break; + } + + if ((mipmapCount == 1) && (genMipmaps)) { glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); @@ -1638,13 +1722,15 @@ Model rlglLoadModel(VertexData mesh) Model model; model.mesh = mesh; + model.transform = MatrixIdentity(); #if defined(GRAPHICS_API_OPENGL_11) + model.mesh.vaoId = 0; // Vertex Array Object + model.mesh.vboId[0] = 0; // Vertex position VBO + model.mesh.vboId[1] = 0; // Texcoords VBO + model.mesh.vboId[2] = 0; // Normals VBO model.texture.id = 0; // No texture required - model.vaoId = 0; // Vertex Array Object - model.vboId[0] = 0; // Vertex position VBO - model.vboId[1] = 0; // Texcoords VBO - model.vboId[2] = 0; // Normals VBO + model.shader.id = 0; // No shader used #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model.texture.id = whiteTexture; // Default whiteTexture @@ -1681,109 +1767,31 @@ Model rlglLoadModel(VertexData mesh) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW); glEnableVertexAttribArray(simpleShader.normalLoc); glVertexAttribPointer(simpleShader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - + model.shader = simpleShader; // By default, simple shader will be used - model.vboId[0] = vertexBuffer[0]; // Vertex position VBO - model.vboId[1] = vertexBuffer[1]; // Texcoords VBO - model.vboId[2] = vertexBuffer[2]; // Normals VBO + model.mesh.vboId[0] = vertexBuffer[0]; // Vertex position VBO + model.mesh.vboId[1] = vertexBuffer[1]; // Texcoords VBO + model.mesh.vboId[2] = vertexBuffer[2]; // Normals VBO if (vaoSupported) { if (vaoModel > 0) { - model.vaoId = vaoModel; + model.mesh.vaoId = vaoModel; TraceLog(INFO, "[VAO ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel); } else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)"); } else { - TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Model uploaded successfully to VRAM (GPU)", model.vboId[0], model.vboId[1], model.vboId[2]); + TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Model uploaded successfully to VRAM (GPU)", model.mesh.vboId[0], model.mesh.vboId[1], model.mesh.vboId[2]); } #endif return model; } -// Convert image data to OpenGL texture (returns OpenGL valid Id) -// NOTE: Expected compressed image data and POT image -unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compFormat) -{ - GLuint id; - -#if defined(GRAPHICS_API_OPENGL_11) - id = 0; - TraceLog(WARNING, "GPU compressed textures not supported on OpenGL 1.1"); - -#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - TraceLog(DEBUG, "Compressed texture width: %i", width); - TraceLog(DEBUG, "Compressed texture height: %i", height); - TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount); - TraceLog(DEBUG, "Compressed texture format: 0x%x", compFormat); - - if (compFormat == 0) - { - id = 0; - TraceLog(WARNING, "Texture compressed format not recognized", id); - } - else - { - glGenTextures(1, &id); - - // Bind the texture - glBindTexture(GL_TEXTURE_2D, id); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - // Set texture parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // If mipmaps are being used, we configure mag-min filters accordingly - if (mipmapCount > 1) - { - // Trilinear filtering with mipmaps - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available) - } - else - { - // Not using mipmappings - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - } - - int blockSize = 0; - int offset = 0; - - if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8; - else blockSize = 16; - - // Load the mipmaps - for (int level = 0; level < mipmapCount && (width || height); level++) - { - unsigned int size = 0; - - // NOTE: size specifies the number of bytes of image data (S3TC/DXTC) - if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) size = ((width + 3)/4)*((height + 3)/4)*blockSize; // S3TC/DXTC -#if defined(GRAPHICS_API_OPENGL_ES2) - else if (compFormat == GL_ETC1_RGB8_OES) size = 8*((width + 3) >> 2)*((height + 3) >> 2); // ETC1 -#endif - glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); - - offset += size; - width /= 2; - height /= 2; - - // Security check for NPOT textures - if (width < 1) width = 1; - if (height < 1) height = 1; - } - } -#endif - - return id; -} #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load a shader (vertex shader + fragment shader) from text data @@ -1801,7 +1809,7 @@ unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) glShaderSource(vertexShader, 1, &pvs, NULL); glShaderSource(fragmentShader, 1, &pfs, NULL); - + GLint success = 0; glCompileShader(vertexShader); @@ -1869,9 +1877,9 @@ unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) glGetProgramInfoLog(program, maxLength, &length, log); TraceLog(INFO, "%s", log); - + glDeleteProgram(program); - + program = 0; } else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); @@ -1907,12 +1915,12 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load a shader (vertex shader + fragment shader) from text data Shader LoadShader(char *vsFileName, char *fsFileName) { Shader shader; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Shaders loading from external text file char *vShaderStr = TextFileRead(vsFileName); char *fShaderStr = TextFileRead(fsFileName); @@ -1932,7 +1940,7 @@ Shader LoadShader(char *vsFileName, char *fsFileName) shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); // NOTE: custom shader does not use colorLoc - + // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); @@ -1941,35 +1949,39 @@ Shader LoadShader(char *vsFileName, char *fsFileName) shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); shader.tintColorLoc = glGetUniformLocation(shader.id, "tintColor"); //-------------------------------------------------------------------- - +#endif + return shader; } // Link shader to model void SetModelShader(Model *model, Shader shader) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model->shader = shader; - - if (vaoSupported) glBindVertexArray(model->vaoId); + + if (vaoSupported) glBindVertexArray(model->mesh.vaoId); // Enable vertex attributes: position - glBindBuffer(GL_ARRAY_BUFFER, model->vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[0]); glEnableVertexAttribArray(shader.vertexLoc); glVertexAttribPointer(shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: texcoords - glBindBuffer(GL_ARRAY_BUFFER, model->vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[1]); glEnableVertexAttribArray(shader.texcoordLoc); glVertexAttribPointer(shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); // Enable vertex attributes: normals - glBindBuffer(GL_ARRAY_BUFFER, model->vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[2]); glEnableVertexAttribArray(shader.normalLoc); glVertexAttribPointer(shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); if (vaoSupported) glBindVertexArray(0); // Unbind VAO +#endif } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix() { PrintMatrix(projection); @@ -1979,7 +1991,6 @@ void PrintModelviewMatrix() { PrintMatrix(modelview); } - #endif //---------------------------------------------------------------------------------- @@ -1987,20 +1998,55 @@ void PrintModelviewMatrix() //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Convert image data to OpenGL texture (returns OpenGL valid Id) +// NOTE: Expected compressed image data and POT image +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat) +{ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + int blockSize = 0; // Bytes every block + int offset = 0; + + if ((compressedFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT) || + (compressedFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) || +#if defined(GRAPHICS_API_OPENGL_ES2) + (compressedFormat == GL_ETC1_RGB8_OES) || +#endif + (compressedFormat == GL_COMPRESSED_RGB8_ETC2)) blockSize = 8; + else blockSize = 16; + + // Load the mipmap levels + for (int level = 0; level < mipmapCount && (width || height); level++) + { + unsigned int size = 0; + + size = ((width + 3)/4)*((height + 3)/4)*blockSize; + + glCompressedTexImage2D(GL_TEXTURE_2D, level, compressedFormat, width, height, 0, size, data + offset); + + offset += size; + width /= 2; + height /= 2; + + // Security check for NPOT textures + if (width < 1) width = 1; + if (height < 1) height = 1; + } +} // Load Shader (Vertex and Fragment) // NOTE: This shader program is used only for batch buffers (lines, triangles, quads) static Shader LoadDefaultShader(void) { Shader shader; - + // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ // Just defined #version 330 despite shader is #version 110 // Vertex shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char vShaderStr[] = " #version 110 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! #endif @@ -2020,7 +2066,7 @@ static Shader LoadDefaultShader(void) // Fragment shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char fShaderStr[] = " #version 110 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! "precision mediump float; \n" // WebGL, required for emscripten @@ -2037,14 +2083,14 @@ static Shader LoadDefaultShader(void) if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); - + // Get handles to GLSL input attibute locations //------------------------------------------------------------------- shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.colorLoc = glGetAttribLocation(shader.id, "vertexColor"); // NOTE: default shader does not use normalLoc - + // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); @@ -2061,7 +2107,7 @@ static Shader LoadDefaultShader(void) static Shader LoadSimpleShader(void) { Shader shader; - + // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2 // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+ // Just defined #version 330 despite shader is #version 110 @@ -2103,14 +2149,14 @@ static Shader LoadSimpleShader(void) if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id); - + // Get handles to GLSL input attibute locations //------------------------------------------------------------------- shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition"); shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); // NOTE: simple shader does not use colorLoc - + // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); diff --git a/src/rlgl.h b/src/rlgl.h index e277b250..c1d71cc9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -88,35 +88,47 @@ typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; -typedef enum { GRAYSCALE = 0, R5G6B5, R8G8B8, R5G5B5A1, R4G4B4A4, R8G8B8A8 } ColorMode; - #ifdef RLGL_STANDALONE + // Texture formats (support depends on OpenGL version) + typedef enum { + UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + UNCOMPRESSED_R5G6B5, // 16 bpp + UNCOMPRESSED_R8G8B8, // 24 bpp + UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + UNCOMPRESSED_R8G8B8A8, // 32 bpp + COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + COMPRESSED_DXT3_RGBA, // 8 bpp + COMPRESSED_DXT5_RGBA, // 8 bpp + COMPRESSED_ETC1_RGB, // 4 bpp + COMPRESSED_ETC2_RGB, // 4 bpp + COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp + } TextureFormat; + // VertexData type + // NOTE: If using OpenGL 1.1, data loaded in CPU; if OpenGL 3.3+ data loaded in GPU (vaoId) typedef struct VertexData { int vertexCount; float *vertices; // 3 components per vertex float *texcoords; // 2 components per vertex float *normals; // 3 components per vertex unsigned char *colors; + unsigned int vaoId; + unsigned int vboId[4]; } VertexData; - - // Texture2D type - typedef struct Texture2D { - unsigned int id; // Texture id - int width; - int height; - } Texture2D; // Shader type typedef struct Shader { unsigned int id; // Shader program id - + // Variable attributes unsigned int vertexLoc; // Vertex attribute location point (vertex shader) unsigned int texcoordLoc; // Texcoord attribute location point (vertex shader) unsigned int normalLoc; // Normal attribute location point (vertex shader) unsigned int colorLoc; // Color attibute location point (vertex shader) - + // Uniforms unsigned int projectionLoc; // Projection matrix uniform location point (vertex shader) unsigned int modelviewLoc; // ModeView matrix uniform location point (vertex shader) @@ -125,15 +137,19 @@ typedef enum { GRAYSCALE = 0, R5G6B5, R8G8B8, R5G5B5A1, R4G4B4A4, R8G8B8A8 } Col } Shader; // 3d Model type - // NOTE: If using OpenGL 1.1, loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId) typedef struct Model { VertexData mesh; - unsigned int vaoId; - unsigned int vboId[4]; + Matrix transform; Texture2D texture; Shader shader; - //Matrix transform; } Model; + + // Texture2D type + typedef struct Texture2D { + unsigned int id; // Texture id + int width; + int height; + } Texture2D; #endif #ifdef __cplusplus @@ -181,28 +197,27 @@ void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) int rlGetVersion(void); // Returns current OpenGL version -void rlEnableFBO(void); +void rlEnableFBO(void); // Enable rendering to postprocessing FBO //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ void rlglInit(void); // Initialize rlgl (shaders, VAO, VBO...) -void rlglInitPostpro(void); // Initialize postprocessing system void rlglClose(void); // De-init rlgl void rlglDraw(void); // Draw VAO/VBO -void rlglDrawPostpro(unsigned int shaderId); // Draw with postpro shader void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) -Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates -unsigned int rlglLoadTexture(unsigned char *data, int width, int height, int colorMode, bool genMipmaps); // Load in GPU OpenGL texture -unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format); -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data -#endif +unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load in GPU OpenGL texture +unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data +void rlglInitPostpro(void); // Initialize postprocessing system +void rlglDrawPostpro(void); // Draw with postprocessing shader +void rlglSetPostproShader(Shader shader); // Set postprocessing shader Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); +Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates + byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) diff --git a/src/shapes.c b/src/shapes.c index d872eacf..d88845cc 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -98,7 +98,7 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) // Draw a color-filled circle void DrawCircle(int centerX, int centerY, float radius, Color color) { - DrawPoly((Vector2){centerX, centerY}, 360, radius, 0, color); + DrawPoly((Vector2){ centerX, centerY }, 36, radius, 0, color); } // Draw a gradient-filled circle @@ -106,14 +106,14 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) { rlBegin(RL_TRIANGLES); - for (int i=0; i < 360; i += 2) + for (int i = 0; i < 360; i += 10) { rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(centerX, centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius); + rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f(centerX + sin(DEG2RAD*(i+2)) * radius, centerY + cos(DEG2RAD*(i+2)) * radius); + rlVertex2f(centerX + sin(DEG2RAD*(i + 10)) * radius, centerY + cos(DEG2RAD*(i + 10))*radius); } rlEnd(); } @@ -122,12 +122,12 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co void DrawCircleV(Vector2 center, float radius, Color color) { rlBegin(RL_TRIANGLES); - for (int i=0; i < 360; i += 2) + for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2i(center.x, center.y); - rlVertex2f(center.x + sin(DEG2RAD*i) * radius, center.y + cos(DEG2RAD*i) * radius); - rlVertex2f(center.x + sin(DEG2RAD*(i+2)) * radius, center.y + cos(DEG2RAD*(i+2)) * radius); + rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius); + rlVertex2f(center.x + sin(DEG2RAD*(i + 10)) * radius, center.y + cos(DEG2RAD*(i + 10)) * radius); } rlEnd(); } @@ -139,10 +139,10 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) rlColor4ub(color.r, color.g, color.b, color.a); // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) - for (int i=0; i < 360; i++) + for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius); - rlVertex2f(centerX + sin(DEG2RAD*(i+1)) * radius, centerY + cos(DEG2RAD*(i+1)) * radius); + rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius); + rlVertex2f(centerX + sin(DEG2RAD*(i + 10)) * radius, centerY + cos(DEG2RAD*(i + 10))*radius); } rlEnd(); } @@ -201,7 +201,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color) rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlNormal3f(0.0f, 0.0f, 1.0f); rlTexCoord2f(0.0f, 0.0f); rlVertex2f(position.x, position.y); @@ -275,13 +275,13 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlRotatef(rotation, 0, 0, 1); rlBegin(RL_TRIANGLES); - for (int i=0; i < 360; i += 360/sides) + for (int i = 0; i < 360; i += 360/sides) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2i(0, 0); - rlVertex2f(sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius); - rlVertex2f(sin(DEG2RAD*(i+360/sides)) * radius, cos(DEG2RAD*(i+360/sides)) * radius); + rlVertex2f(sin(DEG2RAD*i)*radius, cos(DEG2RAD*i)*radius); + rlVertex2f(sin(DEG2RAD*(i + 360/sides))*radius, cos(DEG2RAD*(i + 360/sides))*radius); } rlEnd(); rlPopMatrix(); @@ -299,8 +299,8 @@ void DrawPolyEx(Vector2 *points, int numPoints, Color color) for (int i = 0; i < numPoints - 2; i++) { rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i+1].x, points[i+1].y); - rlVertex2f(points[i+2].x, points[i+2].y); + rlVertex2f(points[i + 1].x, points[i + 1].y); + rlVertex2f(points[i + 2].x, points[i + 2].y); } rlEnd(); } @@ -318,7 +318,7 @@ void DrawPolyExLines(Vector2 *points, int numPoints, Color color) for (int i = 0; i < numPoints - 1; i++) { rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i+1].x, points[i+1].y); + rlVertex2f(points[i + 1].x, points[i + 1].y); } rlEnd(); } @@ -367,10 +367,10 @@ bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) { bool collision = false; - int dx = abs((rec1.x + rec1.width / 2) - (rec2.x + rec2.width / 2)); - int dy = abs((rec1.y + rec1.height / 2) - (rec2.y + rec2.height / 2)); + int dx = abs((rec1.x + rec1.width/2) - (rec2.x + rec2.width/2)); + int dy = abs((rec1.y + rec1.height/2) - (rec2.y + rec2.height/2)); - if ((dx <= (rec1.width / 2 + rec2.width / 2)) && ((dy <= (rec1.height / 2 + rec2.height / 2)))) collision = true; + if ((dx <= (rec1.width/2 + rec2.width/2)) && ((dy <= (rec1.height/2 + rec2.height/2)))) collision = true; return collision; } @@ -395,10 +395,10 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) { bool collision = false; - float dx = abs((rec.x + rec.width / 2) - center.x); - float dy = abs((rec.y + rec.height / 2) - center.y); + float dx = abs((rec.x + rec.width/2) - center.x); + float dy = abs((rec.y + rec.height/2) - center.y); - if ((dx <= (rec.width / 2 + radius)) && (dy <= (rec.height / 2 + radius))) collision = true; + if ((dx <= (rec.width/2 + radius)) && (dy <= (rec.height/2 + radius))) collision = true; return collision; } diff --git a/src/text.c b/src/text.c index 86d58b1a..7b3fc7d2 100644 --- a/src/text.c +++ b/src/text.c @@ -30,10 +30,9 @@ #include // Used for functions with variable number of parameters (FormatText()) #include // Standard input / output lib -#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#include "utils.h" // Required for function GetExtendion() +#include "utils.h" // Required for function GetExtension() -// Following libs will be used on LoadTTF() +// Following libs are used on LoadTTF() #define STB_TRUETYPE_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION #include "stb_rect_pack.h" @@ -44,7 +43,7 @@ //---------------------------------------------------------------------------------- #define FONT_FIRST_CHAR 32 #define MAX_FONTCHARS 128 -#define MAX_FORMATTEXT_LENGTH 50 +#define MAX_FORMATTEXT_LENGTH 64 #define BIT_CHECK(a,b) ((a) & (1<<(b))) @@ -82,7 +81,7 @@ extern void LoadDefaultFont(void) { // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // http://www.utf8-chartable.de/unicode-utf8-table.pl - + defaultFont.numChars = 224; // Number of chars included in our default font Image image; @@ -93,60 +92,60 @@ extern void LoadDefaultFont(void) // This way, we reconstruct SpriteFont without creating large global variables // This data is automatically allocated to Stack and automatically deallocated at the end of this function int defaultFontData[512] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, - 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, - 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, - 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, - 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, - 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, - 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, - 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, - 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, - 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, - 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, - 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x06000000, 0x24000000, 0x00000901, 0x00000000, 0x09108000, - 0x24fa28a2, 0x00000901, 0x00000000, 0x013e0000, 0x2242252a, 0x00000952, 0x00000000, 0x038a8000, 0x2422222a, 0x00000929, 0x00000000, 0x010a8000, - 0x2412252a, 0x00000901, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000901, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, - 0x0003e000, 0x00000000, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, - 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202, - 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002, - 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002, - 0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010, - 0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7, - 0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a, - 0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b, - 0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210, - 0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe, - 0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2, - 0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000, - 0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, - 0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000, - 0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, + 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, + 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, + 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, + 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, + 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, + 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, + 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, + 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, + 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, + 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, + 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x06000000, 0x24000000, 0x00000901, 0x00000000, 0x09108000, + 0x24fa28a2, 0x00000901, 0x00000000, 0x013e0000, 0x2242252a, 0x00000952, 0x00000000, 0x038a8000, 0x2422222a, 0x00000929, 0x00000000, 0x010a8000, + 0x2412252a, 0x00000901, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000901, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, + 0x0003e000, 0x00000000, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, + 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202, + 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002, + 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002, + 0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010, + 0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7, + 0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a, + 0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b, + 0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210, + 0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe, + 0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2, + 0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000, + 0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, + 0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000, + 0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; int charsHeight = 10; int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, - 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, - 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 5, 5, 5, 7, 1, 5, 3, 7, 3, 5, 4, 1, 7, 4, 3, 5, 3, 3, 2, 5, 6, 1, 2, 2, 3, 5, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 3, 3, 3, 3, 7, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 4, 6, - 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 2, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 5 }; + 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, + 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 5, 5, 7, 1, 5, 3, 7, 3, 5, 4, 1, 7, 4, 3, 5, 3, 3, 2, 5, 6, 1, 2, 2, 3, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 3, 3, 3, 3, 7, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 4, 6, + 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 2, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 5 }; // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- @@ -168,7 +167,7 @@ extern void LoadDefaultFont(void) if (counter > 512) counter = 0; // Security check... } - + //FILE *myimage = fopen("default_font.raw", "wb"); //fwrite(image.pixels, 1, 128*128*4, myimage); //fclose(myimage); @@ -211,7 +210,7 @@ extern void LoadDefaultFont(void) extern void UnloadDefaultFont(void) { - rlDeleteTextures(defaultFont.texture.id); + UnloadTexture(defaultFont.texture); free(defaultFont.charSet); } @@ -258,7 +257,7 @@ SpriteFont LoadSpriteFont(const char *fileName) // Unload SpriteFont from GPU memory void UnloadSpriteFont(SpriteFont spriteFont) { - rlDeleteTextures(spriteFont.texture.id); + UnloadTexture(spriteFont.texture); free(spriteFont.charSet); } @@ -284,54 +283,35 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color) void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int fontSize, int spacing, Color tint) { int length = strlen(text); - int positionX = (int)position.x; + int offsetX = 0; float scaleFactor; unsigned char letter; - + Character c; if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; - rlEnableTexture(spriteFont.texture.id); - - rlBegin(RL_QUADS); - for(int i = 0; i < length; i++) + for(int i = 0; i < length; i++) + { + if ((unsigned char)text[i] == 0xc2) { - if ((unsigned char)text[i] == 0xc2) - { - letter = (unsigned char)text[i + 1]; - c = spriteFont.charSet[letter - FONT_FIRST_CHAR]; - i++; - } - else if ((unsigned char)text[i] == 0xc3) - { - letter = (unsigned char)text[i + 1]; - c = spriteFont.charSet[letter - FONT_FIRST_CHAR + 64]; - i++; - } - else c = spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR]; - - rlColor4ub(tint.r, tint.g, tint.b, tint.a); - rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer - - rlTexCoord2f((float)c.x / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); - rlVertex2f(positionX, position.y); - - rlTexCoord2f((float)c.x / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); - rlVertex2f(positionX, position.y + c.h*scaleFactor); - - rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); - rlVertex2f(positionX + c.w*scaleFactor, position.y + (c.h) * scaleFactor); - - rlTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); - rlVertex2f(positionX + c.w*scaleFactor, position.y); - - positionX += (c.w*scaleFactor + spacing); + letter = (unsigned char)text[i + 1]; + c = spriteFont.charSet[letter - FONT_FIRST_CHAR]; + i++; } - rlEnd(); + else if ((unsigned char)text[i] == 0xc3) + { + letter = (unsigned char)text[i + 1]; + c = spriteFont.charSet[letter - FONT_FIRST_CHAR + 64]; + i++; + } + else c = spriteFont.charSet[(int)text[i] - FONT_FIRST_CHAR]; - rlDisableTexture(); + DrawTexturePro(spriteFont.texture, (Rectangle){ c.x, c.y, c.w, c.h }, (Rectangle){ position.x + offsetX, position.y, c.w*scaleFactor, c.h*scaleFactor} , (Vector2){ 0, 0 }, 0.0f, tint); + + offsetX += (c.w*scaleFactor + spacing); + } } // Formatting of text with variables to 'embed' diff --git a/src/textures.c b/src/textures.c index c56816e0..e45739ac 100644 --- a/src/textures.c +++ b/src/textures.c @@ -51,10 +51,8 @@ typedef struct { unsigned char *data; // Image raw data int width; // Image base width int height; // Image base height - //int bpp; // bytes per pixel - //int components; // num color components int mipmaps; // Mipmap levels, 1 by default - int compFormat; // Compressed data format, 0 if no compression + int format; // Data format } ImageEx; //---------------------------------------------------------------------------------- @@ -134,7 +132,7 @@ Image LoadImage(const char *fileName) ImageEx imageDDS = LoadDDS(fileName); - if (imageDDS.compFormat == 0) + if (imageDDS.format == 0) { image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color)); image.width = imageDDS.width; @@ -304,14 +302,7 @@ Texture2D LoadTexture(const char *fileName) { ImageEx image = LoadDDS(fileName); - if (image.compFormat == 0) - { - texture.id = rlglLoadTexture(image.data, image.width, image.height, R8G8B8A8, false); - } - else - { - texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat); - } + texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); texture.width = image.width; texture.height = image.height; @@ -325,7 +316,7 @@ Texture2D LoadTexture(const char *fileName) { ImageEx image = LoadPKM(fileName); - texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat); + texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); texture.width = image.width; texture.height = image.height; @@ -352,7 +343,17 @@ Texture2D LoadTexture(const char *fileName) return texture; } -// TODO: Texture2D LoadTextureEx(const char *imageData, int width, int height, int colorMode) +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps) +{ + Texture2D texture; + + texture.width = width; + texture.height = height; + + texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount, genMipmaps); + + return texture; +} // Load a texture from image data // NOTE: image is not unloaded, it must be done manually @@ -382,7 +383,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) } // NOTE: rlglLoadTexture() can generate mipmaps (POT image required) - texture.id = rlglLoadTexture(imgData, image.width, image.height, R8G8B8A8, genMipmaps); + texture.id = rlglLoadTexture(imgData, image.width, image.height, UNCOMPRESSED_R8G8B8A8, 1, genMipmaps); texture.width = image.width; texture.height = image.height; @@ -494,7 +495,7 @@ void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Co Rectangle destRec = { (int)position.x, (int)position.y, sourceRec.width, sourceRec.height }; Vector2 origin = { 0, 0 }; - DrawTexturePro(texture, sourceRec, destRec, origin, 0, tint); + DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint); } // Draw a part of a texture (defined by a rectangle) with 'pro' parameters @@ -545,18 +546,6 @@ static ImageEx LoadDDS(const char *fileName) #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 - #endif - - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 - #endif - - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 - #endif - // DDS Pixel Format typedef struct { unsigned int size; @@ -594,7 +583,7 @@ static ImageEx LoadDDS(const char *fileName) image.width = 0; image.height = 0; image.mipmaps = 0; - image.compFormat = 0; + image.format = 0; FILE *ddsFile = fopen(fileName, "rb"); @@ -626,8 +615,6 @@ static ImageEx LoadDDS(const char *fileName) image.width = header.width; image.height = header.height; - image.mipmaps = 1; - image.compFormat = 0; if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed { @@ -649,8 +636,11 @@ static ImageEx LoadDDS(const char *fileName) *dest++ = 255; } } - + free(buffer); + + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8; } else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed { @@ -659,18 +649,20 @@ static ImageEx LoadDDS(const char *fileName) fread(image.data, image.width*image.height*4, 1, ddsFile); image.mipmaps = 1; - image.compFormat = 0; + image.format = UNCOMPRESSED_R8G8B8A8; } - else if ((header.ddspf.flags == 0x04) && (header.ddspf.fourCC > 0)) + else if (((header.ddspf.flags == 0x04) || (header.ddspf.flags == 0x05)) && (header.ddspf.fourCC > 0)) { - TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName); - TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName); + //TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName); + //TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName); int bufsize; // Calculate data size, including all mipmaps if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2; else bufsize = header.pitchOrLinearSize; + + TraceLog(DEBUG, "Pitch or linear size: %i", header.pitchOrLinearSize); image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); @@ -680,19 +672,18 @@ static ImageEx LoadDDS(const char *fileName) fclose(ddsFile); image.mipmaps = header.mipMapCount; - image.compFormat = 0; switch(header.ddspf.fourCC) { - case FOURCC_DXT1: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; - case FOURCC_DXT3: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; - case FOURCC_DXT5: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case FOURCC_DXT1: + { + if (header.ddspf.flags == 0x04) image.format = COMPRESSED_DXT1_RGB; + else image.format = COMPRESSED_DXT1_RGBA; + } break; + case FOURCC_DXT3: image.format = COMPRESSED_DXT3_RGBA; break; + case FOURCC_DXT5: image.format = COMPRESSED_DXT5_RGBA; break; default: break; } - - // NOTE: Image num color components not required... for now... - //if (fourCC == FOURCC_DXT1) image.components = 3; - //else image.components = 4; } } } @@ -708,10 +699,6 @@ static ImageEx LoadPKM(const char *fileName) // If OpenGL ES 2.0. the following format could be supported (ETC1): //GL_ETC1_RGB8_OES - #ifndef GL_ETC1_RGB8_OES - #define GL_ETC1_RGB8_OES 0x8D64 - #endif - // If OpenGL ES 3.0, the following formats are supported (ETC2/EAC): //GL_COMPRESSED_RGB8_ETC2 //GL_COMPRESSED_RGBA8_ETC2 @@ -780,7 +767,7 @@ static ImageEx LoadPKM(const char *fileName) image.width = width; image.height = height; image.mipmaps = 1; - image.compFormat = GL_ETC1_RGB8_OES; + image.format = COMPRESSED_ETC1_RGB; } } -- cgit v1.2.3 From 7d2318c1677fd6e81b8cc63b040289148ca8adfc Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 13 Apr 2015 20:15:28 +0200 Subject: WIP on shaders and textures Moved functions: LoadShader(), UnloadShader() Add support for PVR textures compressed/uncompressed WIP: Detect available extensions for compressed textures --- src/models.c | 21 ++++++++++ src/raylib.h | 3 ++ src/rlgl.c | 52 ++++++++++++++++++------- src/rlgl.h | 6 ++- src/textures.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index f4f83fd8..ffb077d4 100644 --- a/src/models.c +++ b/src/models.c @@ -1101,12 +1101,33 @@ void UnloadModel(Model model) rlDeleteShader(model.shader.id); } +// Link a texture to a model void SetModelTexture(Model *model, Texture2D texture) { if (texture.id <= 0) model->texture.id = whiteTexture; // Default white texture (use mesh color) else model->texture = texture; } +// Load a custom shader (vertex shader + fragment shader) +Shader LoadShader(char *vsFileName, char *fsFileName) +{ + Shader shader = rlglLoadShader(vsFileName, fsFileName); + + return shader; +} + +// Unload a custom shader from memory +void UnloadShader(Shader shader) +{ + rlDeleteShader(shader.id); +} + +// Set shader for a model +void SetModelShader(Model *model, Shader shader) +{ + rlglSetModelShader(model, shader); +} + // Draw a model (with texture if set) void DrawModel(Model model, Vector3 position, float scale, Color tint) { diff --git a/src/raylib.h b/src/raylib.h index 0e532ebe..603cc47d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -338,6 +338,8 @@ typedef enum { COMPRESSED_ETC1_RGB, // 4 bpp COMPRESSED_ETC2_RGB, // 4 bpp COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp } TextureFormat; @@ -543,6 +545,7 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader (vertex shader + fragment shader) +void UnloadShader(Shader shader); // Unload a custom shader from memory bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); diff --git a/src/rlgl.c b/src/rlgl.c index c4deda8f..cedad6a7 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -793,7 +793,7 @@ void rlglInit(void) const GLubyte *extensionName = glGetStringi(GL_EXTENSIONS, i); if (strcmp(extensionName, (const GLubyte *)"GL_ARB_vertex_array_object") == 0) { - // The extension is supported by our hardware and driver, try to get related functions popinters + // The extension is supported by our hardware and driver, try to get related functions pointers glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)wglGetProcAddress("glGenVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)wglGetProcAddress("glBindVertexArray"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)wglGetProcAddress("glDeleteVertexArrays"); @@ -851,14 +851,40 @@ void rlglInit(void) // Show supported extensions // NOTE: We don't need that much data on screen... right now... -/* + #if defined(GRAPHICS_API_OPENGL_33) GLint numExt; glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); for (int i = 0; i < numExt; i++) { - TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); + //TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); + /* + if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_EXT_texture_compression_s3tc") == 0) + { + // DDS texture compression support + + // TODO: Check required tokens + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_OES_compressed_ETC1_RGB8_texture") == 0) + { + // ETC1 texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) + { + //OES_compressed_ETC2_RGB8_texture, + //OES_compressed_ETC2_RGBA8_texture, + // ETC2/EAC texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) + { + // PVR texture compression support + } + else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) + { + // ASTC texture compression support + } + */ } #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string @@ -866,7 +892,7 @@ void rlglInit(void) // NOTE: String could be splitted using strtok() function (string.h) TraceLog(INFO, "Supported extension: %s", extensions); #endif -*/ + /* GLint numComp = 0; glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -894,7 +920,7 @@ void rlglInit(void) // Init default Shader (GLSL 110) -> Common for GL 3.3+ and ES2 defaultShader = LoadDefaultShader(); simpleShader = LoadSimpleShader(); - //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok + //customShader = rlglLoadShader("custom.vs", "custom.fs"); // Works ok InitializeBuffers(); // Init vertex arrays InitializeBuffersGPU(); // Init VBO and VAO @@ -1795,7 +1821,7 @@ Model rlglLoadModel(VertexData mesh) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load a shader (vertex shader + fragment shader) from text data -unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr) +unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr) { unsigned int program; GLuint vertexShader; @@ -1915,17 +1941,17 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } -// Load a shader (vertex shader + fragment shader) from text data -Shader LoadShader(char *vsFileName, char *fsFileName) +// Load a shader (vertex shader + fragment shader) from files +Shader rlglLoadShader(char *vsFileName, char *fsFileName) { Shader shader; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Shaders loading from external text file char *vShaderStr = TextFileRead(vsFileName); char *fShaderStr = TextFileRead(fsFileName); - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shader.id); @@ -1955,7 +1981,7 @@ Shader LoadShader(char *vsFileName, char *fsFileName) } // Link shader to model -void SetModelShader(Model *model, Shader shader) +void rlglSetModelShader(Model *model, Shader shader) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model->shader = shader; @@ -2079,7 +2105,7 @@ static Shader LoadDefaultShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" "} \n"; - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); @@ -2145,7 +2171,7 @@ static Shader LoadSimpleShader(void) " gl_FragColor = texture2D(texture0, fragTexCoord) * tintColor; \n" "} \n"; - shader.id = rlglLoadShader(vShaderStr, fShaderStr); + shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id); else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id); diff --git a/src/rlgl.h b/src/rlgl.h index c1d71cc9..3335fe16 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -104,6 +104,8 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion; COMPRESSED_ETC1_RGB, // 4 bpp COMPRESSED_ETC2_RGB, // 4 bpp COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp } TextureFormat; @@ -208,10 +210,12 @@ void rlglDraw(void); // Draw VAO/VBO void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load in GPU OpenGL texture -unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data +Shader rlglLoadShader(char *vsFileName, char *fsFileName); // Load a shader (vertex shader + fragment shader) from files +unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr); // Load a shader from text data void rlglInitPostpro(void); // Initialize postprocessing system void rlglDrawPostpro(void); // Draw with postprocessing shader void rlglSetPostproShader(Shader shader); // Set postprocessing shader +void rlglSetModelShader(Model *model, Shader shader); // Set shader for a model Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); diff --git a/src/textures.c b/src/textures.c index e45739ac..1d22e509 100644 --- a/src/textures.c +++ b/src/textures.c @@ -70,6 +70,7 @@ typedef struct { //---------------------------------------------------------------------------------- static ImageEx LoadDDS(const char *fileName); // Load DDS file static ImageEx LoadPKM(const char *fileName); // Load PKM file +static ImageEx LoadPVR(const char *fileName); // Load PVR file //---------------------------------------------------------------------------------- // Module Functions Definition @@ -326,6 +327,20 @@ Texture2D LoadTexture(const char *fileName) free(image.data); } + else if (strcmp(GetExtension(fileName),"pvr") == 0) + { + ImageEx image = LoadPVR(fileName); + + texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); + + texture.width = image.width; + texture.height = image.height; + + if (texture.id == 0) TraceLog(WARNING, "[%s] PVR texture could not be loaded", fileName); + else TraceLog(INFO, "[%s] PVR texture loaded successfully", fileName); + + free(image.data); + } else { Image image = LoadImage(fileName); @@ -694,6 +709,7 @@ static ImageEx LoadDDS(const char *fileName) // Loading PKM image data (ETC1/ETC2 compression) // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) +// ETC1 compression support requires extension GL_OES_compressed_ETC1_RGB8_texture static ImageEx LoadPKM(const char *fileName) { // If OpenGL ES 2.0. the following format could be supported (ETC1): @@ -771,5 +787,110 @@ static ImageEx LoadPKM(const char *fileName) } } + return image; +} + +// Loading PVR image data (uncompressed or PVRT compression) +// NOTE: PVR compression requires extension GL_IMG_texture_compression_pvrtc (PowerVR GPUs) +static ImageEx LoadPVR(const char *fileName) +{ + // If supported in OpenGL ES 2.0. the following formats could be defined: + // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB) + // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGBA) + + // PVR file v2 Header (52 bytes) + typedef struct { + unsigned int headerLength; + unsigned int height; + unsigned int width; + unsigned int numMipmaps; + unsigned int flags; + unsigned int dataLength; + unsigned int bpp; + unsigned int bitmaskRed; + unsigned int bitmaskGreen; + unsigned int bitmaskBlue; + unsigned int bitmaskAlpha; + unsigned int pvrTag; + unsigned int numSurfs; + } pvrHeaderV2; + + // PVR file v3 Header (52 bytes) + // NOTE: After it could be metadata (15 bytes?) + typedef struct { + unsigned int version; + unsigned int flags; + unsigned long long pixelFormat; + unsigned int colourSpace; + unsigned int channelType; + unsigned int height; + unsigned int width; + unsigned int depth; + unsigned int numSurfaces; + unsigned int numFaces; + unsigned int numMipmaps; + unsigned int metaDataSize; + } pvrHeaderV3; + + // Bytes Swap (little-endian <-> big-endian) + //unsigned short data; + //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8); + + ImageEx image; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; + + FILE *pvrFile = fopen(fileName, "rb"); + + if (pvrFile == NULL) + { + TraceLog(WARNING, "[%s] PVR image file could not be opened", fileName); + } + else + { + // Check PVR image version + unsigned int pvrVersion = 0; + fread(&pvrVersion, sizeof(unsigned int), 1, pvrFile); + fsetpos(pvrFile, 0); + + if (pvrVersion == 52) + { + pvrHeaderV2 header; + + fread(&header, sizeof(pvrHeaderV2), 1, pvrFile); + + image.width = header.width; + image.height = header.height; + image.mipmaps = header.numMipmaps; + image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA + } + else if (pvrVersion == 3) + { + pvrHeaderV3 header; + + fread(&header, sizeof(pvrHeaderV3), 1, pvrFile); + + image.width = header.width; + image.height = header.height; + image.mipmaps = header.numMipmaps; + + // TODO: Skip metaDataSize + + image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA + } + + int size = (image.width/4)*(image.height/4)*8; // Total data size in bytes + + image.data = (unsigned char*)malloc(size * sizeof(unsigned char)); + + fread(image.data, 1, size, pvrFile); + + fclose(pvrFile); // Close file pointer + } + return image; } \ No newline at end of file -- cgit v1.2.3 From ceb73257272e34a739b6225e1f30e4e377fdab77 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 22 Apr 2015 17:34:42 +0200 Subject: Added Gestures System for Android and Web --- src/core.c | 316 ++++------------------------ src/gestures.c | 636 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/raylib.h | 24 ++- 3 files changed, 702 insertions(+), 274 deletions(-) create mode 100644 src/gestures.c (limited to 'src') diff --git a/src/core.c b/src/core.c index fbc4838a..7196fbb0 100644 --- a/src/core.c +++ b/src/core.c @@ -158,22 +158,6 @@ static struct android_poll_source *source; // Android events polling source static int ident, events; static bool windowReady = false; // Used to detect display initialization -// Gestures detection variables -static float tapTouchX, tapTouchY; -static int64_t lastTapTime = 0; -static float lastTapX = 0, lastTapY = 0; -static bool touchTap = false; -static bool doubleTap = false; -static bool drag = false; -static int stdVector[MAX_TOUCH_POINTS]; -static int indexPosition = 0; -const AInputEvent* eventDrag; -static int32_t touchId; -const int32_t DOUBLE_TAP_TIMEOUT = 300*1000000; -const int32_t DOUBLE_TAP_SLOP = 100; -const int32_t TAP_TIMEOUT = 180*1000000; -const int32_t TOUCH_SLOP = 8; - #elif defined(PLATFORM_RPI) static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) @@ -241,11 +225,6 @@ static int exitKey = KEY_ESCAPE; // Default exit key (ESC) static int lastKeyPressed = -1; #endif -#if defined(PLATFORM_ANDROID) -static float touchX; // Touch position X -static float touchY; // Touch position Y -#endif - static double currentTime, previousTime; // Used to track timmings static double updateTime, drawTime; // Time measures for update and draw static double frameTime; // Time measure for one frame @@ -276,6 +255,17 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads defaul extern void UpdateMusicStream(void); // [Module: audio] Updates buffers for music streaming +extern Vector2 GetRawPosition(void); +extern void ResetGestures(void); + +#if defined(PLATFORM_ANDROID) +extern void InitAndroidGestures(struct android_app *app); +#endif + +#if defined(PLATFORM_WEB) +extern void InitWebGestures(void); +#endif + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -313,8 +303,7 @@ static void TakeScreenshot(void); #endif #if defined(PLATFORM_ANDROID) -static int32_t InputCallback(struct android_app *app, AInputEvent *event); // Process Android activity input events -static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands +static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands #endif static void ProcessCamera(Camera *camera, Vector3 *playerPosition); @@ -350,6 +339,11 @@ void InitWindow(int width, int height, const char *title) InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif + +#if defined(PLATFORM_WEB) + InitWebGestures(); // Init touch input events for web +#endif + mousePosition.x = screenWidth/2; mousePosition.y = screenHeight/2; @@ -401,8 +395,9 @@ void InitWindow(int width, int height, struct android_app *state) //AConfiguration_getScreenLong(app->config); //state->userData = &engine; - app->onAppCmd = CommandCallback; - app->onInputEvent = InputCallback; + app->onAppCmd = AndroidCommandCallback; + + InitAndroidGestures(app); InitAssetManager(app->activity->assetManager); @@ -557,6 +552,11 @@ void EndDrawing(void) if (enabledPostpro) rlglDrawPostpro(); // Draw postprocessing effect (shader) SwapBuffers(); // Copy back buffer to front buffer + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) + ResetGestures(); +#endif + PollInputEvents(); // Poll user events UpdateMusicStream(); // NOTE: Function checks if music is enabled @@ -1053,63 +1053,37 @@ bool IsGamepadButtonUp(int gamepad, int button) #endif #if defined(PLATFORM_ANDROID) -bool IsScreenTouched(void) -{ - return touchTap; -} - -bool IsDoubleTap(void) -{ - if (doubleTap) TraceLog(INFO, "DOUBLE TAP gesture detected"); - - return doubleTap; -} - -bool IsDragGesture(void) -{ - return drag; -} - // Returns touch position X int GetTouchX(void) { - return (int)touchX; + return (int)GetRawPosition().x; } // Returns touch position Y int GetTouchY(void) { - return (int)touchY; + return (int)GetRawPosition().y; } // Returns touch position XY Vector2 GetTouchPosition(void) { - Vector2 position = { touchX, touchY }; + Vector2 position = GetRawPosition(); + + if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) + { + // TODO: Seems to work ok but... review! + position.x = position.x*((float)screenWidth / (float)(displayWidth - renderOffsetX)) - renderOffsetX/2; + position.y = position.y*((float)screenHeight / (float)(displayHeight - renderOffsetY)) - renderOffsetY/2; + } + else + { + position.x = position.x*((float)renderWidth / (float)displayWidth) - renderOffsetX/2; + position.y = position.y*((float)renderHeight / (float)displayHeight) - renderOffsetY/2; + } return position; } - -/*bool GetPointer(Vector2 *dragPositions) -{ - //static int stdVector[MAX_TOUCH_POINTS]; - //static int indexPosition = 0; - //if (indexPosition == 0) return false; - Vector2 vec_pointers_[]; - - //eventDrag - int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] ); - - if (iIndex == -1) return false; - - float x = AMotionEvent_getX(eventDrag, iIndex); - float y = AMotionEvent_getY(eventDrag, iIndex); - - *dragPositions = Vector2( x, y ); - - - return true; -}*/ #endif // Set postprocessing shader @@ -1499,209 +1473,8 @@ static void WindowIconifyCallback(GLFWwindow* window, int iconified) #endif #if defined(PLATFORM_ANDROID) -// Android: Process activity input events -static int32_t InputCallback(struct android_app *app, AInputEvent *event) -{ - int type = AInputEvent_getType(event); - //int32_t key = 0; - - if (type == AINPUT_EVENT_TYPE_MOTION) - { - // Detect TOUCH position - if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) - { - // TODO: Seems to work ok but... review! - touchX = AMotionEvent_getX(event, 0) * ((float)screenWidth / (float)(displayWidth - renderOffsetX)) - renderOffsetX/2; - touchY = AMotionEvent_getY(event, 0) * ((float)screenHeight / (float)(displayHeight - renderOffsetY)) - renderOffsetY/2; - } - else - { - touchX = AMotionEvent_getX(event, 0) * ((float)renderWidth / (float)displayWidth) - renderOffsetX/2; - touchY = AMotionEvent_getY(event, 0) * ((float)renderHeight / (float)displayHeight) - renderOffsetY/2; - } - - // Detect TAP event -/* - if (AMotionEvent_getPointerCount(event) > 1 ) - { - // Only support single touch - return false; - } -*/ - int32_t action = AMotionEvent_getAction(event); - unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - - switch (flags) - { - case AMOTION_EVENT_ACTION_DOWN: - { - touchId = AMotionEvent_getPointerId(event, 0); - tapTouchX = AMotionEvent_getX(event, 0); - tapTouchY = AMotionEvent_getY(event, 0); - - } break; - case AMOTION_EVENT_ACTION_UP: - { - int64_t eventTime = AMotionEvent_getEventTime(event); - int64_t downTime = AMotionEvent_getDownTime(event); - - if (eventTime - downTime <= TAP_TIMEOUT) - { - if (touchId == AMotionEvent_getPointerId(event, 0)) - { - float x = AMotionEvent_getX(event, 0) - tapTouchX; - float y = AMotionEvent_getY(event, 0) - tapTouchY; - - float densityFactor = 1.0f; - - if ( x*x + y*y < TOUCH_SLOP*TOUCH_SLOP * densityFactor) - { - // TAP Detected - touchTap = true; - } - } - } - break; - } - } - - //float AMotionEvent_getX(event, size_t pointer_index); - //int32_t AMotionEvent_getButtonState(event); // Pressed buttons - //int32_t AMotionEvent_getPointerId(event, size_t pointer_index); - //size_t pointerCount = AMotionEvent_getPointerCount(event); - //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1 - //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area - - // Detect DOUBLE TAP event - bool tapDetected = touchTap; - - switch (flags) - { - case AMOTION_EVENT_ACTION_DOWN: - { - int64_t eventTime = AMotionEvent_getEventTime(event); - - if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT) - { - float x = AMotionEvent_getX(event, 0) - lastTapX; - float y = AMotionEvent_getY(event, 0) - lastTapY; - - float densityFactor = 1.0f; - - if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor)) - { - // Doubletap detected - doubleTap = true; - - } - } - } break; - case AMOTION_EVENT_ACTION_UP: - { - if (tapDetected) - { - lastTapTime = AMotionEvent_getEventTime(event); - lastTapX = AMotionEvent_getX(event, 0); - lastTapY = AMotionEvent_getY(event, 0); - - } - } break; - } - - - // Detect DRAG event - //int32_t action = AMotionEvent_getAction(event); - - int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - //uint32_t flags = action & AMOTION_EVENT_ACTION_MASK; - //event_ = event; - - int32_t count = AMotionEvent_getPointerCount(event); - - switch (flags) - { - case AMOTION_EVENT_ACTION_DOWN: - { - stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0); - indexPosition++; - TraceLog(INFO, "ACTION_DOWN"); - - //ret = GESTURE_STATE_START; - } break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - { - stdVector[indexPosition] = AMotionEvent_getPointerId(event, index); - indexPosition++; - TraceLog(INFO, "ACTION_POINTER_DOWN"); - - } break; - case AMOTION_EVENT_ACTION_UP: - { - //int value = stdVector[indexPosition]; - indexPosition--; - //ret = GESTURE_STATE_END; - TraceLog(INFO, "ACTION_UP"); - - } break; - case AMOTION_EVENT_ACTION_POINTER_UP: - { - int32_t releasedPointerId = AMotionEvent_getPointerId(event, index); - - int i = 0; - for (i = 0; i < MAX_TOUCH_POINTS; i++) - { - if (stdVector[i] == releasedPointerId) - { - for (int k = i; k < indexPosition - 1; k++) - { - stdVector[k] = stdVector[k + 1]; - } - - //indexPosition--; - indexPosition = 0; - break; - } - } - - if (i <= 1) - { - // Reset pinch or drag - //if (count == 2) //ret = GESTURE_STATE_START; - } - TraceLog(INFO, "ACTION_POINTER_UP"); - - } break; - case AMOTION_EVENT_ACTION_MOVE: - { - if (count == 1) - { - //TraceLog(INFO, "DRAG gesture detected"); - - drag = true; //ret = GESTURE_STATE_MOVE; - } - else break; - TraceLog(INFO, "ACTION_MOVE"); - - } break; - case AMOTION_EVENT_ACTION_CANCEL: break; - default: break; - } - - //-------------------------------------------------------------------- - - return 1; - } - else if (type == AINPUT_EVENT_TYPE_KEY) - { - //key = AKeyEvent_getKeyCode(event); - //int32_t AKeyEvent_getMetaState(event); - } - - return 0; -} - // Android: Process activity lifecycle commands -static void CommandCallback(struct android_app *app, int32_t cmd) +static void AndroidCommandCallback(struct android_app *app, int32_t cmd) { switch (cmd) { @@ -1905,11 +1678,6 @@ static void PollInputEvents(void) // TODO: Check virtual keyboard (?) - // Reset touch events - touchTap = false; - doubleTap = false; - drag = false; - // Poll Events (registered events) // TODO: Enable/disable activityMinimized to block activity if minimized //while ((ident = ALooper_pollAll(activityMinimized ? 0 : -1, NULL, &events,(void**)&source)) >= 0) @@ -2397,6 +2165,7 @@ static void LogoAnimation(void) // Process desired camera mode and controls static void ProcessCamera(Camera *camera, Vector3 *playerPosition) { +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) // Mouse movement detection if (cameraMode != CAMERA_FREE) { @@ -2647,4 +2416,5 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) } break; default: break; } +#endif } \ No newline at end of file diff --git a/src/gestures.c b/src/gestures.c new file mode 100644 index 00000000..594c3cd5 --- /dev/null +++ b/src/gestures.c @@ -0,0 +1,636 @@ +/********************************************************************************************** +* +* raylib.gestures +* +* Gestures Detection and Usage Functions Definitions +* +* Copyright (c) 2015 Marc Palau and Ramon Santamaria +* +* 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. +* +**********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "utils.h" + +#include // malloc(), free() +#include // printf(), fprintf() +#include // Used for ... +#include + +#if defined(PLATFORM_ANDROID) + #include // Java native interface + #include // Android sensors functions + #include // Defines AWINDOW_FLAG_FULLSCREEN and others +#endif + +#if defined(PLATFORM_WEB) + #include + #include +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define FORCE_TO_SWIPE 20 +#define TAP_TIMEOUT 300 + +#define MAX_TOUCH_POINTS 4 + +typedef enum { + TYPE_MOTIONLESS, + TYPE_DRAG, + TYPE_DUAL_INPUT +} GestureType; + +typedef enum { + UP, + DOWN, + MOVE +} ActionType; + +typedef struct { + ActionType action; + int pointCount; + int pointerId[MAX_TOUCH_POINTS]; + Vector2 position[MAX_TOUCH_POINTS]; +} GestureEvent; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- + +// typedef +static GestureType gestureType = TYPE_MOTIONLESS; + +// Gestures detection variables +static int32_t touchId; + +// Event +static int64_t eventTime = 0; + +// Tap +// Our initial press position on tap +static Vector2 initialTapPosition = { 0, 0 }; + +// Double tap +// If we are double tapping or not +static bool doubleTapping = false; +// If we recently made a tap +static bool untap = false; + +// Drag +// Our initial press position on drag +static Vector2 initialDragPosition = { 0, 0 }; +// Position that will compare itself with the mouse one +static Vector2 endDragPosition = { 0, 0 }; +// Position of the last event detection +static Vector2 lastDragPosition = { 0, 0 }; +// The total drag vector +static Vector2 dragVector = { 0, 0 }; +// The distance traveled dragging +static float magnitude = 0; +// The angle direction of the drag +static float angle = 0; +// A magnitude to calculate how fast we did the drag ( pixels per frame ) +static float intensity = 0; +// Time that have passed while dragging +static int draggingTimeCounter = 0; + +// Pinch +// First initial pinch position +static Vector2 firstInitialPinchPosition = { 0, 0 }; +// Second initial pinch position +static Vector2 secondInitialPinchPosition = { 0, 0 }; +// First end pinch position +static Vector2 firstEndPinchPosition = { 0, 0 }; +// Second end pinch position +static Vector2 secondEndPinchPosition = { 0, 0 }; +// Delta Displacement +static float pinchDelta = 0; + +// Detected gesture +static int currentGesture = GESTURE_NONE; + +static float touchX, touchY; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +extern void ProcessMotionEvent(GestureEvent event); +extern void ResetGestures(void); +extern Vector2 GetRawPosition(void); + +static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, float magnitude); +static float OnPinch(); +static void SetDualInput(GestureEvent event); +static float Distance(Vector2 v1, Vector2 v2); +static float DotProduct(Vector2 v1, Vector2 v2); +static int GetCurrentTime(); + +#if defined(PLATFORM_WEB) +static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +#endif + +#if defined(PLATFORM_ANDROID) +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Returns tap position XY +extern Vector2 GetRawPosition(void) +{ + Vector2 position = { touchX, touchY }; + + return position; +} + +// Check if a gesture have been detected +bool IsGestureDetected(void) +{ + if (currentGesture == GESTURE_DRAG) TraceLog(INFO, "DRAG"); + else if (currentGesture == GESTURE_TAP) TraceLog(INFO, "TAP"); + else if (currentGesture == GESTURE_DOUBLETAP) TraceLog(INFO, "DOUBLE"); + else if (currentGesture == GESTURE_HOLD) TraceLog(INFO, "HOLD"); + else if (currentGesture == GESTURE_SWIPE_RIGHT) TraceLog(INFO, "RIGHT"); + else if (currentGesture == GESTURE_SWIPE_UP) TraceLog(INFO, "UP"); + else if (currentGesture == GESTURE_SWIPE_LEFT) TraceLog(INFO, "LEFT"); + else if (currentGesture == GESTURE_SWIPE_DOWN) TraceLog(INFO, "DOWN"); + else if (currentGesture == GESTURE_PINCH_IN) TraceLog(INFO, "PINCH IN"); + else if (currentGesture == GESTURE_PINCH_OUT) TraceLog(INFO, "PINCH OUT"); + + if (currentGesture != GESTURE_NONE) return false; + else return true; +} + +// Check gesture type +int GetGestureType(void) +{ + return currentGesture; +} + +// Get drag intensity (pixels per frame) +float GetDragIntensity(void) +{ + return intensity; +} + +// Get drag angle +// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise +float GetDragAngle(void) +{ + return angle; +} + +// Get drag vector (between initial and final position) +Vector2 GetDragVector(void) +{ + return dragVector; +} + +// Hold time measured in frames +int GetHoldDuration(void) +{ + return 0; +} + +// Get magnitude between two pinch points +float GetPinchDelta(void) +{ + return pinchDelta; +} + +// Get angle beween two pinch points +// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise +float GetPinchAngle(void) +{ + return 0; +} + +extern void ResetGestures(void) +{ + if (currentGesture != GESTURE_HOLD) currentGesture = GESTURE_NONE; +} + +#if defined(PLATFORM_WEB) +extern void InitWebGestures(void) +{ + /* + emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch); + emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch); + emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch); + emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch); + */ + + //emscripten_set_touchstart_callback(0, NULL, 1, Emscripten_HandleTouch); + + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenInputCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenInputCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenInputCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenInputCallback); +} +#endif + +#if defined(PLATFORM_ANDROID) +extern void InitAndroidGestures(struct android_app *app) +{ + app->onInputEvent = AndroidInputCallback; + + // TODO: Receive frameBuffer data: displayWidth/displayHeight, renderWidth/renderHeight, screenWidth/screenHeight +} +#endif + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +extern void ProcessMotionEvent(GestureEvent event) +{ + // Resets + dragVector = (Vector2){ 0, 0 }; + pinchDelta = 0; + + switch (gestureType) + { + case TYPE_MOTIONLESS: // Detect TAP, DOUBLE_TAP and HOLD events + { + if (event.action == DOWN) + { + if (event.pointCount > 1) SetDualInput(event); + else + { + // Set the press position + initialTapPosition = event.position[0]; + + // If too much time have passed, we reset the double tap + if (GetCurrentTime() - eventTime > TAP_TIMEOUT) untap = false; + + // If we are in time, we detect the double tap + if (untap) doubleTapping = true; + + // Update our event time + eventTime = GetCurrentTime(); + + // Set hold + currentGesture = GESTURE_HOLD; + } + } + else if (event.action == UP) + { + // Detect that we are tapping instead of holding + if (GetCurrentTime() - eventTime < TAP_TIMEOUT) + { + if (doubleTapping) + { + // If we tapped before we define it as double tap + currentGesture = GESTURE_DOUBLETAP; + untap = false; + } + else + { + // Simple tap + currentGesture = GESTURE_TAP; + untap = true; + } + } + else currentGesture = GESTURE_NONE; + + // Tap finished + doubleTapping = false; + // Update our event time + eventTime = GetCurrentTime(); + } + // Begin dragging + else if (event.action == MOVE) + { + if (event.pointCount > 1) SetDualInput(event); + else + { + // Set the drag starting position + initialDragPosition = initialTapPosition; + endDragPosition = initialDragPosition; + + // Initialize drag + draggingTimeCounter = 0; + gestureType = TYPE_DRAG; + currentGesture = GESTURE_NONE; + } + } + } break; + case TYPE_DRAG: // Detect DRAG and SWIPE events + { + // end of the drag + if (event.action == UP) + { + // Return Swipe if we have enough sensitivity + if (intensity > FORCE_TO_SWIPE) + { + if (angle < 30 || angle > 330) currentGesture = GESTURE_SWIPE_RIGHT; // Right + else if (angle > 60 && angle < 120) currentGesture = GESTURE_SWIPE_UP; // Up + else if (angle > 150 && angle < 210) currentGesture = GESTURE_SWIPE_LEFT; // Left + else if (angle > 240 && angle < 300) currentGesture = GESTURE_SWIPE_DOWN; // Down + } + + magnitude = 0; + angle = 0; + intensity = 0; + + gestureType = TYPE_MOTIONLESS; + } + // Update while we are dragging + else if (event.action == MOVE) + { + if (event.pointCount > 1) SetDualInput(event); + else + { + lastDragPosition = endDragPosition; + + endDragPosition = GetRawPosition(); + + //endDragPosition.x = AMotionEvent_getX(event, 0); + //endDragPosition.y = AMotionEvent_getY(event, 0); + + // Calculate attributes + dragVector = (Vector2){ endDragPosition.x - lastDragPosition.x, endDragPosition.y - lastDragPosition.y }; + magnitude = sqrt(pow(endDragPosition.x - initialDragPosition.x, 2) + pow(endDragPosition.y - initialDragPosition.y, 2)); + angle = CalculateAngle(initialDragPosition, endDragPosition, magnitude); + intensity = magnitude / (float)draggingTimeCounter; + + currentGesture = GESTURE_DRAG; + draggingTimeCounter++; + } + } + } break; + case TYPE_DUAL_INPUT: + { + if (event.action == UP) + { + if (event.pointCount == 1) + { + // Set the drag starting position + initialTapPosition = event.position[0]; + } + gestureType = TYPE_MOTIONLESS; + } + else if (event.action == MOVE) + { + // Adapt the ending position of the inputs + firstEndPinchPosition = event.position[0]; + secondEndPinchPosition = event.position[1]; + + // If there is no more than two inputs + if (event.pointCount == 2) + { + // Detect pinch delta + pinchDelta = OnPinch(); + + // Pinch gesture resolution + if (pinchDelta != 0) + { + if (pinchDelta > 0) currentGesture = GESTURE_PINCH_IN; + else currentGesture = GESTURE_PINCH_OUT; + } + } + else + { + // Set the drag starting position + initialTapPosition = event.position[0]; + + gestureType = TYPE_MOTIONLESS; + } + + // Readapt the initial position of the inputs + firstInitialPinchPosition = firstEndPinchPosition; + secondInitialPinchPosition = secondEndPinchPosition; + } + } break; + } + //-------------------------------------------------------------------- +} + + +static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, float magnitude) +{ + float angle; + + // Calculate arcsinus of the movement ( Our sinus is (actualPosition.y - initialPosition.y) / magnitude) + angle = asin((actualPosition.y - initialPosition.y) / magnitude); + angle *= RAD2DEG; + + // Calculate angle depending on the sector + if (actualPosition.x - initialPosition.x >= 0) + { + // Sector 4 + if (actualPosition.y - initialPosition.y >= 0) + { + angle *= -1; + angle += 360; + } + // Sector 1 + else + { + angle *= -1; + } + } + else + { + // Sector 3 + if (actualPosition.y - initialPosition.y >= 0) + { + angle += 180; + } + // Sector 2 + else + { + angle *= -1; + angle = 180 - angle; + } + } + + return angle; +} + +static float OnPinch() +{ + // Calculate distances + float initialDistance = Distance(firstInitialPinchPosition, secondInitialPinchPosition); + float endDistance = Distance(firstEndPinchPosition, secondEndPinchPosition); + + // Calculate Vectors + Vector2 firstTouchVector = { firstEndPinchPosition.x - firstInitialPinchPosition.x, firstEndPinchPosition.y - firstInitialPinchPosition.y }; + Vector2 secondTouchVector = { secondEndPinchPosition.x - secondInitialPinchPosition.x, secondEndPinchPosition.y - secondInitialPinchPosition.y }; + + // Detect the pinch gesture + // Calculate Distances + if (DotProduct(firstTouchVector, secondTouchVector) < -0.5) return initialDistance - endDistance; + else return 0; +} + +static void SetDualInput(GestureEvent event) +{ + initialDragPosition = (Vector2){ 0, 0 }; + endDragPosition = (Vector2){ 0, 0 }; + lastDragPosition = (Vector2){ 0, 0 }; + + // Initialize positions + firstInitialPinchPosition = event.position[0]; + secondInitialPinchPosition = event.position[1]; + + firstEndPinchPosition = firstInitialPinchPosition; + secondEndPinchPosition = secondInitialPinchPosition; + + // Resets + magnitude = 0; + angle = 0; + intensity = 0; + + gestureType = TYPE_DUAL_INPUT; +} + +static float Distance(Vector2 v1, Vector2 v2) +{ + float result; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + + result = sqrt(dx*dx + dy*dy); + + return result; +} + +static float DotProduct(Vector2 v1, Vector2 v2) +{ + float result; + + float v1Module = sqrt(v1.x*v1.x + v1.y*v1.y); + float v2Module = sqrt(v2.x*v2.x + v2.y*v2.y); + + Vector2 v1Normalized = { v1.x / v1Module, v1.y / v1Module }; + Vector2 v2Normalized = { v2.x / v2Module, v2.y / v2Module }; + + result = v1Normalized.x*v2Normalized.x + v1Normalized.y*v2Normalized.y; + + return result; +} + +static int GetCurrentTime() +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time provided in nanoseconds + + return nowTime / 1000000; // Return time in miliseconds +} + +#if defined(PLATFORM_ANDROID) +// Android: Process activity input events +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) +{ + int type = AInputEvent_getType(event); + //int32_t key = 0; + + if (type == AINPUT_EVENT_TYPE_MOTION) + { + touchX = AMotionEvent_getX(event, 0); + touchY = AMotionEvent_getY(event, 0); + } + else if (type == AINPUT_EVENT_TYPE_KEY) + { + //key = AKeyEvent_getKeyCode(event); + //int32_t AKeyEvent_getMetaState(event); + } + + int32_t action = AMotionEvent_getAction(event); + unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + + + GestureEvent gestureEvent; + + // Action + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.action = DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.action = UP; + else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.action = MOVE; + + // Points + gestureEvent.pointCount = AMotionEvent_getPointerCount(event); + + // Position + gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) }; + gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) }; + + ProcessMotionEvent(gestureEvent); + + return 0; +} +#endif + +#if defined(PLATFORM_WEB) +static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) +{ + /* + for (int i = 0; i < touchEvent->numTouches; i++) + { + long x, y, id; + + if (!touchEvent->touches[i].isChanged) continue; + + id = touchEvent->touches[i].identifier; + x = touchEvent->touches[i].canvasX; + y = touchEvent->touches[i].canvasY; + } + + printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches, + event->ctrlKey ? " CTRL" : "", event->shiftKey ? " SHIFT" : "", event->altKey ? " ALT" : "", event->metaKey ? " META" : ""); + + for(int i = 0; i < event->numTouches; ++i) + { + const EmscriptenTouchPoint *t = &event->touches[i]; + + printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); + } + */ + + GestureEvent gestureEvent; + + // Action + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.action = DOWN; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.action = UP; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.action = MOVE; + + // Points + gestureEvent.pointCount = event->numTouches; + + // Position + gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY }; + gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY }; + + + ProcessMotionEvent(gestureEvent); + + return 1; +} +#endif + + + + + + diff --git a/src/raylib.h b/src/raylib.h index 603cc47d..962376a9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -183,6 +183,20 @@ typedef enum { false, true } bool; #endif +typedef enum { + GESTURE_NONE = 0, + GESTURE_TAP, + GESTURE_DOUBLETAP, + GESTURE_HOLD, + GESTURE_DRAG, + GESTURE_SWIPE_RIGHT, + GESTURE_SWIPE_LEFT, + GESTURE_SWIPE_UP, + GESTURE_SWIPE_DOWN, + GESTURE_PINCH_IN, + GESTURE_PINCH_OUT +} Gestures; + // byte type typedef unsigned char byte; @@ -433,10 +447,18 @@ bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad b #endif #if defined(PLATFORM_ANDROID) -bool IsScreenTouched(void); // Detect screen touch event int GetTouchX(void); // Returns touch position X int GetTouchY(void); // Returns touch position Y Vector2 GetTouchPosition(void); // Returns touch position XY + +bool IsGestureDetected(void); +int GetGestureType(void); +float GetDragIntensity(void); +float GetDragAngle(void); +Vector2 GetDragVector(void); +int GetHoldDuration(void); // Hold time in frames +float GetPinchDelta(void); +float GetPinchAngle(void); #endif //------------------------------------------------------------------------------------ -- cgit v1.2.3 From 7db895ab5d415d931e0319061d437c952a6155b2 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 22 Apr 2015 18:36:52 +0200 Subject: Corrected some bugs and warnings --- src/core.c | 4 ++-- src/gestures.c | 10 +++++----- src/raylib.h | 2 +- src/raymath.c | 6 +++--- src/rlgl.c | 2 ++ src/shapes.c | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 7196fbb0..81583bf3 100644 --- a/src/core.c +++ b/src/core.c @@ -718,7 +718,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) float realy = (float)GetScreenHeight() - mousePosition.y; - float z; + //float z; // glReadPixels(mousePosition.x, mousePosition.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); //http://www.bfilipek.com/2012/06/select-mouse-opengl.html @@ -955,7 +955,7 @@ bool IsCursorHidden() // TODO: Enable gamepad usage on Rapsberry Pi // NOTE: emscripten not implemented -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // Detect if a gamepad is available bool IsGamepadAvailable(int gamepad) { diff --git a/src/gestures.c b/src/gestures.c index 594c3cd5..00b57e4c 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -30,7 +30,8 @@ #include // malloc(), free() #include // printf(), fprintf() #include // Used for ... -#include +#include // Used for clock functions +#include // Defines int32_t, int64_t #if defined(PLATFORM_ANDROID) #include // Java native interface @@ -40,7 +41,7 @@ #if defined(PLATFORM_WEB) #include - #include + #include #endif //---------------------------------------------------------------------------------- @@ -78,7 +79,7 @@ typedef struct { static GestureType gestureType = TYPE_MOTIONLESS; // Gestures detection variables -static int32_t touchId; +//static int32_t touchId; // Not used... // Event static int64_t eventTime = 0; @@ -616,13 +617,12 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.action = MOVE; // Points - gestureEvent.pointCount = event->numTouches; + gestureEvent.pointCount = touchEvent->numTouches; // Position gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY }; gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY }; - ProcessMotionEvent(gestureEvent); return 1; diff --git a/src/raylib.h b/src/raylib.h index 962376a9..ad1df97a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -437,7 +437,7 @@ void HideCursor(void); // Hides cursor bool IsCursorHidden(void); // Returns true if cursor is not visible #endif -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once diff --git a/src/raymath.c b/src/raymath.c index 8ad50312..9763b075 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -920,13 +920,13 @@ Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - if (abs(cosHalfTheta) >= 1.0f) result = q1; + if (fabs(cosHalfTheta) >= 1.0f) result = q1; else { float halfTheta = acos(cosHalfTheta); float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); - if (abs(sinHalfTheta) < 0.001f) + if (fabs(sinHalfTheta) < 0.001f) { result.x = (q1.x*0.5f + q2.x*0.5f); result.y = (q1.y*0.5f + q2.y*0.5f); @@ -1072,7 +1072,7 @@ Matrix QuaternionToMatrix(Quaternion q) // Returns the axis and the angle for a given quaternion void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) { - if (abs(q.w) > 1.0f) QuaternionNormalize(&q); + if (fabs(q.w) > 1.0f) QuaternionNormalize(&q); Vector3 resAxis = { 0, 0, 0 }; float resAngle = 0; diff --git a/src/rlgl.c b/src/rlgl.c index cedad6a7..a5c40815 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1411,12 +1411,14 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) //glGetIntegerv(GL_VIEWPORT, viewport); // Viewport data +/* int x = 0; int y = 0; int width = GetScreenWidth(); int height = GetScreenHeight(); float minDepth = 0.0f; float maxDepth = 1.0f; +*/ /* Matrix modelviewprojection = MatrixMultiply(modelview, projection); MatrixInvert(&modelviewprojection); diff --git a/src/shapes.c b/src/shapes.c index d88845cc..6e6c9dd7 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -395,8 +395,8 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) { bool collision = false; - float dx = abs((rec.x + rec.width/2) - center.x); - float dy = abs((rec.y + rec.height/2) - center.y); + float dx = fabs((rec.x + rec.width/2) - center.x); + float dy = fabs((rec.y + rec.height/2) - center.y); if ((dx <= (rec.width/2 + radius)) && (dy <= (rec.height/2 + radius))) collision = true; -- cgit v1.2.3 From 05fe1c22eddab069a4cf3dcb427f1c57a56df6d5 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 22 Apr 2015 18:37:09 +0200 Subject: Updated to latest version --- src/stb_rect_pack.h | 20 ++++- src/stb_truetype.h | 242 ++++++++++++++++++++++++++++------------------------ 2 files changed, 148 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/stb_rect_pack.h b/src/stb_rect_pack.h index dcc9d887..63a5b159 100644 --- a/src/stb_rect_pack.h +++ b/src/stb_rect_pack.h @@ -1,4 +1,4 @@ -// stb_rect_pack.h - v0.05 - public domain - rectangle packing +// stb_rect_pack.h - v0.06 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. @@ -13,6 +13,7 @@ // More docs to come. // // No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. // // This library currently uses the Skyline Bottom-Left algorithm. // @@ -20,8 +21,18 @@ // implement them to the same API, but with a different init // function. // +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// Bugfixes / warning fixes +// [your name could be here] +// // Version history: // +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort // 0.05: added STBRP_ASSERT to allow replacing assert // 0.04: fixed minor bug in STBRP_LARGE_RECTS support // 0.01: initial release @@ -169,7 +180,10 @@ struct stbrp_context // #ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT #include +#define STBRP_SORT qsort +#endif #ifndef STBRP_ASSERT #include @@ -524,7 +538,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n } // sort according to heuristic - qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare); + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); for (i=0; i < num_rects; ++i) { stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); @@ -537,7 +551,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n } // unsort - qsort(rects, num_rects, sizeof(rects[0]), rect_original_order); + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); // set was_packed flags for (i=0; i < num_rects; ++i) diff --git a/src/stb_truetype.h b/src/stb_truetype.h index 56ef47b7..ae60212e 100644 --- a/src/stb_truetype.h +++ b/src/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.02 - public domain +// stb_truetype.h - v1.05 - public domain // authored from 2009-2014 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -34,12 +34,20 @@ // Johan Duparc // Hou Qiming // Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle // // Misc other: // Ryan Gordon // // VERSION HISTORY // +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match // non-oversampled; STBTT_POINT_SIZE for packed case only @@ -83,6 +91,9 @@ // before the #include of this file. This expands out the actual // implementation into that C/C++ file. // +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// // Simple 3D API (don't ship this, but it's fine for tools and quick start) // stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture // stbtt_GetBakedQuad() -- compute quad to draw for a given char @@ -222,16 +233,16 @@ #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation #include "stb_truetype.h" -char ttf_buffer[1<<20]; +unsigned char ttf_buffer[1<<20]; unsigned char temp_bitmap[512*512]; stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs -GLstbtt_uint ftex; +GLuint ftex; void my_stbtt_initfont(void) { fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); - stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! // can free ttf_buffer at this point glGenTextures(1, &ftex); glBindTexture(GL_TEXTURE_2D, ftex); @@ -243,6 +254,7 @@ void my_stbtt_initfont(void) void my_stbtt_print(float x, float y, char *text) { // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, ftex); glBegin(GL_QUADS); while (*text) { @@ -428,6 +440,12 @@ int main(int arg, char **argv) #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ #define __STB_INCLUDE_STB_TRUETYPE_H__ +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + #ifdef __cplusplus extern "C" { #endif @@ -445,7 +463,7 @@ typedef struct float xoff,yoff,xadvance; } stbtt_bakedchar; -extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake @@ -461,7 +479,7 @@ typedef struct float x1,y1,s1,t1; // bottom-right } stbtt_aligned_quad; -extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space stbtt_aligned_quad *q, // output: quad to draw @@ -494,7 +512,7 @@ typedef struct typedef struct stbtt_pack_context stbtt_pack_context; -extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); // Initializes a packing context stored in the passed-in stbtt_pack_context. // Future calls using this context will pack characters into the bitmap passed // in here: a 1-channel bitmap that is weight x height. stride_in_bytes is @@ -505,12 +523,12 @@ extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int // // Returns 0 on failure, 1 on success. -extern void stbtt_PackEnd (stbtt_pack_context *spc); +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); // Cleans up the packing context and frees all memory. #define STBTT_POINT_SIZE(x) (-(x)) -extern int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); // Creates character bitmaps from the font_index'th font found in fontdata (use // font_index=0 if you don't know what that is). It creates num_chars_in_range @@ -533,13 +551,13 @@ typedef struct stbtt_packedchar *chardata_for_range; // output } stbtt_pack_range; -extern int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. -extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); // Oversampling a font increases the quality by allowing higher-quality subpixel // positioning, and is especially valuable at smaller text sizes. // @@ -551,7 +569,7 @@ extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_ov // oversampled textures with bilinear filtering. Look at the readme in // stb/tests/oversample for information about oversampled fonts -extern void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space stbtt_aligned_quad *q, // output: quad to draw @@ -577,7 +595,7 @@ struct stbtt_pack_context { // // -extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // Each .ttf/.ttc file may have more than one font. Each font has a sequential // index number starting from 0. Call this function to get the font offset for // a given index; it returns -1 if the index is out of range. A regular .ttf @@ -601,7 +619,7 @@ typedef struct stbtt_fontinfo int indexToLocFormat; // format needed to map from glyph index to glyph } stbtt_fontinfo; -extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); // Given an offset into the file that defines a font, this function builds // the necessary cached info for the rest of the system. You must allocate // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't @@ -613,7 +631,7 @@ extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int o // // CHARACTER TO GLYPH-INDEX CONVERSIOn -int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); // If you're going to perform multiple operations on the same character // and you want a speed-up, call this function with the character you're // going to process, then use glyph-based functions instead of the @@ -625,7 +643,7 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); // CHARACTER PROPERTIES // -extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); // computes a scale factor to produce a font whose "height" is 'pixels' tall. // Height is measured as the distance from the highest ascender to the lowest // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics @@ -633,12 +651,12 @@ extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels) // scale = pixels / (ascent - descent) // so if you prefer to measure height by the ascent only, use a similar calculation. -extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); // computes a scale factor to produce a font whose EM size is mapped to // 'pixels' tall. This is probably what traditional APIs compute, but // I'm not positive. -extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); // ascent is the coordinate above the baseline the font extends; descent // is the coordinate below the baseline the font extends (i.e. it is typically negative) // lineGap is the spacing between one row's descent and the next row's ascent... @@ -646,23 +664,23 @@ extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int * // these are expressed in unscaled coordinates, so you must multiply by // the scale factor for a given size -extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); // the bounding box around all possible characters -extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); // leftSideBearing is the offset from the current horizontal position to the left edge of the character // advanceWidth is the offset from the current horizontal position to the next horizontal position // these are expressed in unscaled coordinates -extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); // an additional amount to add to the 'advance' value between ch1 and ch2 -extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); // Gets the bounding box of the visible part of the glyph, in unscaled coordinates -extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); // as above, but takes one or more glyph indices for greater efficiency @@ -690,11 +708,11 @@ extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int * } stbtt_vertex; #endif -extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); // returns non-zero if nothing is drawn for this glyph -extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); // returns # of vertices and fills *vertices with the pointer to them // these are expressed in "unscaled" coordinates // @@ -705,7 +723,7 @@ extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbt // draws a quadratic bezier from previous endpoint to // its x,y, using cx,cy as the bezier control point. -extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // frees the data allocated above ////////////////////////////////////////////////////////////////////////////// @@ -713,10 +731,10 @@ extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // BITMAP RENDERING // -extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); // frees the bitmap allocated below -extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); // allocates a large-enough single-channel 8bpp bitmap and renders the // specified character/glyph at the specified scale into it, with // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). @@ -725,39 +743,39 @@ extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float // // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap -extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel // shift for the character -extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the // width and height and positioning info for it first. -extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel // shift for the character -extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); // get the bbox of the bitmap centered around the glyph origin; so the // bitmap width is ix1-ix0, height is iy1-iy0, and location to place // the bitmap top left is (leftSideBearing*scale,iy0). // (Note that the bitmap uses y-increases-down, but the shape uses // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) -extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel // shift for the character // the following functions are equivalent to the above functions, but operate // on glyph indices instead of Unicode codepoints (for efficiency) -extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); // @TODO: don't expose this structure @@ -767,7 +785,7 @@ typedef struct unsigned char *pixels; } stbtt__bitmap; -extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); ////////////////////////////////////////////////////////////////////////////// // @@ -791,7 +809,7 @@ extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stb // You have to have called stbtt_InitFont() first. -extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); // returns the offset (not index) of the font that matches, or -1 if none // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". // if you use any other flag, use a font name like "Arial"; this checks @@ -802,11 +820,11 @@ extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *nam #define STBTT_MACSTYLE_UNDERSCORE 4 #define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 -extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); // returns 1/0 whether the first string interpreted as utf8 is identical to // the second string interpreted as big-endian utf16... useful for strings from next func -extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); // returns the string (which may be big-endian double byte, e.g. for unicode) // and puts the length in bytes in *length. // @@ -905,10 +923,10 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #else - stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } #endif @@ -939,7 +957,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, return 0; } -int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) { // if it's just a font, there's only one valid index if (stbtt__isfont(font_collection)) @@ -958,7 +976,7 @@ int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) return -1; } -int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) { stbtt_uint8 *data = (stbtt_uint8 *) data2; stbtt_uint32 cmap, t; @@ -1015,7 +1033,7 @@ int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontsta return 1; } -int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) { stbtt_uint8 *data = info->data; stbtt_uint32 index_map = info->index_map; @@ -1040,7 +1058,6 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - stbtt_uint16 item, offset, start, end; // do a binary search of the segments stbtt_uint32 endCount = index_map + 14; @@ -1057,8 +1074,8 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) // now decrement to bias correctly to find smallest search -= 2; while (entrySelector) { + stbtt_uint16 end; searchRange >>= 1; - start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); end = ttUSHORT(data + search + searchRange*2); if (unicode_codepoint > end) search += searchRange*2; @@ -1066,19 +1083,21 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) } search += 2; - item = (stbtt_uint16) ((search - endCount) >> 1); + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - end = ttUSHORT(data + index_map + 14 + 2 + 2*item); - if (unicode_codepoint < start) - return 0; + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } } else if (format == 12 || format == 13) { stbtt_uint32 ngroups = ttULONG(data+index_map+12); stbtt_int32 low,high; @@ -1107,7 +1126,7 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) return 0; } -int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) { return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); } @@ -1139,7 +1158,7 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) return g1==g2 ? -1 : g1; // if length is 0, return -1 } -int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { int g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 0; @@ -1151,12 +1170,12 @@ int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int return 1; } -int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) { return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); } -int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; int g = stbtt__GetGlyfOffset(info, glyph_index); @@ -1181,7 +1200,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_ return num_vertices; } -int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; @@ -1407,7 +1426,7 @@ int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_verte return num_vertices; } -void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) { stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); if (glyph_index < numOfLongHorMetrics) { @@ -1419,7 +1438,7 @@ void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *ad } } -int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; stbtt_uint32 needle, straw; @@ -1449,26 +1468,26 @@ int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph return 0; } -int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) { if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs return 0; return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); } -void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) { stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); } -void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) { if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); if (descent) *descent = ttSHORT(info->data+info->hhea + 6); if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); } -void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) { *x0 = ttSHORT(info->data + info->head + 36); *y0 = ttSHORT(info->data + info->head + 38); @@ -1476,19 +1495,19 @@ void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *y1 = ttSHORT(info->data + info->head + 42); } -float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) { int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); return (float) height / fheight; } -float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) { int unitsPerEm = ttUSHORT(info->data + info->head + 18); return pixels / unitsPerEm; } -void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) { STBTT_free(v, info->userdata); } @@ -1498,7 +1517,7 @@ void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) // antialiasing software rasterizer // -void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { int x0,y0,x1,y1; if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { @@ -1516,17 +1535,17 @@ void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, floa } } -void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); } -void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); } -void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); } @@ -1807,7 +1826,7 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x } // returns number of contours -stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) { stbtt__point *points=0; int num_points=0; @@ -1876,7 +1895,7 @@ error: return NULL; } -void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) { float scale = scale_x > scale_y ? scale_y : scale_x; int winding_count, *winding_lengths; @@ -1888,12 +1907,12 @@ void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vert } } -void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) { STBTT_free(bitmap, userdata); } -unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; @@ -1930,12 +1949,12 @@ unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float sc return gbm.pixels; } -unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); } -void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) { int ix0,iy0; stbtt_vertex *vertices; @@ -1954,27 +1973,27 @@ void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *ou STBTT_free(vertices, info->userdata); } -void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); } -unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); } -void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); } -unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); } -void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); } @@ -1985,7 +2004,7 @@ void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output // // This is SUPER-CRAPPY packing to keep source code small -extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake @@ -2030,13 +2049,13 @@ extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font return bottom_y; } -void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) { float d3d_bias = opengl_fillrule ? 0 : -0.5f; float ipw = 1.0f / pw, iph = 1.0f / ph; stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = round_x + d3d_bias; q->y0 = round_y + d3d_bias; @@ -2133,7 +2152,7 @@ static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rect // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. -int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) { stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); int num_nodes = pw - padding; @@ -2163,13 +2182,13 @@ int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int return 1; } -void stbtt_PackEnd (stbtt_pack_context *spc) +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) { STBTT_free(spc->nodes , spc->user_allocator_context); STBTT_free(spc->pack_info, spc->user_allocator_context); } -void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) { STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); @@ -2189,7 +2208,7 @@ static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_i for (j=0; j < h; ++j) { int i; unsigned int total; - memset(buffer, 0, kernel_width); + STBTT_memset(buffer, 0, kernel_width); total = 0; @@ -2243,7 +2262,7 @@ static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_i for (j=0; j < w; ++j) { int i; unsigned int total; - memset(buffer, 0, kernel_width); + STBTT_memset(buffer, 0, kernel_width); total = 0; @@ -2301,7 +2320,7 @@ static float stbtt__oversample_shift(int oversample) return (float)-(oversample - 1) / (2.0f * (float)oversample); } -int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) { stbtt_fontinfo info; float recip_h = 1.0f / spc->h_oversample; @@ -2407,10 +2426,11 @@ int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int f } } + STBTT_free(rects, spc->user_allocator_context); return return_value; } -int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) { stbtt_pack_range range; @@ -2421,14 +2441,14 @@ int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int fo return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } -void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) { float ipw = 1.0f / pw, iph = 1.0f / ph; stbtt_packedchar *b = chardata + char_index; if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5); + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = x; q->y0 = y; q->x1 = x + b->xoff2 - b->xoff; @@ -2494,14 +2514,14 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 return i; } -int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); } // returns results in whatever encoding you request... but note that 2-byte encodings // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) { stbtt_int32 i,count,stringOffset; stbtt_uint8 *fc = font->data; @@ -2598,7 +2618,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam return 0; } -int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { -- cgit v1.2.3 From ba257b09f55f240ec48200383fe49ca4bf3e1cab Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 22 Apr 2015 19:12:59 +0200 Subject: Corrected input bug --- src/core.c | 2 +- src/raylib.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 81583bf3..d83c14f7 100644 --- a/src/core.c +++ b/src/core.c @@ -1052,7 +1052,7 @@ bool IsGamepadButtonUp(int gamepad, int button) } #endif -#if defined(PLATFORM_ANDROID) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) // Returns touch position X int GetTouchX(void) { diff --git a/src/raylib.h b/src/raylib.h index ad1df97a..e804da62 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -446,7 +446,7 @@ bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad b bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed #endif -#if defined(PLATFORM_ANDROID) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) int GetTouchX(void); // Returns touch position X int GetTouchY(void); // Returns touch position Y Vector2 GetTouchPosition(void); // Returns touch position XY -- cgit v1.2.3 From eae98e1c34512579d69966c99713bd0c45bfcb50 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 4 May 2015 23:46:31 +0200 Subject: Big batch of changes, check description: - Camera system moved to a separate module [camera.c] - WIP: Added customization functions for camera controls - Added custom shaders support on batch drawing - Complete redesign of textures module to support multiple texture formats (compressed and uncompressed) --- src/camera.c | 476 +++++++++++++++++++++++++++++++ src/core.c | 406 ++------------------------ src/gestures.c | 50 ++-- src/models.c | 116 ++++---- src/raylib.h | 75 +++-- src/rlgl.c | 158 +++++++---- src/rlgl.h | 2 + src/text.c | 37 ++- src/textures.c | 883 ++++++++++++++++++++++++++++++++++++--------------------- 9 files changed, 1331 insertions(+), 872 deletions(-) create mode 100644 src/camera.c (limited to 'src') diff --git a/src/camera.c b/src/camera.c new file mode 100644 index 00000000..b960afdf --- /dev/null +++ b/src/camera.c @@ -0,0 +1,476 @@ +/********************************************************************************************** +* +* raylib.camera +* +* Camera Modes Setup and Control Functions +* +* Copyright (c) 2015 Marc Palau and Ramon Santamaria +* +* 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. +* +**********************************************************************************************/ + +#include "raylib.h" +#include + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// CAMERA_GENERIC +#define CAMERA_SCROLL_SENSITIVITY 1.5 + +// FREE_CAMERA +#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 +#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3 +#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12 +#define FREE_CAMERA_MIN_CLAMP 85 +#define FREE_CAMERA_MAX_CLAMP -85 +#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 +#define FREE_CAMERA_PANNING_DIVIDER 5.1 + +// ORBITAL_CAMERA +#define ORBITAL_CAMERA_SPEED 0.01 + +// FIRST_PERSON +//#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003 +#define FIRST_PERSON_FOCUS_DISTANCE 25 +#define FIRST_PERSON_MIN_CLAMP 85 +#define FIRST_PERSON_MAX_CLAMP -85 + +#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0 +#define FIRST_PERSON_STEP_DIVIDER 30.0 +#define FIRST_PERSON_WAVING_DIVIDER 200.0 + +#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85 + +// THIRD_PERSON +//#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003 +#define THIRD_PERSON_DISTANCE_CLAMP 1.2 +#define THIRD_PERSON_MIN_CLAMP 5 +#define THIRD_PERSON_MAX_CLAMP -85 +#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 } + +// PLAYER (used by camera) +#define PLAYER_WIDTH 0.4 +#define PLAYER_HEIGHT 0.9 +#define PLAYER_DEPTH 0.4 +#define PLAYER_MOVEMENT_DIVIDER 20.0 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}}; +static Vector2 cameraAngle = { 0, 0 }; +static float cameraTargetDistance = 5; +static Vector3 resetingPosition = { 0, 0, 0 }; +static int resetingKey = 'Z'; +static Vector2 cameraMousePosition = { 0, 0 }; +static Vector2 cameraMouseVariation = { 0, 0 }; +static float mouseSensitivity = 0.003; +static int cameraMovementController[6] = { 'W', 'A', 'S', 'D', 'E', 'Q' }; +static int cameraMovementCounter = 0; +static bool cameraUseGravity = true; +static int pawnControllingKey = MOUSE_MIDDLE_BUTTON; +static int fnControllingKey = KEY_LEFT_ALT; +static int smoothZoomControllingKey = KEY_LEFT_CONTROL; + +static int cameraMode = CAMERA_CUSTOM; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void ProcessCamera(Camera *camera, Vector3 *playerPosition); +/* +static void SetCameraControls(int front, int left, int back, right, up, down); +static void SetMouseSensitivity(int sensitivity); +static void SetResetPosition(Vector3 resetPosition); +static void SetResetControl(int resetKey); +static void SetPawnControl(int pawnControlKey); +static void SetFnControl(int fnControlKey); +static void SetSmoothZoomControl(int smoothZoomControlKey); +*/ + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Select camera mode (multiple camera modes available) +void SetCameraMode(int mode) +{ + if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + cameraAngle.y = -40 * DEG2RAD; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL)) + { + cameraMode = CAMERA_THIRD_PERSON; + cameraTargetDistance = 5; + cameraAngle.y = -40 * DEG2RAD; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE)) + { + cameraTargetDistance = 10; + cameraAngle.x = 45 * DEG2RAD; + cameraAngle.y = -40 * DEG2RAD; + internalCamera.target = (Vector3){ 0, 0, 0}; + ProcessCamera(&internalCamera, &internalCamera.position); + } + else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL)) + { + cameraTargetDistance = 10; + cameraAngle.x = 225 * DEG2RAD; + cameraAngle.y = -40 * DEG2RAD; + internalCamera.target = (Vector3){ 3, 0, 3}; + ProcessCamera(&internalCamera, &internalCamera.position); + } + + cameraMode = mode; +} + +// Update camera with position +Camera UpdateCamera(Vector3 *position) +{ + // Calculate camera + if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position); + + return internalCamera; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Process desired camera mode and controls +static void ProcessCamera(Camera *camera, Vector3 *playerPosition) +{ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) + // Mouse movement detection + if (cameraMode != CAMERA_FREE) + { + HideCursor(); + + if (GetMousePosition().x < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetScreenWidth() - GetScreenHeight() / 3, GetMousePosition().y}); + else if (GetMousePosition().y < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() - GetScreenHeight() / 3}); + else if (GetMousePosition().x > GetScreenWidth() - GetScreenHeight() / 3) SetMousePosition((Vector2) { GetScreenHeight() / 3, GetMousePosition().y}); + else if (GetMousePosition().y > GetScreenHeight() - GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() / 3}); + else + { + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + } + else + { + ShowCursor(); + + cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; + cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; + } + + cameraMousePosition = GetMousePosition(); + + // Support for multiple automatic camera modes + switch (cameraMode) + { + case CAMERA_FREE: + { + // Camera zoom + if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP; + } + // Camera looking down + else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + + if (camera->target.y < 0) camera->target.y = -0.001; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; + } + // Camera looking up + else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) + { + camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; + + if (camera->target.y > 0) camera->target.y = 0.001; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0)) + { + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; + } + + // Inputs + if (IsKeyDown(fnControllingKey)) + { + if (IsKeyDown(smoothZoomControllingKey)) + { + // Camera smooth zoom + if (IsMouseButtonDown(pawnControllingKey)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY); + } + // Camera orientation calculation + else if (IsMouseButtonDown(pawnControllingKey)) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY; + cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY; + + // Angle clamp + if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD; + } + } + // Paning + else if (IsMouseButtonDown(pawnControllingKey)) + { + camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); + } + + // Focus to center + if (IsKeyDown(resetingKey)) camera->target = resetingPosition; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_ORBITAL: + { + cameraAngle.x += ORBITAL_CAMERA_SPEED; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Focus to center + if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + + } break; + case CAMERA_FIRST_PERSON: + case CAMERA_THIRD_PERSON: + { + bool isMoving = false; + + // Keyboard inputs + if (IsKeyDown(cameraMovementController[0])) + { + playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown(cameraMovementController[2])) + { + playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown(cameraMovementController[1])) + { + playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + else if (IsKeyDown(cameraMovementController[3])) + { + playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; + + isMoving = true; + } + + if (IsKeyDown(cameraMovementController[4])) + { + if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER; + } + else if (IsKeyDown(cameraMovementController[5])) + { + if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER; + } + + if (cameraMode == CAMERA_THIRD_PERSON) + { + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -mouseSensitivity; + cameraAngle.y += cameraMouseVariation.y * -mouseSensitivity; + + // Angle clamp + if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera zoom + cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; + + // Camera is always looking at player + camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); + camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; + camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); + + // Camera position update + camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; + + if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; + + camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; + } + else + { + if (isMoving) cameraMovementCounter++; + + // Camera orientation calculation + // Get the mouse sensitivity + cameraAngle.x += cameraMouseVariation.x * -mouseSensitivity; + cameraAngle.y += cameraMouseVariation.y * -mouseSensitivity; + + // Angle clamp + if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD; + else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD; + + // Camera is always looking at player + camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; + camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; + + camera->position.x = playerPosition->x; + camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; + camera->position.z = playerPosition->z; + + camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; + } + } break; + default: break; + } +#endif +} + +void SetCameraControls(int frontKey, int leftKey, int backKey, int rightKey, int upKey, int downKey) +{ + cameraMovementController[0] = frontKey; + cameraMovementController[1] = leftKey; + cameraMovementController[2] = backKey; + cameraMovementController[3] = rightKey; + cameraMovementController[4] = upKey; + cameraMovementController[5] = downKey; +} + +void SetMouseSensitivity(float sensitivity) +{ + mouseSensitivity = (sensitivity / 10000.0); +} + +void SetResetPosition(Vector3 resetPosition) +{ + resetingPosition = resetPosition; +} + +void SetResetControl(int resetKey) +{ + resetingKey = resetKey; +} + +void SetPawnControl(int pawnControlKey) +{ + pawnControllingKey = pawnControlKey; +} + +void SetFnControl(int fnControlKey) +{ + fnControllingKey = fnControlKey; +} + +void SetSmoothZoomControl(int smoothZoomControlKey) +{ + smoothZoomControllingKey = smoothZoomControlKey; +} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core.c b/src/core.c index d83c14f7..384e043a 100644 --- a/src/core.c +++ b/src/core.c @@ -45,7 +45,7 @@ #include // Standard input / output lib #include // Declares malloc() and free() for memory management, rand(), atexit() #include // Required for typedef unsigned long long int uint64_t, used by hi-res timer -#include // Useful to initialize random seed - Android/RPI hi-res timer +#include // Useful to initialize random seed - Android/RPI hi-res timer (NOTE: Linux only!) #include // Math related functions, tan() used to set perspective #include // String function definitions, memset() #include // Macros for reporting and retrieving error conditions through error codes @@ -99,48 +99,6 @@ //---------------------------------------------------------------------------------- #define MAX_TOUCH_POINTS 256 -// Camera System configuration -//---------------------------------------------------------------------------------- -// CAMERA_GENERIC -#define CAMERA_SCROLL_SENSITIVITY 1.5 - -// FREE_CAMERA -#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 -#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3 -#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12 -#define FREE_CAMERA_MIN_CLAMP 85 -#define FREE_CAMERA_MAX_CLAMP -85 -#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 -#define FREE_CAMERA_PANNING_DIVIDER 5.1 - -// ORBITAL_CAMERA -#define ORBITAL_CAMERA_SPEED 0.01 - -// FIRST_PERSON -#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003 -#define FIRST_PERSON_FOCUS_DISTANCE 25 -#define FIRST_PERSON_MIN_CLAMP 85 -#define FIRST_PERSON_MAX_CLAMP -85 - -#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0 -#define FIRST_PERSON_STEP_DIVIDER 30.0 -#define FIRST_PERSON_WAVING_DIVIDER 200.0 - -#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85 - -// THIRD_PERSON -#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003 -#define THIRD_PERSON_DISTANCE_CLAMP 1.2 -#define THIRD_PERSON_MIN_CLAMP 5 -#define THIRD_PERSON_MAX_CLAMP -85 -#define THIRD_PERSON_OFFSET (Vector3){ 0.4, 0, 0 } - -// PLAYER (used by camera) -#define PLAYER_WIDTH 0.4 -#define PLAYER_HEIGHT 0.9 -#define PLAYER_DEPTH 0.4 -#define PLAYER_MOVEMENT_DIVIDER 20.0 - //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -207,7 +165,6 @@ static bool cursorOnScreen = false; // Tracks if cursor is inside client static Texture2D cursor; // Cursor texture static Vector2 mousePosition; -static bool cursorHidden; static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once @@ -225,6 +182,8 @@ static int exitKey = KEY_ESCAPE; // Default exit key (ESC) static int lastKeyPressed = -1; #endif +static bool cursorHidden; + static double currentTime, previousTime; // Used to track timmings static double updateTime, drawTime; // Time measures for update and draw static double frameTime; // Time measure for one frame @@ -233,17 +192,6 @@ static double targetTime = 0.0; // Desired time for one frame, if 0 static char configFlags = 0; static bool showLogo = false; -// Camera variables -static int cameraMode = CAMERA_CUSTOM; -static Camera currentCamera; -static Camera internalCamera = {{2,0,2},{0,0,0},{0,1,0}}; -static Vector2 cameraAngle = { 0, 0 }; -static float cameraTargetDistance = 5; -static Vector2 cameraMousePosition = { 0, 0 }; -static Vector2 cameraMouseVariation = { 0, 0 }; -static int cameraMovementCounter = 0; -static bool cameraUseGravity = true; - // Shaders variables static bool enabledPostpro = false; @@ -306,8 +254,6 @@ static void TakeScreenshot(void); static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands #endif -static void ProcessCamera(Camera *camera, Vector3 *playerPosition); - //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- @@ -600,10 +546,7 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (MODELVIEW) // Setup Camera view - if (cameraMode == CAMERA_CUSTOM) currentCamera = camera; - else currentCamera = internalCamera; - - Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + Matrix view = MatrixLookAt(camera.position, camera.target, camera.up); rlMultMatrixf(GetMatrixVector(view)); // Multiply MODELVIEW matrix by view matrix (camera) } @@ -707,7 +650,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) Ray ray; Matrix proj = MatrixIdentity(); - Matrix view = MatrixLookAt(currentCamera.position, currentCamera.target, currentCamera.up); + Matrix view = MatrixLookAt(camera.position, camera.target, camera.up); float aspect = (GLfloat)GetScreenWidth()/(GLfloat)GetScreenHeight(); double top = 0.1f*tanf(45.0f*PI / 360.0f); @@ -725,7 +668,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) Vector3 nearPoint = { mousePosition.x, realy, 0.0f }; Vector3 farPoint = { mousePosition.x, realy, 1.0f }; - nearPoint = internalCamera.position; + //nearPoint = internalCamera.position; farPoint = rlglUnproject(farPoint, proj, view); Vector3 direction = VectorSubtract(farPoint, nearPoint); @@ -734,72 +677,9 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) ray.position = nearPoint; ray.direction = direction; - // Test - Vector2 screenPos; - screenPos.x = (mousePosition.x / (float)GetScreenWidth() * 2.0) - 1.0f; - screenPos.y = (mousePosition.y / (float)GetScreenHeight() * 2.0) - 1.0f; - - direction = VectorSubtract(internalCamera.target, internalCamera.position); - - printf("/nScreenPos %f, %f", screenPos.x, screenPos.y); - - Matrix rotate; - rotate = MatrixIdentity(); - rotate = MatrixRotate(0, 45*DEG2RAD*screenPos.y, 0); - VectorTransform(&direction, rotate); - - VectorNormalize(&direction); - - ray.position = internalCamera.position; - ray.direction = direction; - return ray; } -void SetCameraMode(int mode) -{ - if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE)) - { - cameraMode = CAMERA_THIRD_PERSON; - cameraTargetDistance = 5; - cameraAngle.y = -40 * DEG2RAD; - ProcessCamera(&internalCamera, &internalCamera.position); - } - else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL)) - { - cameraMode = CAMERA_THIRD_PERSON; - cameraTargetDistance = 5; - cameraAngle.y = -40 * DEG2RAD; - ProcessCamera(&internalCamera, &internalCamera.position); - } - else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE)) - { - cameraTargetDistance = 10; - cameraAngle.x = 45 * DEG2RAD; - cameraAngle.y = -40 * DEG2RAD; - internalCamera.target = (Vector3){ 0, 0, 0}; - ProcessCamera(&internalCamera, &internalCamera.position); - } - else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL)) - { - cameraTargetDistance = 10; - cameraAngle.x = 225 * DEG2RAD; - cameraAngle.y = -40 * DEG2RAD; - internalCamera.target = (Vector3){ 3, 0, 3}; - ProcessCamera(&internalCamera, &internalCamera.position); - } - - cameraMode = mode; -} - -Camera UpdateCamera(Vector3 *position) -{ - // Calculate camera - if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position); - - return internalCamera; -} - //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- @@ -915,8 +795,8 @@ int GetMouseWheelMove(void) { return previousMouseWheelY; } -#endif +// Hide mouse cursor void HideCursor() { #if defined(PLATFORM_DESKTOP) @@ -936,6 +816,7 @@ void HideCursor() cursorHidden = true; } +// Show mouse cursor void ShowCursor() { #if defined(PLATFORM_DESKTOP) @@ -948,10 +829,12 @@ void ShowCursor() cursorHidden = false; } +// Check if mouse cursor is hidden bool IsCursorHidden() { return cursorHidden; } +#endif // TODO: Enable gamepad usage on Rapsberry Pi // NOTE: emscripten not implemented @@ -1105,6 +988,18 @@ void SetPostproShader(Shader shader) } } +// Set custom shader to be used in batch draw +void SetCustomShader(Shader shader) +{ + rlglSetCustomShader(shader); +} + +// Set default shader to be used in batch draw +void SetDefaultShader(void) +{ + rlglSetDefaultShader(); +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2161,260 +2056,3 @@ static void LogoAnimation(void) showLogo = false; // Prevent for repeating when reloading window (Android) } - -// Process desired camera mode and controls -static void ProcessCamera(Camera *camera, Vector3 *playerPosition) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) - // Mouse movement detection - if (cameraMode != CAMERA_FREE) - { - HideCursor(); - if (GetMousePosition().x < GetScreenHeight() / 3) SetMousePosition((Vector2){ screenWidth - GetScreenHeight() / 3, GetMousePosition().y}); - else if (GetMousePosition().y < GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, screenHeight - GetScreenHeight() / 3}); - else if (GetMousePosition().x > screenWidth - GetScreenHeight() / 3) SetMousePosition((Vector2) { GetScreenHeight() / 3, GetMousePosition().y}); - else if (GetMousePosition().y > screenHeight - GetScreenHeight() / 3) SetMousePosition((Vector2){ GetMousePosition().x, GetScreenHeight() / 3}); - else - { - cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; - cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; - } - } - else - { - ShowCursor(); - cameraMouseVariation.x = GetMousePosition().x - cameraMousePosition.x; - cameraMouseVariation.y = GetMousePosition().y - cameraMousePosition.y; - } - - cameraMousePosition = GetMousePosition(); - - // Support for multiple automatic camera modes - switch (cameraMode) - { - case CAMERA_FREE: - { - // Pass to orbiting camera - if (IsKeyPressed('O')) cameraMode = CAMERA_ORBITAL; - - // Camera zoom - if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) - { - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - - if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP; - } - // Camera looking down - else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) - { - camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) - { - camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - - if (camera->target.y < 0) camera->target.y = -0.001; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (GetMouseWheelMove() > 0)) - { - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; - } - // Camera looking up - else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (GetMouseWheelMove() < 0)) - { - camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) - { - camera->target.x += GetMouseWheelMove() * (camera->target.x - camera->position.x) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.y += GetMouseWheelMove() * (camera->target.y - camera->position.y) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - camera->target.z += GetMouseWheelMove() * (camera->target.z - camera->position.z) * CAMERA_SCROLL_SENSITIVITY / cameraTargetDistance; - - if (camera->target.y > 0) camera->target.y = 0.001; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (GetMouseWheelMove() > 0)) - { - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP; - } - - - // Inputs - if (IsKeyDown(KEY_LEFT_ALT)) - { - if (IsKeyDown(KEY_LEFT_CONTROL)) - { - // Camera smooth zoom - if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) cameraTargetDistance += (cameraMouseVariation.y * FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY); - } - // Camera orientation calculation - else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) - { - // Camera orientation calculation - // Get the mouse sensitivity - cameraAngle.x += cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY; - cameraAngle.y += cameraMouseVariation.y * -FREE_CAMERA_MOUSE_SENSITIVITY; - - // Angle clamp - if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP * DEG2RAD; - else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP * DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP * DEG2RAD; - } - } - // Paning - else if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) - { - camera->target.x += ((cameraMouseVariation.x * -FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); - camera->target.y += ((cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); - camera->target.z += ((cameraMouseVariation.x * FREE_CAMERA_MOUSE_SENSITIVITY) * sin(cameraAngle.x) + (cameraMouseVariation.y * FREE_CAMERA_MOUSE_SENSITIVITY) * cos(cameraAngle.x) * sin(cameraAngle.y)) * (cameraTargetDistance / FREE_CAMERA_PANNING_DIVIDER); - } - - // Focus to center - if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; - - // Camera position update - camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - - if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - - camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; - - } break; - case CAMERA_ORBITAL: - { - // Pass to free camera - if (IsKeyPressed('O')) cameraMode = CAMERA_FREE; - - cameraAngle.x += ORBITAL_CAMERA_SPEED; - - // Camera zoom - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - // Camera distance clamp - if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; - - // Focus to center - if (IsKeyDown('Z')) camera->target = (Vector3) { 0, 0, 0 }; - - // Camera position update - camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - - if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - - camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; - - } break; - case CAMERA_FIRST_PERSON: - case CAMERA_THIRD_PERSON: - { - bool isMoving = false; - - // Keyboard inputs - if (IsKeyDown('W')) - { - playerPosition->x -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - playerPosition->z -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; - - isMoving = true; - } - else if (IsKeyDown('S')) - { - playerPosition->x += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - playerPosition->z += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y) / PLAYER_MOVEMENT_DIVIDER; - - isMoving = true; - } - - if (IsKeyDown('A')) - { - playerPosition->x -= cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - playerPosition->z += sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - - isMoving = true; - } - else if (IsKeyDown('D')) - { - playerPosition->x += cos(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - playerPosition->z -= sin(cameraAngle.x) / PLAYER_MOVEMENT_DIVIDER; - - isMoving = true; - } - - if (IsKeyDown('E')) - { - if (!cameraUseGravity) playerPosition->y += 1 / PLAYER_MOVEMENT_DIVIDER; - } - else if (IsKeyDown('Q')) - { - if (!cameraUseGravity) playerPosition->y -= 1 / PLAYER_MOVEMENT_DIVIDER; - } - - if (cameraMode == CAMERA_THIRD_PERSON) - { - // Camera orientation calculation - // Get the mouse sensitivity - cameraAngle.x += cameraMouseVariation.x * -THIRD_PERSON_MOUSE_SENSITIVITY; - cameraAngle.y += cameraMouseVariation.y * -THIRD_PERSON_MOUSE_SENSITIVITY; - - // Angle clamp - if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP * DEG2RAD; - else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP * DEG2RAD; - - // Camera zoom - cameraTargetDistance -= (GetMouseWheelMove() * CAMERA_SCROLL_SENSITIVITY); - - // Camera distance clamp - if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP; - - // Camera is always looking at player - camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x * cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x); - camera->target.y = playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y; - camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z * sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x * sin(cameraAngle.x); - - // Camera position update - camera->position.x = sin(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.x; - - if (cameraAngle.y <= 0) camera->position.y = sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - else camera->position.y = -sin(cameraAngle.y) * cameraTargetDistance * sin(cameraAngle.y) + camera->target.y; - - camera->position.z = cos(cameraAngle.x) * cameraTargetDistance * cos(cameraAngle.y) + camera->target.z; - } - else - { - if (isMoving) cameraMovementCounter++; - - // Camera orientation calculation - // Get the mouse sensitivity - cameraAngle.x += cameraMouseVariation.x * -FIRST_PERSON_MOUSE_SENSITIVITY; - cameraAngle.y += cameraMouseVariation.y * -FIRST_PERSON_MOUSE_SENSITIVITY; - - // Angle clamp - if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP * DEG2RAD; - else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP * DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP * DEG2RAD; - - // Camera is always looking at player - camera->target.x = camera->position.x - sin(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; - camera->target.y = camera->position.y + sin(cameraAngle.y) * FIRST_PERSON_FOCUS_DISTANCE; - camera->target.z = camera->position.z - cos(cameraAngle.x) * FIRST_PERSON_FOCUS_DISTANCE; - - camera->position.x = playerPosition->x; - camera->position.y = (playerPosition->y + PLAYER_HEIGHT * FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMovementCounter / FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / FIRST_PERSON_STEP_DIVIDER; - camera->position.z = playerPosition->z; - - camera->up.x = sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; - camera->up.z = -sin(cameraMovementCounter / (FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER * 2)) / FIRST_PERSON_WAVING_DIVIDER; - } - } break; - default: break; - } -#endif -} \ No newline at end of file diff --git a/src/gestures.c b/src/gestures.c index 00b57e4c..2574aa02 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -30,9 +30,14 @@ #include // malloc(), free() #include // printf(), fprintf() #include // Used for ... -#include // Used for clock functions #include // Defines int32_t, int64_t +#if defined(_WIN32) + //#include +#elif defined(__linux) + #include // Used for clock functions +#endif + #if defined(PLATFORM_ANDROID) #include // Java native interface #include // Android sensors functions @@ -75,15 +80,10 @@ typedef struct { // Global Variables Definition //---------------------------------------------------------------------------------- -// typedef static GestureType gestureType = TYPE_MOTIONLESS; - -// Gestures detection variables +static double eventTime = 0; //static int32_t touchId; // Not used... -// Event -static int64_t eventTime = 0; - // Tap // Our initial press position on tap static Vector2 initialTapPosition = { 0, 0 }; @@ -127,7 +127,7 @@ static float pinchDelta = 0; // Detected gesture static int currentGesture = GESTURE_NONE; -static float touchX, touchY; +static Vector2 touchPosition; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -141,7 +141,7 @@ static float OnPinch(); static void SetDualInput(GestureEvent event); static float Distance(Vector2 v1, Vector2 v2); static float DotProduct(Vector2 v1, Vector2 v2); -static int GetCurrentTime(); +static double GetCurrentTime(); #if defined(PLATFORM_WEB) static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); @@ -158,9 +158,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Returns tap position XY extern Vector2 GetRawPosition(void) { - Vector2 position = { touchX, touchY }; - - return position; + return touchPosition; } // Check if a gesture have been detected @@ -531,13 +529,27 @@ static float DotProduct(Vector2 v1, Vector2 v2) return result; } -static int GetCurrentTime() +static double GetCurrentTime() { +#if defined(_WIN32) +/* + // NOTE: Requires Windows.h + FILETIME tm; + GetSystemTimePreciseAsFileTime(&tm); + ULONGLONG nowTime = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; // Time provided in 100-nanosecond intervals + + return ((double)nowTime/10000000.0); // Return time in seconds +*/ +#endif + +#if defined(__linux) + // NOTE: Only for Linux-based systems struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time provided in nanoseconds - return nowTime / 1000000; // Return time in miliseconds + return ((double)nowTime/1000000.0); // Return time in miliseconds +#endif } #if defined(PLATFORM_ANDROID) @@ -545,23 +557,21 @@ static int GetCurrentTime() static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { int type = AInputEvent_getType(event); - //int32_t key = 0; if (type == AINPUT_EVENT_TYPE_MOTION) { - touchX = AMotionEvent_getX(event, 0); - touchY = AMotionEvent_getY(event, 0); + touchPosition.x = AMotionEvent_getX(event, 0); + touchPosition.y = AMotionEvent_getY(event, 0); } else if (type == AINPUT_EVENT_TYPE_KEY) { - //key = AKeyEvent_getKeyCode(event); + //int32_t key = AKeyEvent_getKeyCode(event); //int32_t AKeyEvent_getMetaState(event); } int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - GestureEvent gestureEvent; // Action @@ -609,6 +619,8 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent } */ + touchPosition = gestureEvent.position[0]; + GestureEvent gestureEvent; // Action diff --git a/src/models.c b/src/models.c index ffb077d4..be1b949a 100644 --- a/src/models.c +++ b/src/models.c @@ -599,6 +599,8 @@ Model LoadHeightmap(Image heightmap, float maxHeight) int mapX = heightmap.width; int mapZ = heightmap.height; + + Color *heightmapPixels = GetPixelData(heightmap); // NOTE: One vertex per pixel // TODO: Consider resolution when generating model data? @@ -628,15 +630,15 @@ Model LoadHeightmap(Image heightmap, float maxHeight) // one triangle - 3 vertex vData.vertices[vCounter] = x; - vData.vertices[vCounter + 1] = GetHeightValue(heightmap.pixels[x + z*mapX])*scaleFactor; + vData.vertices[vCounter + 1] = GetHeightValue(heightmapPixels[x + z*mapX])*scaleFactor; vData.vertices[vCounter + 2] = z; vData.vertices[vCounter + 3] = x; - vData.vertices[vCounter + 4] = GetHeightValue(heightmap.pixels[x + (z+1)*mapX])*scaleFactor; + vData.vertices[vCounter + 4] = GetHeightValue(heightmapPixels[x + (z+1)*mapX])*scaleFactor; vData.vertices[vCounter + 5] = z+1; vData.vertices[vCounter + 6] = x+1; - vData.vertices[vCounter + 7] = GetHeightValue(heightmap.pixels[(x+1) + z*mapX])*scaleFactor; + vData.vertices[vCounter + 7] = GetHeightValue(heightmapPixels[(x+1) + z*mapX])*scaleFactor; vData.vertices[vCounter + 8] = z; // another triangle - 3 vertex @@ -649,7 +651,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight) vData.vertices[vCounter + 14] = vData.vertices[vCounter + 5]; vData.vertices[vCounter + 15] = x+1; - vData.vertices[vCounter + 16] = GetHeightValue(heightmap.pixels[(x+1) + (z+1)*mapX])*scaleFactor; + vData.vertices[vCounter + 16] = GetHeightValue(heightmapPixels[(x+1) + (z+1)*mapX])*scaleFactor; vData.vertices[vCounter + 17] = z+1; vCounter += 18; // 6 vertex, 18 floats @@ -691,6 +693,8 @@ Model LoadHeightmap(Image heightmap, float maxHeight) trisCounter += 2; } } + + free(heightmapPixels); // Fill color data // NOTE: Not used any more... just one plain color defined at DrawModel() @@ -713,17 +717,19 @@ Model LoadHeightmap(Image heightmap, float maxHeight) } // Load a map image as a 3d model (cubes based) -Model LoadCubicmap(Image cubesmap) +Model LoadCubicmap(Image cubicmap) { VertexData vData; + Color *cubicmapPixels = GetPixelData(cubicmap); + // Map cube size will be 1.0 float mapCubeSide = 1.0f; - int mapWidth = cubesmap.width * (int)mapCubeSide; - int mapHeight = cubesmap.height * (int)mapCubeSide; + int mapWidth = cubicmap.width * (int)mapCubeSide; + int mapHeight = cubicmap.height * (int)mapCubeSide; // NOTE: Max possible number of triangles numCubes * (12 triangles by cube) - int maxTriangles = cubesmap.width*cubesmap.height*12; + int maxTriangles = cubicmap.width*cubicmap.height*12; int vCounter = 0; // Used to count vertices int tcCounter = 0; // Used to count texcoords @@ -775,9 +781,9 @@ Model LoadCubicmap(Image cubesmap) Vector3 v8 = { x + w/2, 0, z + h/2 }; // We check pixel color to be WHITE, we will full cubes - if ((cubesmap.pixels[z*cubesmap.width + x].r == 255) && - (cubesmap.pixels[z*cubesmap.width + x].g == 255) && - (cubesmap.pixels[z*cubesmap.width + x].b == 255)) + if ((cubicmapPixels[z*cubicmap.width + x].r == 255) && + (cubicmapPixels[z*cubicmap.width + x].g == 255) && + (cubicmapPixels[z*cubicmap.width + x].b == 255)) { // Define triangles (Checking Collateral Cubes!) //---------------------------------------------- @@ -832,10 +838,10 @@ Model LoadCubicmap(Image cubesmap) mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; tcCounter += 6; - if (((z < cubesmap.height - 1) && - (cubesmap.pixels[(z + 1)*cubesmap.width + x].r == 0) && - (cubesmap.pixels[(z + 1)*cubesmap.width + x].g == 0) && - (cubesmap.pixels[(z + 1)*cubesmap.width + x].b == 0)) || (z == cubesmap.height - 1)) + if (((z < cubicmap.height - 1) && + (cubicmapPixels[(z + 1)*cubicmap.width + x].r == 0) && + (cubicmapPixels[(z + 1)*cubicmap.width + x].g == 0) && + (cubicmapPixels[(z + 1)*cubicmap.width + x].b == 0)) || (z == cubicmap.height - 1)) { // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8 // NOTE: Collateral occluded faces are not generated @@ -865,9 +871,9 @@ Model LoadCubicmap(Image cubesmap) } if (((z > 0) && - (cubesmap.pixels[(z - 1)*cubesmap.width + x].r == 0) && - (cubesmap.pixels[(z - 1)*cubesmap.width + x].g == 0) && - (cubesmap.pixels[(z - 1)*cubesmap.width + x].b == 0)) || (z == 0)) + (cubicmapPixels[(z - 1)*cubicmap.width + x].r == 0) && + (cubicmapPixels[(z - 1)*cubicmap.width + x].g == 0) && + (cubicmapPixels[(z - 1)*cubicmap.width + x].b == 0)) || (z == 0)) { // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5 // NOTE: Collateral occluded faces are not generated @@ -896,10 +902,10 @@ Model LoadCubicmap(Image cubesmap) tcCounter += 6; } - if (((x < cubesmap.width - 1) && - (cubesmap.pixels[z*cubesmap.width + (x + 1)].r == 0) && - (cubesmap.pixels[z*cubesmap.width + (x + 1)].g == 0) && - (cubesmap.pixels[z*cubesmap.width + (x + 1)].b == 0)) || (x == cubesmap.width - 1)) + if (((x < cubicmap.width - 1) && + (cubicmapPixels[z*cubicmap.width + (x + 1)].r == 0) && + (cubicmapPixels[z*cubicmap.width + (x + 1)].g == 0) && + (cubicmapPixels[z*cubicmap.width + (x + 1)].b == 0)) || (x == cubicmap.width - 1)) { // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5 // NOTE: Collateral occluded faces are not generated @@ -929,9 +935,9 @@ Model LoadCubicmap(Image cubesmap) } if (((x > 0) && - (cubesmap.pixels[z*cubesmap.width + (x - 1)].r == 0) && - (cubesmap.pixels[z*cubesmap.width + (x - 1)].g == 0) && - (cubesmap.pixels[z*cubesmap.width + (x - 1)].b == 0)) || (x == 0)) + (cubicmapPixels[z*cubicmap.width + (x - 1)].r == 0) && + (cubicmapPixels[z*cubicmap.width + (x - 1)].g == 0) && + (cubicmapPixels[z*cubicmap.width + (x - 1)].b == 0)) || (x == 0)) { // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7 // NOTE: Collateral occluded faces are not generated @@ -961,9 +967,9 @@ Model LoadCubicmap(Image cubesmap) } } // We check pixel color to be BLACK, we will only draw floor and roof - else if ((cubesmap.pixels[z*cubesmap.width + x].r == 0) && - (cubesmap.pixels[z*cubesmap.width + x].g == 0) && - (cubesmap.pixels[z*cubesmap.width + x].b == 0)) + else if ((cubicmapPixels[z*cubicmap.width + x].r == 0) && + (cubicmapPixels[z*cubicmap.width + x].g == 0) && + (cubicmapPixels[z*cubicmap.width + x].b == 0)) { // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) mapVertices[vCounter] = v1; @@ -1065,6 +1071,8 @@ Model LoadCubicmap(Image cubesmap) free(mapVertices); free(mapNormals); free(mapTexcoords); + + free(cubicmapPixels); // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct @@ -1335,6 +1343,8 @@ bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSph // NOTE: player position (or camera) is modified inside this function Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius) { + Color *cubicmapPixels = GetPixelData(cubicmap); + // Detect the cell where the player is located Vector3 impactDirection = { 0, 0, 0 }; @@ -1347,8 +1357,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p // Multiple Axis -------------------------------------------------------------------------------------------- // Axis x-, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) @@ -1360,8 +1370,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x-, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) @@ -1373,8 +1383,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x+, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) @@ -1386,8 +1396,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x+, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) @@ -1401,7 +1411,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p // Single Axis --------------------------------------------------------------------------------------------------- // Axis x- - if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) + if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) { if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) { @@ -1410,7 +1420,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } } // Axis x+ - if (cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) + if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) { if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) { @@ -1419,7 +1429,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } } // Axis y- - if (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) + if (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) { if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius) { @@ -1428,7 +1438,7 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } } // Axis y+ - if (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) + if (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) { if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius) { @@ -1440,9 +1450,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p // Diagonals ------------------------------------------------------------------------------------------------------- // Axis x-, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) @@ -1460,9 +1470,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x-, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) @@ -1480,9 +1490,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x+, y- - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) @@ -1500,9 +1510,9 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p } // Axis x+, y+ - if ((cubicmap.pixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmap.pixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) @@ -1531,6 +1541,8 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p playerPosition->y = (1.5 - radius) - 0.01; impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z}; } + + free(cubicmapPixels); return impactDirection; } diff --git a/src/raylib.h b/src/raylib.h index e804da62..bd1692d2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -27,7 +27,6 @@ * * Some design decisions: * 32bit Colors - All defined color are always RGBA -* 32bit Textures - All loaded images are converted automatically to RGBA textures * SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures * One custom default font is loaded automatically when InitWindow() * If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined) @@ -78,7 +77,7 @@ // Some basic Defines //---------------------------------------------------------------------------------- #ifndef PI -#define PI 3.14159265358979323846 + #define PI 3.14159265358979323846 #endif #define DEG2RAD (PI / 180.0f) @@ -183,20 +182,6 @@ typedef enum { false, true } bool; #endif -typedef enum { - GESTURE_NONE = 0, - GESTURE_TAP, - GESTURE_DOUBLETAP, - GESTURE_HOLD, - GESTURE_DRAG, - GESTURE_SWIPE_RIGHT, - GESTURE_SWIPE_LEFT, - GESTURE_SWIPE_UP, - GESTURE_SWIPE_DOWN, - GESTURE_PINCH_IN, - GESTURE_PINCH_OUT -} Gestures; - // byte type typedef unsigned char byte; @@ -240,17 +225,21 @@ typedef struct Rectangle { // Image type, bpp always RGBA (32bit) // NOTE: Data stored in CPU memory (RAM) typedef struct Image { - Color *pixels; - int width; - int height; + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (TextureFormat) } Image; // Texture2D type, bpp always RGBA (32bit) // NOTE: Data stored in GPU memory typedef struct Texture2D { - unsigned int id; // OpenGL id - int width; - int height; + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (TextureFormat) } Texture2D; // Character type (one font glyph) @@ -337,9 +326,11 @@ typedef struct Wave { short channels; } Wave; -// Texture formats (support depends on OpenGL version) +// Texture formats +// NOTE: Support depends on OpenGL version and platform typedef enum { UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + UNCOMPRESSED_GRAY_ALPHA, // 16 bpp (2 channels) UNCOMPRESSED_R5G6B5, // 16 bpp UNCOMPRESSED_R8G8B8, // 24 bpp UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) @@ -354,9 +345,25 @@ typedef enum { COMPRESSED_ETC2_EAC_RGBA, // 8 bpp COMPRESSED_PVRT_RGB, // 4 bpp COMPRESSED_PVRT_RGBA, // 4 bpp - /*COMPRESSED_ASTC_RGBA_4x4*/ // 8 bpp + COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + COMPRESSED_ASTC_8x8_RGBA // 2 bpp } TextureFormat; +// Gestures type +typedef enum { + GESTURE_NONE = 0, + GESTURE_TAP, + GESTURE_DOUBLETAP, + GESTURE_HOLD, + GESTURE_DRAG, + GESTURE_SWIPE_RIGHT, + GESTURE_SWIPE_LEFT, + GESTURE_SWIPE_UP, + GESTURE_SWIPE_DOWN, + GESTURE_PINCH_IN, + GESTURE_PINCH_OUT +} Gestures; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -402,16 +409,27 @@ int GetHexValue(Color color); // Returns hexadecim int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -void SetCameraMode(int mode); // Multiple camera modes available -Camera UpdateCamera(Vector3 *position); // Update camera with position (when using internal camera) - void SetConfigFlags(char flags); // Enable some window configurations void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) void SetPostproShader(Shader shader); // Set fullscreen postproduction shader +void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw +void SetDefaultShader(void); // Set default shader to be used in batch draw Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Gives the rayTrace from mouse position +// Camera modes setup and control functions (module: camera) +void SetCameraMode(int mode); // Select camera mode (multiple camera modes available) +Camera UpdateCamera(Vector3 *position); // Update camera with position + +void SetCameraControls(int front, int left, int back, int right, int up, int down); +void SetMouseSensitivity(float sensitivity); +void SetResetPosition(Vector3 resetPosition); +void SetResetControl(int resetKey); +void SetPawnControl(int pawnControlKey); +void SetFnControl(int fnControlKey); +void SetSmoothZoomControl(int smoothZoomControlKey); + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ @@ -451,6 +469,7 @@ int GetTouchX(void); // Returns touch positio int GetTouchY(void); // Returns touch position Y Vector2 GetTouchPosition(void); // Returns touch position XY +// Gestures System (module: gestures) bool IsGestureDetected(void); int GetGestureType(void); float GetDragIntensity(void); @@ -504,6 +523,8 @@ Texture2D CreateTexture(Image image, bool genMipmaps); void UnloadImage(Image image); // Unload image from CPU memory (RAM) void UnloadTexture(Texture2D texture); // Unload texture from GPU memory void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) +Color *GetPixelData(Image image); // Get pixel data from image as a Color struct array +void SetPixelData(Image *image, Color *pixels, int format); // Set image data from Color struct array void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 diff --git a/src/rlgl.c b/src/rlgl.c index a5c40815..fbea84a2 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -143,6 +143,7 @@ typedef struct { typedef struct { GLuint textureId; int vertexCount; + // TODO: DrawState state -> Blending mode, shader } DrawCall; // pixel type (same as Color type) @@ -175,6 +176,7 @@ static VertexPositionColorTextureIndexBuffer quads; // Shader Programs static Shader defaultShader, simpleShader; +static Shader currentShader; // By default, defaultShader // Vertex Array Objects (VAO) static GLuint vaoLines, vaoTriangles, vaoQuads; @@ -330,14 +332,14 @@ void rlRotatef(float angleDeg, float x, float y, float z) Matrix rotation = MatrixIdentity(); // OPTION 1: It works... - //if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD); - //else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD); - //else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD); + if (x == 1) rotation = MatrixRotateX(angleDeg*DEG2RAD); + else if (y == 1) rotation = MatrixRotateY(angleDeg*DEG2RAD); + else if (z == 1) rotation = MatrixRotateZ(angleDeg*DEG2RAD); // OPTION 2: Requires review... - Vector3 axis = (Vector3){ x, y, z }; - VectorNormalize(&axis); - rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD); + //Vector3 axis = (Vector3){ x, y, z }; + //VectorNormalize(&axis); + //rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD); // OPTION 3: TODO: Review, it doesn't work! //Vector3 vec = (Vector3){ x, y, z }; @@ -921,6 +923,8 @@ void rlglInit(void) defaultShader = LoadDefaultShader(); simpleShader = LoadSimpleShader(); //customShader = rlglLoadShader("custom.vs", "custom.fs"); // Works ok + + currentShader = defaultShader; InitializeBuffers(); // Init vertex arrays InitializeBuffersGPU(); // Init VBO and VAO @@ -1094,6 +1098,7 @@ void rlglClose(void) #endif } +// Drawing batches: triangles, quads, lines void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -1101,11 +1106,11 @@ void rlglDraw(void) if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { - glUseProgram(defaultShader.id); + glUseProgram(currentShader.id); - glUniformMatrix4fv(defaultShader.projectionLoc, 1, false, GetMatrixVector(projection)); - glUniformMatrix4fv(defaultShader.modelviewLoc, 1, false, GetMatrixVector(modelview)); - glUniform1i(defaultShader.textureLoc, 0); + glUniformMatrix4fv(currentShader.projectionLoc, 1, false, GetMatrixVector(projection)); + glUniformMatrix4fv(currentShader.modelviewLoc, 1, false, GetMatrixVector(modelview)); + glUniform1i(currentShader.textureLoc, 0); } // NOTE: We draw in this order: triangle shapes, textured quads and lines @@ -1121,12 +1126,12 @@ void rlglDraw(void) else { glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); } glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); @@ -1149,16 +1154,16 @@ void rlglDraw(void) { // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); - glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultShader.texcoordLoc); + glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.texcoordLoc); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); } @@ -1206,12 +1211,12 @@ void rlglDraw(void) else { glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(defaultShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(defaultShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); } glDrawArrays(GL_LINES, 0, lines.vCounter); @@ -1310,7 +1315,7 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader glUniformMatrix4fv(model.shader.projectionLoc, 1, false, GetMatrixVector(projection)); glUniformMatrix4fv(model.shader.modelviewLoc, 1, false, GetMatrixVector(modelviewworld)); - glUniform1i(model.shader.textureLoc, 0); + glUniform1i(model.shader.textureLoc, 0); // Texture fits in texture unit 0 (Check glActiveTexture()) // Apply color tinting to model // NOTE: Just update one uniform on fragment shader @@ -1573,16 +1578,18 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma GLuint id; - // TODO: Review compressed textures support by OpenGL version - /* (rlGetVersion() == OPENGL_11) - if ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA) || - (textureFormat == COMPRESSED_ETC1_RGB8) || (textureFormat == COMPRESSED_ETC2_RGB8) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA8)) + // Check compressed textures support by OpenGL 1.1 + if (rlGetVersion() == OPENGL_11) { - id = 0; - TraceLog(WARNING, "GPU compressed textures not supported on OpenGL 1.1"); - return id; + if ((textureFormat == COMPRESSED_ETC1_RGB) || (textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA) || + (textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA) || + (textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA)) + { + id = 0; + TraceLog(WARNING, "Required GPU compressed texture format not supported"); + return id; + } } - */ glGenTextures(1, &id); // Generate Pointer to the texture @@ -1679,10 +1686,17 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // With swizzleMask we define how a one channel texture will be mapped to RGBA // Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, 1.0f }; + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - TraceLog(INFO, "Grayscale texture loaded and swizzled!"); + TraceLog(INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", id); + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); + + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } break; case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; @@ -1711,6 +1725,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma switch (textureFormat) { case UNCOMPRESSED_GRAYSCALE: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_GRAY_ALPHA: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; @@ -1724,7 +1739,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 //case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - default: TraceLog(WARNING, "Texture format not recognized"); break; + default: TraceLog(WARNING, "Texture format not supported"); break; } if ((mipmapCount == 1) && (genMipmaps)) @@ -2009,13 +2024,50 @@ void rlglSetModelShader(Model *model, Shader shader) #endif } +// Set custom shader to be used on batch draw +void rlglSetCustomShader(Shader shader) +{ + if (currentShader.id != shader.id) + { + rlglDraw(); + + currentShader = shader; +/* + if (vaoSupported) glBindVertexArray(vaoQuads); + + // Enable vertex attributes: position + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); + glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: texcoords + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); + glEnableVertexAttribArray(currentShader.texcoordLoc); + glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + + // Enable vertex attributes: colors + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); + glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO +*/ + } +} + +// Set default shader to be used on batch draw +void rlglSetDefaultShader(void) +{ + rlglSetCustomShader(defaultShader); +} + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -void PrintProjectionMatrix() +void PrintProjectionMatrix(void) { PrintMatrix(projection); } -void PrintModelviewMatrix() +void PrintModelviewMatrix(void) { PrintMatrix(modelview); } @@ -2292,7 +2344,7 @@ static void InitializeBuffers(void) } // Initialize Vertex Array Objects (Contain VBO) -// NOTE: lines, triangles and quads buffers use defaultShader +// NOTE: lines, triangles and quads buffers use currentShader static void InitializeBuffersGPU(void) { if (vaoSupported) @@ -2308,14 +2360,14 @@ static void InitializeBuffersGPU(void) // Lines - Vertex positions buffer binding and attributes enable glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.vertexLoc); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); // Lines - colors buffer glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.colorLoc); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines); else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]); @@ -2334,13 +2386,13 @@ static void InitializeBuffersGPU(void) // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.vertexLoc); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.colorLoc); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles); else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]); @@ -2359,18 +2411,18 @@ static void InitializeBuffersGPU(void) // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.vertexLoc); - glVertexAttribPointer(defaultShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.texcoordLoc); - glVertexAttribPointer(defaultShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.texcoordLoc); + glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(defaultShader.colorLoc); - glVertexAttribPointer(defaultShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); // Fill index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); diff --git a/src/rlgl.h b/src/rlgl.h index 3335fe16..41c13ef7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -216,6 +216,8 @@ void rlglInitPostpro(void); // Initialize postprocessing sys void rlglDrawPostpro(void); // Draw with postprocessing shader void rlglSetPostproShader(Shader shader); // Set postprocessing shader void rlglSetModelShader(Model *model, Shader shader); // Set shader for a model +void rlglSetCustomShader(Shader shader); // Set custom shader to be used on batch draw +void rlglSetDefaultShader(void); // Set default shader to be used on batch draw Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); diff --git a/src/text.c b/src/text.c index 7b3fc7d2..8e3fc4b9 100644 --- a/src/text.c +++ b/src/text.c @@ -87,6 +87,8 @@ extern void LoadDefaultFont(void) Image image; image.width = 128; // We know our default font image is 128 pixels width image.height = 128; // We know our default font image is 128 pixels height + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8A8; // Default font is directly defined here (data generated from a sprite font image) // This way, we reconstruct SpriteFont without creating large global variables @@ -149,9 +151,9 @@ extern void LoadDefaultFont(void) // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- - image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); + Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); - for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array + for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter @@ -160,7 +162,7 @@ extern void LoadDefaultFont(void) { for (int j = 31; j >= 0; j--) { - if (BIT_CHECK(defaultFontData[counter], j)) image.pixels[i+j] = WHITE; + if (BIT_CHECK(defaultFontData[counter], j)) imagePixels[i+j] = WHITE; } counter++; @@ -171,6 +173,10 @@ extern void LoadDefaultFont(void) //FILE *myimage = fopen("default_font.raw", "wb"); //fwrite(image.pixels, 1, 128*128*4, myimage); //fclose(myimage); + + SetPixelData(&image, imagePixels, 0); + + free(imagePixels); defaultFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture UnloadImage(image); @@ -232,14 +238,16 @@ SpriteFont LoadSpriteFont(const char *fileName) { Image image = LoadImage(fileName); - // At this point we have a pixel array with all the data... + // At this point we have a data array... + + Color *imagePixels = GetPixelData(image); #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) ConvertToPOT(&image, MAGENTA); #endif // Process bitmap Font pixel data to get measures (Character array) // spriteFont.charSet data is filled inside the function and memory is allocated! - int numChars = ParseImageData(image.pixels, image.width, image.height, &spriteFont.charSet); + int numChars = ParseImageData(imagePixels, image.width, image.height, &spriteFont.charSet); TraceLog(INFO, "[%s] SpriteFont data parsed correctly", fileName); TraceLog(INFO, "[%s] SpriteFont num chars detected: %i", fileName, numChars); @@ -248,6 +256,7 @@ SpriteFont LoadSpriteFont(const char *fileName) spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture + free(imagePixels); UnloadImage(image); } @@ -522,6 +531,8 @@ static SpriteFont LoadRBMF(const char *fileName) image.width = (int)rbmfHeader.imgWidth; image.height = (int)rbmfHeader.imgHeight; + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8A8; int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; @@ -535,9 +546,9 @@ static SpriteFont LoadRBMF(const char *fileName) // Re-construct image from rbmfFileData //----------------------------------------- - image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); + Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); - for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array + for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter @@ -546,11 +557,15 @@ static SpriteFont LoadRBMF(const char *fileName) { for (int j = 31; j >= 0; j--) { - if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE; + if (BIT_CHECK(rbmfFileData[counter], j)) imagePixels[i+j] = WHITE; } counter++; } + + SetPixelData(&image, imagePixels, 0); + + free(imagePixels); TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName); @@ -607,7 +622,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) Image image; image.width = 512; image.height = 512; - image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + //image.pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25); @@ -647,7 +662,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) free(ttfBuffer); // Now we have image data in tempBitmap and chardata filled... - +/* for (int i = 0; i < 512*512; i++) { image.pixels[i].r = tempBitmap[i]; @@ -655,7 +670,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) image.pixels[i].b = tempBitmap[i]; image.pixels[i].a = 255; } - +*/ free(tempBitmap); // REFERENCE EXAMPLE diff --git a/src/textures.c b/src/textures.c index 1d22e509..d2b2de1d 100644 --- a/src/textures.c +++ b/src/textures.c @@ -47,13 +47,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef struct { - unsigned char *data; // Image raw data - int width; // Image base width - int height; // Image base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format -} ImageEx; +// ... //---------------------------------------------------------------------------------- // Global Variables Definition @@ -63,14 +57,16 @@ typedef struct { //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) //---------------------------------------------------------------------------------- -//... +// ... //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static ImageEx LoadDDS(const char *fileName); // Load DDS file -static ImageEx LoadPKM(const char *fileName); // Load PKM file -static ImageEx LoadPVR(const char *fileName); // Load PVR file +static Image LoadDDS(const char *fileName); // Load DDS file +static Image LoadPKM(const char *fileName); // Load PKM file +static Image LoadKTX(const char *fileName); // Load KTX file +static Image LoadPVR(const char *fileName); // Load PVR file +static Image LoadASTC(const char *fileName); // Load ASTC file //---------------------------------------------------------------------------------- // Module Functions Definition @@ -81,10 +77,12 @@ Image LoadImage(const char *fileName) { Image image; - // Initial values - image.pixels = NULL; + // Initialize image default values + image.data = NULL; image.width = 0; image.height = 0; + image.mipmaps = 0; + image.format = 0; if ((strcmp(GetExtension(fileName),"png") == 0) || (strcmp(GetExtension(fileName),"bmp") == 0) || @@ -94,82 +92,39 @@ Image LoadImage(const char *fileName) (strcmp(GetExtension(fileName),"psd") == 0) || (strcmp(GetExtension(fileName),"pic") == 0)) { - int imgWidth; - int imgHeight; - int imgBpp; - + int imgWidth = 0; + int imgHeight = 0; + int imgBpp = 0; + // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...) - // Force loading to 4 components (RGBA) - byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); - - if (imgData != NULL) - { - // Convert array to pixel array for working convenience - image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color)); - - int pix = 0; - - for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4) - { - image.pixels[pix].r = imgData[i]; - image.pixels[pix].g = imgData[i+1]; - image.pixels[pix].b = imgData[i+2]; - image.pixels[pix].a = imgData[i+3]; - pix++; - } - - stbi_image_free(imgData); - - image.width = imgWidth; - image.height = imgHeight; - - TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height); - } - else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName); - } - else if (strcmp(GetExtension(fileName),"dds") == 0) - { - // NOTE: DDS uncompressed images can also be loaded (discarding mipmaps...) - - ImageEx imageDDS = LoadDDS(fileName); + image.data = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 0); - if (imageDDS.format == 0) - { - image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color)); - image.width = imageDDS.width; - image.height = imageDDS.height; - - int pix = 0; - - for (int i = 0; i < (image.width * image.height * 4); i += 4) - { - image.pixels[pix].r = imageDDS.data[i]; - image.pixels[pix].g = imageDDS.data[i+1]; - image.pixels[pix].b = imageDDS.data[i+2]; - image.pixels[pix].a = imageDDS.data[i+3]; - pix++; - } - - free(imageDDS.data); - - TraceLog(INFO, "[%s] DDS Image loaded successfully (uncompressed, no mipmaps)", fileName); - } - else TraceLog(WARNING, "[%s] DDS Compressed image data could not be loaded", fileName); + image.width = imgWidth; + image.height = imgHeight; + image.mipmaps = 1; + + if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE; + else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA; + else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8; + else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8; } - else if (strcmp(GetExtension(fileName),"pkm") == 0) - { - TraceLog(INFO, "[%s] PKM Compressed image data could not be loaded", fileName); + else if (strcmp(GetExtension(fileName),"dds") == 0) image = LoadDDS(fileName); + else if (strcmp(GetExtension(fileName),"pkm") == 0) image = LoadPKM(fileName); + else if (strcmp(GetExtension(fileName),"ktx") == 0) image = LoadKTX(fileName); + else if (strcmp(GetExtension(fileName),"pvr") == 0) image = LoadPVR(fileName); + else if (strcmp(GetExtension(fileName),"astc") == 0) image = LoadASTC(fileName); + + if (image.data != NULL) + { + TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height); } - else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName); - - // ALTERNATIVE: We can load pixel data directly into Color struct pixels array, - // to do that, struct data alignment should be the right one (4 byte); it is. - //image.pixels = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); + else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName); return image; } // Load an image from rRES file (raylib Resource) +// TODO: Review function to support multiple color modes Image LoadImageFromRES(const char *rresName, int resId) { Image image; @@ -232,28 +187,19 @@ Image LoadImageFromRES(const char *rresName, int resId) image.width = (int)imgWidth; image.height = (int)imgHeight; - unsigned char *data = malloc(infoHeader.size); + unsigned char *compData = malloc(infoHeader.size); - fread(data, infoHeader.size, 1, rresFile); + fread(compData, infoHeader.size, 1, rresFile); - unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize); + unsigned char *imgData = DecompressData(compData, infoHeader.size, infoHeader.srcSize); - image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight); + // TODO: Review color mode + //image.data = (unsigned char *)malloc(sizeof(unsigned char)*imgWidth*imgHeight*4); + image.data = imgData; - int pix = 0; + //free(imgData); - for (int i = 0; i < (imgWidth*imgHeight*4); i += 4) - { - image.pixels[pix].r = imgData[i]; - image.pixels[pix].g = imgData[i+1]; - image.pixels[pix].b = imgData[i+2]; - image.pixels[pix].a = imgData[i+3]; - pix++; - } - - free(imgData); - - free(data); + free(compData); TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height); } @@ -294,66 +240,14 @@ Texture2D LoadTexture(const char *fileName) { Texture2D texture; - // Init texture to default values - texture.id = 0; - texture.width = 0; - texture.height = 0; - - if (strcmp(GetExtension(fileName),"dds") == 0) - { - ImageEx image = LoadDDS(fileName); - - texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); - - texture.width = image.width; - texture.height = image.height; - - if (texture.id == 0) TraceLog(WARNING, "[%s] DDS texture could not be loaded", fileName); - else TraceLog(INFO, "[%s] DDS texture loaded successfully", fileName); - - free(image.data); - } - else if (strcmp(GetExtension(fileName),"pkm") == 0) - { - ImageEx image = LoadPKM(fileName); - - texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); - - texture.width = image.width; - texture.height = image.height; - - if (texture.id == 0) TraceLog(WARNING, "[%s] PKM texture could not be loaded", fileName); - else TraceLog(INFO, "[%s] PKM texture loaded successfully", fileName); - - free(image.data); - } - else if (strcmp(GetExtension(fileName),"pvr") == 0) - { - ImageEx image = LoadPVR(fileName); - - texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); - - texture.width = image.width; - texture.height = image.height; - - if (texture.id == 0) TraceLog(WARNING, "[%s] PVR texture could not be loaded", fileName); - else TraceLog(INFO, "[%s] PVR texture loaded successfully", fileName); - - free(image.data); - } - else - { - Image image = LoadImage(fileName); - - if (image.pixels != NULL) - { + Image image = LoadImage(fileName); + #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) - ConvertToPOT(&image, BLANK); + ConvertToPOT(&image, BLANK); #endif - texture = LoadTextureFromImage(image, false); - UnloadImage(image); - } - } + + texture = LoadTextureFromImage(image, false); + UnloadImage(image); return texture; } @@ -364,6 +258,8 @@ Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, in texture.width = width; texture.height = height; + texture.mipmaps = mipmapCount; + texture.format = textureFormat; texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount, genMipmaps); @@ -380,45 +276,15 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) texture.id = 0; texture.width = 0; texture.height = 0; - - if ((image.pixels != NULL) && (image.width > 0) && (image.height > 0)) - { - unsigned char *imgData = malloc(image.width * image.height * 4); - - int j = 0; - - for (int i = 0; i < image.width * image.height * 4; i += 4) - { - imgData[i] = image.pixels[j].r; - imgData[i+1] = image.pixels[j].g; - imgData[i+2] = image.pixels[j].b; - imgData[i+3] = image.pixels[j].a; - - j++; - } - - // NOTE: rlglLoadTexture() can generate mipmaps (POT image required) - texture.id = rlglLoadTexture(imgData, image.width, image.height, UNCOMPRESSED_R8G8B8A8, 1, genMipmaps); - - texture.width = image.width; - texture.height = image.height; - - free(imgData); - } - else TraceLog(WARNING, "Texture could not be loaded, image data is not valid"); - - return texture; -} - -// [DEPRECATED] Load a texture from image data -// NOTE: Use LoadTextureFromImage() instead -Texture2D CreateTexture(Image image, bool genMipmaps) -{ - Texture2D texture; - - texture = LoadTextureFromImage(image, genMipmaps); - - TraceLog(INFO, "Created texture id: %i", texture.id); + texture.mipmaps = 0; + texture.format = 0; + + texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); + + texture.width = image.width; + texture.height = image.height; + texture.mipmaps = image.mipmaps; + texture.format = image.format; return texture; } @@ -438,7 +304,7 @@ Texture2D LoadTextureFromRES(const char *rresName, int resId) // Unload image from CPU memory (RAM) void UnloadImage(Image image) { - free(image.pixels); + free(image.data); } // Unload texture from GPU memory @@ -451,6 +317,8 @@ void UnloadTexture(Texture2D texture) // NOTE: Requirement on OpenGL ES 2.0 (RPI, HTML5) void ConvertToPOT(Image *image, Color fillColor) { + // TODO: Review for new image struct + /* // Just add the required amount of pixels at the right and bottom sides of image... int potWidth = GetNextPOT(image->width); int potHeight = GetNextPOT(image->height); @@ -467,7 +335,7 @@ void ConvertToPOT(Image *image, Color fillColor) { for (int i = 0; i < potWidth; i++) { - if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->pixels[j*image->width + i]; + if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->data[j*image->width + i]; else imgDataPixelPOT[j*potWidth + i] = fillColor; } } @@ -480,6 +348,115 @@ void ConvertToPOT(Image *image, Color fillColor) image->width = potWidth; image->height = potHeight; } + */ +} + +// Get pixel data from image in the form of Color struct array +Color *GetPixelData(Image image) +{ + Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + + int k = 0; + + for (int i = 0; i < image.width*image.height; i++) + { + switch (image.format) + { + case UNCOMPRESSED_GRAYSCALE: + { + pixels[i].r = ((unsigned char *)image.data)[k]; + pixels[i].g = ((unsigned char *)image.data)[k]; + pixels[i].b = ((unsigned char *)image.data)[k]; + pixels[i].a = 255; + + k++; + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + pixels[i].r = ((unsigned char *)image.data)[k]; + pixels[i].g = ((unsigned char *)image.data)[k]; + pixels[i].b = ((unsigned char *)image.data)[k]; + pixels[i].a = ((unsigned char *)image.data)[k + 1]; + + k += 2; + } break; + case UNCOMPRESSED_R5G5B5A1: + { + unsigned short pixel = ((unsigned short *)image.data)[k]; + + pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31)); + pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31)); + pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255); + + k++; + } break; + case UNCOMPRESSED_R5G6B5: + { + unsigned short pixel = ((unsigned short *)image.data)[k]; + + pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63)); + pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31)); + pixels[i].a = 255; + + k++; + } break; + case UNCOMPRESSED_R4G4B4A4: + { + unsigned short pixel = ((unsigned short *)image.data)[k]; + + pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15)); + pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15)); + pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15)); + + k++; + } break; + case UNCOMPRESSED_R8G8B8A8: + { + pixels[i].r = ((unsigned char *)image.data)[k]; + pixels[i].g = ((unsigned char *)image.data)[k + 1]; + pixels[i].b = ((unsigned char *)image.data)[k + 2]; + pixels[i].a = ((unsigned char *)image.data)[k + 3]; + + k += 4; + } break; + case UNCOMPRESSED_R8G8B8: + { + pixels[i].r = (unsigned char)((unsigned char *)image.data)[k]; + pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1]; + pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2]; + pixels[i].a = 255; + + k += 3; + } break; + default: TraceLog(WARNING, "Format not supported for pixel data retrieval"); break; + } + } + + return pixels; +} + +// Fill image data with pixels Color data (RGBA - 32bit) +// NOTE: Pixels color array size must be coherent with image size +// TODO: Review to support different color modes (TextureFormat) +void SetPixelData(Image *image, Color *pixels, int format) +{ + free(image->data); + image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char)); + + int k = 0; + + for (int i = 0; i < image->width*image->height*4; i += 4) + { + ((unsigned char *)image->data)[i] = pixels[k].r; + ((unsigned char *)image->data)[i + 1] = pixels[k].g; + ((unsigned char *)image->data)[i + 2] = pixels[k].b; + ((unsigned char *)image->data)[i + 3] = pixels[k].a; + + k++; + } } // Draw a Texture2D @@ -554,9 +531,17 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V //---------------------------------------------------------------------------------- // Loading DDS image data (compressed or uncompressed) -// NOTE: Compressed data loading not supported on OpenGL 1.1 -static ImageEx LoadDDS(const char *fileName) +static Image LoadDDS(const char *fileName) { + // Required extension: + // GL_EXT_texture_compression_s3tc + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 + // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII @@ -569,7 +554,7 @@ static ImageEx LoadDDS(const char *fileName) unsigned int rgbBitCount; unsigned int rBitMask; unsigned int gBitMask; - unsigned int bitMask; + unsigned int bBitMask; unsigned int aBitMask; } ddsPixelFormat; @@ -581,7 +566,7 @@ static ImageEx LoadDDS(const char *fileName) unsigned int width; unsigned int pitchOrLinearSize; unsigned int depth; - unsigned int mipMapCount; + unsigned int mipmapCount; unsigned int reserved1[11]; ddsPixelFormat ddspf; unsigned int caps; @@ -591,8 +576,7 @@ static ImageEx LoadDDS(const char *fileName) unsigned int reserved2; } ddsHeader; - ImageEx image; - ddsHeader header; + Image image; image.data = NULL; image.width = 0; @@ -604,7 +588,7 @@ static ImageEx LoadDDS(const char *fileName) if (ddsFile == NULL) { - TraceLog(WARNING, "[%s] DDS image file could not be opened", fileName); + TraceLog(WARNING, "[%s] DDS file could not be opened", fileName); } else { @@ -616,11 +600,12 @@ static ImageEx LoadDDS(const char *fileName) if (strncmp(filecode, "DDS ", 4) != 0) { TraceLog(WARNING, "[%s] DDS file does not seem to be a valid image", fileName); - fclose(ddsFile); } else { - // Get the surface descriptor + ddsHeader header; + + // Get the image header fread(&header, sizeof(ddsHeader), 1, ddsFile); TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader)); @@ -630,63 +615,85 @@ static ImageEx LoadDDS(const char *fileName) image.width = header.width; image.height = header.height; + image.mipmaps = 1; // Default value, could be changed (header.mipmapCount) - if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed + if (header.ddspf.rgbBitCount == 16) // 16bit mode, no compressed { - image.data = (unsigned char *)malloc(header.width * header.height * 4); - unsigned char *buffer = (unsigned char *)malloc(header.width * header.height * 3); - - fread(buffer, image.width*image.height*3, 1, ddsFile); - - unsigned char *src = buffer; - unsigned char *dest = image.data; + if (header.ddspf.flags == 0x40) // no alpha channel + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); - for(int y = 0; y < image.height; y++) + image.format = UNCOMPRESSED_R5G6B5; + } + else if (header.ddspf.flags == 0x41) // with alpha channel { - for(int x = 0; x < image.width; x++) + if (header.ddspf.aBitMask == 0x8000) // 1bit alpha + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); + + unsigned char alpha = 0; + + // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1 + for (int i = 0; i < image.width*image.height; i++) + { + alpha = ((unsigned short *)image.data)[i] >> 15; + ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 1; + ((unsigned short *)image.data)[i] += alpha; + } + + image.format = UNCOMPRESSED_R5G5B5A1; + } + else if (header.ddspf.aBitMask == 0xf000) // 4bit alpha { - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; - *dest++ = 255; + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); + + unsigned char alpha = 0; + + // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4 + for (int i = 0; i < image.width*image.height; i++) + { + alpha = ((unsigned short *)image.data)[i] >> 12; + ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4; + ((unsigned short *)image.data)[i] += alpha; + } + + image.format = UNCOMPRESSED_R4G4B4A4; } } + } + if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed + { + // NOTE: not sure if this case exists... + image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); + fread(image.data, image.width*image.height*3, 1, ddsFile); - free(buffer); - - image.mipmaps = 1; image.format = UNCOMPRESSED_R8G8B8; } else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed { - image.data = (unsigned char *)malloc(header.width * header.height * 4); - + image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); fread(image.data, image.width*image.height*4, 1, ddsFile); - image.mipmaps = 1; image.format = UNCOMPRESSED_R8G8B8A8; } else if (((header.ddspf.flags == 0x04) || (header.ddspf.flags == 0x05)) && (header.ddspf.fourCC > 0)) { - //TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName); - //TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName); - int bufsize; // Calculate data size, including all mipmaps - if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2; + if (header.mipmapCount > 1) bufsize = header.pitchOrLinearSize*2; else bufsize = header.pitchOrLinearSize; TraceLog(DEBUG, "Pitch or linear size: %i", header.pitchOrLinearSize); - image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); + image.data = (unsigned char*)malloc(bufsize*sizeof(unsigned char)); fread(image.data, 1, bufsize, ddsFile); - // Close file pointer - fclose(ddsFile); - - image.mipmaps = header.mipMapCount; + image.mipmaps = header.mipmapCount; switch(header.ddspf.fourCC) { @@ -701,6 +708,8 @@ static ImageEx LoadDDS(const char *fileName) } } } + + fclose(ddsFile); // Close file pointer } return image; @@ -709,94 +718,195 @@ static ImageEx LoadDDS(const char *fileName) // Loading PKM image data (ETC1/ETC2 compression) // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) -// ETC1 compression support requires extension GL_OES_compressed_ETC1_RGB8_texture -static ImageEx LoadPKM(const char *fileName) +static Image LoadPKM(const char *fileName) { - // If OpenGL ES 2.0. the following format could be supported (ETC1): - //GL_ETC1_RGB8_OES - - // If OpenGL ES 3.0, the following formats are supported (ETC2/EAC): - //GL_COMPRESSED_RGB8_ETC2 - //GL_COMPRESSED_RGBA8_ETC2 - //GL_COMPRESSED_RG11_EAC - //... + // Required extensions: + // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0) + // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0) + + // Supported tokens (defined by extensions) + // GL_ETC1_RGB8_OES 0x8D64 + // GL_COMPRESSED_RGB8_ETC2 0x9274 + // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 // PKM file (ETC1) Header (16 bytes) typedef struct { char id[4]; // "PKM " - char version[2]; // "10" - unsigned short format; // Format = number of mipmaps = 0 (ETC1_RGB_NO_MIPMAPS) - unsigned short extWidth; // Texture width (big-endian) - unsigned short extHeight; // Texture height (big-endian) + char version[2]; // "10" or "20" + unsigned short format; // Data format (big-endian) (Check list below) + unsigned short width; // Texture width (big-endian) (origWidth rounded to multiple of 4) + unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4) unsigned short origWidth; // Original width (big-endian) unsigned short origHeight; // Original height (big-endian) } pkmHeader; + + // Formats list + // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used) + // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R // NOTE: The extended width and height are the widths rounded up to a multiple of 4. - // NOTE: ETC is always 4bit per pixel (64 bits for each 4x4 block of pixels) + // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) - // Bytes Swap (little-endian <-> big-endian) - //unsigned short data; - //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8); - - ImageEx image; - - unsigned short width; - unsigned short height; - unsigned short useless; + Image image; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; FILE *pkmFile = fopen(fileName, "rb"); if (pkmFile == NULL) { - TraceLog(WARNING, "[%s] PKM image file could not be opened", fileName); + TraceLog(WARNING, "[%s] PKM file could not be opened", fileName); } else { - // Verify the type of file - char filecode[4]; + pkmHeader header; - fread(filecode, 1, 4, pkmFile); + // Get the image header + fread(&header, sizeof(pkmHeader), 1, pkmFile); - if (strncmp(filecode, "PKM ", 4) != 0) + if (strncmp(header.id, "PKM ", 4) != 0) { TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName); - fclose(pkmFile); } else { - // Get the surface descriptor - fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard version - fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard format - - fread(&width, sizeof(unsigned short), 1, pkmFile); // Read extended width - fread(&height, sizeof(unsigned short), 1, pkmFile); // Read extended height - - int size = (width/4)*(height/4)*8; // Total data size in bytes + // NOTE: format, width and height come as big-endian, data must be swapped to little-endian + header.format = ((header.format & 0x00FF) << 8) | ((header.format & 0xFF00) >> 8); + header.width = ((header.width & 0x00FF) << 8) | ((header.width & 0xFF00) >> 8); + header.height = ((header.height & 0x00FF) << 8) | ((header.height & 0xFF00) >> 8); + + TraceLog(INFO, "PKM (ETC) image width: %i", header.width); + TraceLog(INFO, "PKM (ETC) image height: %i", header.height); + TraceLog(INFO, "PKM (ETC) image format: %i", header.format); + + image.width = header.width; + image.height = header.height; + image.mipmaps = 1; + + int size = image.width*image.height*4/8; // Total data size in bytes image.data = (unsigned char*)malloc(size * sizeof(unsigned char)); fread(image.data, 1, size, pkmFile); - fclose(pkmFile); // Close file pointer - - image.width = width; - image.height = height; - image.mipmaps = 1; - image.format = COMPRESSED_ETC1_RGB; + if (header.format == 0) image.format = COMPRESSED_ETC1_RGB; + else if (header.format == 1) image.format = COMPRESSED_ETC2_RGB; + else if (header.format == 3) image.format = COMPRESSED_ETC2_EAC_RGBA; } + + fclose(pkmFile); // Close file pointer + } + + return image; +} + +// Load KTX compressed image data (ETC1/ETC2 compression) +static Image LoadKTX(const char *fileName) +{ + // Required extensions: + // GL_OES_compressed_ETC1_RGB8_texture (ETC1) + // GL_ARB_ES3_compatibility (ETC2/EAC) + + // Supported tokens (defined by extensions) + // GL_ETC1_RGB8_OES 0x8D64 + // GL_COMPRESSED_RGB8_ETC2 0x9274 + // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + + // KTX file Header (64 bytes) + // https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int glType; // For compressed textures, glType must equal 0 + unsigned int glTypeSize; // For compressed texture data, usually 1 + unsigned int glFormat; // For compressed textures is 0 + unsigned int glInternalFormat; // Compressed internal format + unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmapLevels; // Non-mipmapped textures = 1 + unsigned int keyValueDataSize; // Used to encode any arbitrary data... + } ktxHeader; + + // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize + + Image image; + + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; + + FILE *ktxFile = fopen(fileName, "rb"); + + if (ktxFile == NULL) + { + TraceLog(WARNING, "[%s] KTX image file could not be opened", fileName); } + else + { + ktxHeader header; + + // Get the image header + fread(&header, sizeof(ktxHeader), 1, ktxFile); + + if ((header.id[1] != 'K') || (header.id[2] != 'T') || (header.id[3] != 'X') || + (header.id[4] != ' ') || (header.id[5] != '1') || (header.id[6] != '1')) + { + TraceLog(WARNING, "[%s] KTX file does not seem to be a valid file", fileName); + } + else + { + image.width = header.width; + image.height = header.height; + image.mipmaps = header.mipmapLevels; + + TraceLog(INFO, "KTX (ETC) image width: %i", header.width); + TraceLog(INFO, "KTX (ETC) image height: %i", header.height); + TraceLog(INFO, "KTX (ETC) image format: 0x%x", header.glInternalFormat); + + unsigned char unused; + + if (header.keyValueDataSize > 0) + { + for (int i = 0; i < header.keyValueDataSize; i++) fread(&unused, 1, 1, ktxFile); + } + + int dataSize; + fread(&dataSize, sizeof(unsigned int), 1, ktxFile); + + image.data = (unsigned char*)malloc(dataSize * sizeof(unsigned char)); + fread(image.data, 1, dataSize, ktxFile); + + if (header.glInternalFormat == 0x8D64) image.format = COMPRESSED_ETC1_RGB; + else if (header.glInternalFormat == 0x9274) image.format = COMPRESSED_ETC2_RGB; + else if (header.glInternalFormat == 0x9278) image.format = COMPRESSED_ETC2_EAC_RGBA; + } + + fclose(ktxFile); // Close file pointer + } + return image; } // Loading PVR image data (uncompressed or PVRT compression) -// NOTE: PVR compression requires extension GL_IMG_texture_compression_pvrtc (PowerVR GPUs) -static ImageEx LoadPVR(const char *fileName) +// NOTE: PVR v2 not supported, use PVR v3 instead +static Image LoadPVR(const char *fileName) { - // If supported in OpenGL ES 2.0. the following formats could be defined: - // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB) - // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGBA) + // Required extension: + // GL_IMG_texture_compression_pvrtc + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 // PVR file v2 Header (52 bytes) typedef struct { @@ -818,25 +928,30 @@ static ImageEx LoadPVR(const char *fileName) // PVR file v3 Header (52 bytes) // NOTE: After it could be metadata (15 bytes?) typedef struct { - unsigned int version; - unsigned int flags; - unsigned long long pixelFormat; - unsigned int colourSpace; - unsigned int channelType; - unsigned int height; - unsigned int width; - unsigned int depth; - unsigned int numSurfaces; - unsigned int numFaces; - unsigned int numMipmaps; - unsigned int metaDataSize; + char id[4]; + unsigned int flags; + unsigned char channels[4]; // pixelFormat high part + unsigned char channelDepth[4]; // pixelFormat low part + unsigned int colourSpace; + unsigned int channelType; + unsigned int height; + unsigned int width; + unsigned int depth; + unsigned int numSurfaces; + unsigned int numFaces; + unsigned int numMipmaps; + unsigned int metaDataSize; } pvrHeaderV3; - // Bytes Swap (little-endian <-> big-endian) - //unsigned short data; - //unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8); + // Metadata (usually 15 bytes) + typedef struct { + unsigned int devFOURCC; + unsigned int key; + unsigned int dataSize; // Not used? + unsigned char *data; // Not used? + } pvrMetadata; - ImageEx image; + Image image; image.data = NULL; image.width = 0; @@ -848,48 +963,164 @@ static ImageEx LoadPVR(const char *fileName) if (pvrFile == NULL) { - TraceLog(WARNING, "[%s] PVR image file could not be opened", fileName); + TraceLog(WARNING, "[%s] PVR file could not be opened", fileName); } else { // Check PVR image version - unsigned int pvrVersion = 0; - fread(&pvrVersion, sizeof(unsigned int), 1, pvrFile); - fsetpos(pvrFile, 0); + unsigned char pvrVersion = 0; + fread(&pvrVersion, sizeof(unsigned char), 1, pvrFile); + fseek(pvrFile, 0, SEEK_SET); - if (pvrVersion == 52) - { - pvrHeaderV2 header; - - fread(&header, sizeof(pvrHeaderV2), 1, pvrFile); - - image.width = header.width; - image.height = header.height; - image.mipmaps = header.numMipmaps; - image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA - } - else if (pvrVersion == 3) + // Load different PVR data formats + if (pvrVersion == 0x50) { pvrHeaderV3 header; + // Get PVR image header fread(&header, sizeof(pvrHeaderV3), 1, pvrFile); - image.width = header.width; - image.height = header.height; - image.mipmaps = header.numMipmaps; - - // TODO: Skip metaDataSize - - image.format = COMPRESSED_PVRT_RGB; //COMPRESSED_PVRT_RGBA + if ((header.id[0] != 'P') || (header.id[1] != 'V') || (header.id[2] != 'R') || (header.id[3] != 3)) + { + TraceLog(WARNING, "[%s] PVR file does not seem to be a valid image", fileName); + } + else + { + image.width = header.width; + image.height = header.height; + image.mipmaps = header.numMipmaps + 1; + + // Check data format + if (((header.channels[0] == 'l') && (header.channels[1] == 0)) && (header.channelDepth[0] == 8)) image.format = UNCOMPRESSED_GRAYSCALE; + else if (((header.channels[0] == 'l') && (header.channels[1] == 'a')) && ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8))) image.format = UNCOMPRESSED_GRAY_ALPHA; + else if ((header.channels[0] == 'r') && (header.channels[1] == 'g') && (header.channels[2] == 'b')) + { + if (header.channels[3] == 'a') + { + if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 5) && (header.channelDepth[2] == 5) && (header.channelDepth[3] == 1)) image.format = UNCOMPRESSED_R5G5B5A1; + else if ((header.channelDepth[0] == 4) && (header.channelDepth[1] == 4) && (header.channelDepth[2] == 4) && (header.channelDepth[3] == 4)) image.format = UNCOMPRESSED_R4G4B4A4; + else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8) && (header.channelDepth[3] == 8)) image.format = UNCOMPRESSED_R8G8B8A8; + } + else if (header.channels[3] == 0) + { + if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 6) && (header.channelDepth[2] == 5)) image.format = UNCOMPRESSED_R5G6B5; + else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8)) image.format = UNCOMPRESSED_R8G8B8; + } + } + else if (header.channels[0] == 2) image.format = COMPRESSED_PVRT_RGB; + else if (header.channels[0] == 3) image.format = COMPRESSED_PVRT_RGBA; + + // Skip meta data header + unsigned char unused = 0; + for (int i = 0; i < header.metaDataSize; i++) fread(&unused, sizeof(unsigned char), 1, pvrFile); + + // Calculate data size (depends on format) + int bpp = 0; + + switch (image.format) + { + case UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case UNCOMPRESSED_GRAY_ALPHA: + case UNCOMPRESSED_R5G5B5A1: + case UNCOMPRESSED_R5G6B5: + case UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case UNCOMPRESSED_R8G8B8: bpp = 24; break; + case COMPRESSED_PVRT_RGB: + case COMPRESSED_PVRT_RGBA: bpp = 4; break; + default: break; + } + + int dataSize = image.width*image.height*bpp/8; // Total data size in bytes + image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); + + // Read data from file + fread(image.data, dataSize, 1, pvrFile); + } } + else if (pvrVersion == 52) TraceLog(INFO, "PVR v2 not supported, update your files to PVR v3"); - int size = (image.width/4)*(image.height/4)*8; // Total data size in bytes + fclose(pvrFile); // Close file pointer + } - image.data = (unsigned char*)malloc(size * sizeof(unsigned char)); + return image; +} - fread(image.data, 1, size, pvrFile); +// Load ASTC compressed image data (ASTC compression) +static Image LoadASTC(const char *fileName) +{ + // Required extensions: + // GL_KHR_texture_compression_astc_hdr + // GL_KHR_texture_compression_astc_ldr + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 + // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 + + // ASTC file Header (16 bytes) + typedef struct { + unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C + unsigned char blockX; // Block X dimensions + unsigned char blockY; // Block Y dimensions + unsigned char blockZ; // Block Z dimensions (1 for 2D images) + unsigned char width[3]; // Image width in pixels (24bit value) + unsigned char height[3]; // Image height in pixels (24bit value) + unsigned char lenght[3]; // Image Z-size (1 for 2D images) + } astcHeader; - fclose(pvrFile); // Close file pointer + Image image; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; + + FILE *astcFile = fopen(fileName, "rb"); + + if (astcFile == NULL) + { + TraceLog(WARNING, "[%s] ASTC file could not be opened", fileName); + } + else + { + astcHeader header; + + // Get ASTC image header + fread(&header, sizeof(astcHeader), 1, astcFile); + + if ((header.id[3] != 0x5c) || (header.id[2] != 0xa1) || (header.id[1] != 0xab) || (header.id[0] != 0x13)) + { + TraceLog(WARNING, "[%s] ASTC file does not seem to be a valid image", fileName); + } + else + { + image.width = 0x00000000 | ((int)header.width[0] << 16) | ((int)header.width[1] << 8) | ((int)header.width[2]); + image.height = 0x00000000 | ((int)header.height[0] << 16) | ((int)header.height[1] << 8) | ((int)header.height[2]); + image.mipmaps = 1; + + TraceLog(DEBUG, "ASTC image width: %i", image.width); + TraceLog(DEBUG, "ASTC image height: %i", image.height); + TraceLog(DEBUG, "ASTC image blocks: %ix%i", header.blockX, header.blockY); + + // NOTE: Each block is always stored in 128bit so we can calculate the bpp + int bpp = 128/(header.blockX*header.blockY); + + // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8 + if ((bpp == 8) || (bpp == 2)) + { + int dataSize = image.width*image.height*bpp/8; // Data size in bytes + + image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); + fread(image.data, dataSize, 1, astcFile); + + if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA; + else if (bpp == 2) image.format = COMPRESSED_ASTC_4x4_RGBA; + } + else TraceLog(WARNING, "[%s] ASTC block size configuration not supported", fileName); + } + + fclose(astcFile); } return image; -- cgit v1.2.3 From a7714c842f72b8d41829caa7564f91abb3ffbd6b Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 11 May 2015 00:15:46 +0200 Subject: raymath module review and other changes Complete review of matrix rotation math Check compressed textures support WIP: LoadImageFromData() --- src/core.c | 3 +- src/makefile | 10 +- src/raylib.h | 3 +- src/raymath.c | 262 +++++++++++++----------------------------------- src/raymath.h | 16 ++- src/rlgl.c | 309 +++++++++++++++++++++++++++++++-------------------------- src/text.c | 32 ++---- src/textures.c | 145 ++++++++++++++++++++++++--- 8 files changed, 393 insertions(+), 387 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 384e043a..cc8f31fd 100644 --- a/src/core.c +++ b/src/core.c @@ -180,9 +180,9 @@ static int currentMouseWheelY = 0; // Required to track mouse wheel var static int exitKey = KEY_ESCAPE; // Default exit key (ESC) static int lastKeyPressed = -1; -#endif static bool cursorHidden; +#endif static double currentTime, previousTime; // Used to track timmings static double updateTime, drawTime; // Time measures for update and draw @@ -227,6 +227,7 @@ static void SwapBuffers(void); // Copy back buffer to f static void PollInputEvents(void); // Register user events static void LogoAnimation(void); // Plays raylib logo appearing animation static void SetupFramebufferSize(int displayWidth, int displayHeight); + #if defined(PLATFORM_RPI) static void InitMouse(void); // Mouse initialization (including mouse thread) static void *MouseThread(void *arg); // Mouse reading thread diff --git a/src/makefile b/src/makefile index 77aa2b5d..1908b138 100644 --- a/src/makefile +++ b/src/makefile @@ -93,7 +93,7 @@ else endif # define all object files required -OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o +OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o camera.o gestures.o # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is raylib @@ -148,6 +148,14 @@ utils.o: utils.c stb_vorbis.o: stb_vorbis.c $(CC) -c stb_vorbis.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) +# compile camera module +camera.o: camera.c + $(CC) -c camera.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + +# compile gestures module +gestures.o: gestures.c + $(CC) -c gestures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + # clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) diff --git a/src/raylib.h b/src/raylib.h index bd1692d2..0eafa390 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -515,16 +515,15 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 //------------------------------------------------------------------------------------ Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) +Image LoadImageFromData(Color *pixels, int width, int height, int format); // Load image from Color array data Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load a texture from raw data into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) -Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() void UnloadImage(Image image); // Unload image from CPU memory (RAM) void UnloadTexture(Texture2D texture); // Unload texture from GPU memory void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) Color *GetPixelData(Image image); // Get pixel data from image as a Color struct array -void SetPixelData(Image *image, Color *pixels, int format); // Set image data from Color struct array void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 diff --git a/src/raymath.c b/src/raymath.c index 9763b075..f5e30833 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -431,109 +431,16 @@ Matrix MatrixSubstract(Matrix left, Matrix right) } // Returns translation matrix -// TODO: Review this function Matrix MatrixTranslate(float x, float y, float z) { -/* - For OpenGL - 1, 0, 0, 0 - 0, 1, 0, 0 - 0, 0, 1, 0 - x, y, z, 1 - Is the correct Translation Matrix. Why? Opengl Uses column-major matrix ordering. - Which is the Transpose of the Matrix you initially presented, which is in row-major ordering. - Row major is used in most math text-books and also DirectX, so it is a common - point of confusion for those new to OpenGL. - - * matrix notation used in opengl documentation does not describe in-memory layout for OpenGL matrices - - Translation matrix should be laid out in memory like this: - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, trabsX, transY, transZ, 1 } - - - 9.005 Are OpenGL matrices column-major or row-major? - - For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out - contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements - of the 16-element matrix, where indices are numbered from 1 to 16 as described in section - 2.11.2 of the OpenGL 2.1 Specification. - - Column-major versus row-major is purely a notational convention. Note that post-multiplying - with column-major matrices produces the same result as pre-multiplying with row-major matrices. - The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. - You can use any notation, as long as it's clearly stated. - - Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion - in the OpenGL programming community. Column-major notation suggests that matrices - are not laid out in memory as a programmer would expect. -*/ - Matrix result = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }; return result; } -// Returns rotation matrix -// TODO: Review this function -Matrix MatrixRotate(float angleX, float angleY, float angleZ) -{ - Matrix result; - - Matrix rotX = MatrixRotateX(angleX); - Matrix rotY = MatrixRotateY(angleY); - Matrix rotZ = MatrixRotateZ(angleZ); - - result = MatrixMultiply(MatrixMultiply(rotX, rotY), rotZ); - - return result; -} - -/* -Matrix MatrixRotate(float angle, float x, float y, float z) -{ - Matrix result = MatrixIdentity(); - - float c = cosf(angle*DEG2RAD); // cosine - float s = sinf(angle*DEG2RAD); // sine - float c1 = 1.0f - c; // 1 - c - - float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12, - m1 = result.m1, m5 = result.m5, m9 = result.m9, m13 = result.m13, - m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14; - - // build rotation matrix - float r0 = x * x * c1 + c; - float r1 = x * y * c1 + z * s; - float r2 = x * z * c1 - y * s; - float r4 = x * y * c1 - z * s; - float r5 = y * y * c1 + c; - float r6 = y * z * c1 + x * s; - float r8 = x * z * c1 + y * s; - float r9 = y * z * c1 - x * s; - float r10= z * z * c1 + c; - - // multiply rotation matrix - result.m0 = r0*m0 + r4*m1 + r8*m2; - result.m1 = r1*m0 + r5*m1 + r9*m2; - result.m2 = r2*m0 + r6*m1 + r10*m2; - result.m4 = r0*m4 + r4*m5 + r8*m6; - result.m5 = r1*m4 + r5*m5 + r9*m6; - result.m6 = r2*m4 + r6*m5 + r10*m6; - result.m8 = r0*m8 + r4*m9 + r8*m10; - result.m9 = r1*m8 + r5*m9 + r9*m10; - result.m10 = r2*m8 + r6*m9 + r10*m10; - result.m12 = r0*m12+ r4*m13 + r8*m14; - result.m13 = r1*m12+ r5*m13 + r9*m14; - result.m14 = r2*m12+ r6*m13 + r10*m14; - - return result; -} -*/ - // Create rotation matrix from axis and angle -// TODO: Test this function -// NOTE: NO prototype defined! -Matrix MatrixFromAxisAngle(Vector3 axis, float angle) +// NOTE: Angle should be provided in radians +Matrix MatrixRotate(float angle, Vector3 axis) { Matrix result; @@ -545,15 +452,15 @@ Matrix MatrixFromAxisAngle(Vector3 axis, float angle) if ((length != 1) && (length != 0)) { - length = 1 / length; + length = 1/length; x *= length; y *= length; z *= length; } - float s = sin(angle); - float c = cos(angle); - float t = 1-c; + float s = sinf(angle); + float c = cosf(angle); + float t = 1.0f - c; // Cache some matrix values (speed optimization) float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; @@ -583,70 +490,51 @@ Matrix MatrixFromAxisAngle(Vector3 axis, float angle) result.m14 = mat.m14; result.m15 = mat.m15; - return result; -}; - -// Create rotation matrix from axis and angle (version 2) -// TODO: Test this function -// NOTE: NO prototype defined! -Matrix MatrixFromAxisAngle2(Vector3 axis, float angle) -{ - Matrix result; - - VectorNormalize(&axis); - float axisX = axis.x, axisY = axis.y, axisZ = axis.y; - - // Calculate angles - float cosres = (float)cos(angle); - float sinres = (float)sin(angle); - float t = 1.0f - cosres; - - // Do the conversion math once - float tXX = t * axisX * axisX; - float tXY = t * axisX * axisY; - float tXZ = t * axisX * axisZ; - float tYY = t * axisY * axisY; - float tYZ = t * axisY * axisZ; - float tZZ = t * axisZ * axisZ; - - float sinX = sinres * axisX; - float sinY = sinres * axisY; - float sinZ = sinres * axisZ; - - result.m0 = tXX + cosres; - result.m1 = tXY + sinZ; - result.m2 = tXZ - sinY; - result.m3 = 0; - result.m4 = tXY - sinZ; - result.m5 = tYY + cosres; - result.m6 = tYZ + sinX; - result.m7 = 0; - result.m8 = tXZ + sinY; - result.m9 = tYZ - sinX; - result.m10 = tZZ + cosres; - result.m11 = 0; - result.m12 = 0; - result.m13 = 0; - result.m14 = 0; - result.m15 = 1; - return result; } -// Returns rotation matrix for a given quaternion -Matrix MatrixFromQuaternion(Quaternion q) +/* +// Another implementation for MatrixRotate... +Matrix MatrixRotate(float angle, float x, float y, float z) { Matrix result = MatrixIdentity(); - Vector3 axis; - float angle; + float c = cosf(angle); // cosine + float s = sinf(angle); // sine + float c1 = 1.0f - c; // 1 - c - QuaternionToAxisAngle(q, &axis, &angle); + float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12, + m1 = result.m1, m5 = result.m5, m9 = result.m9, m13 = result.m13, + m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14; - result = MatrixFromAxisAngle2(axis, angle); + // build rotation matrix + float r0 = x * x * c1 + c; + float r1 = x * y * c1 + z * s; + float r2 = x * z * c1 - y * s; + float r4 = x * y * c1 - z * s; + float r5 = y * y * c1 + c; + float r6 = y * z * c1 + x * s; + float r8 = x * z * c1 + y * s; + float r9 = y * z * c1 - x * s; + float r10= z * z * c1 + c; + + // multiply rotation matrix + result.m0 = r0*m0 + r4*m1 + r8*m2; + result.m1 = r1*m0 + r5*m1 + r9*m2; + result.m2 = r2*m0 + r6*m1 + r10*m2; + result.m4 = r0*m4 + r4*m5 + r8*m6; + result.m5 = r1*m4 + r5*m5 + r9*m6; + result.m6 = r2*m4 + r6*m5 + r10*m6; + result.m8 = r0*m8 + r4*m9 + r8*m10; + result.m9 = r1*m8 + r5*m9 + r9*m10; + result.m10 = r2*m8 + r6*m9 + r10*m10; + result.m12 = r0*m12+ r4*m13 + r8*m14; + result.m13 = r1*m12+ r5*m13 + r9*m14; + result.m14 = r2*m12+ r6*m13 + r10*m14; return result; } +*/ // Returns x-rotation matrix (angle in radians) Matrix MatrixRotateX(float angle) @@ -704,22 +592,6 @@ Matrix MatrixScale(float x, float y, float z) return result; } -// Returns transformation matrix for a given translation, rotation and scale -// NOTE: Transformation order is rotation -> scale -> translation -// NOTE: Rotation angles should come in radians -Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale) -{ - Matrix result = MatrixIdentity(); - - Matrix mRotation = MatrixRotate(rotation.x, rotation.y, rotation.z); - Matrix mScale = MatrixScale(scale.x, scale.y, scale.z); - Matrix mTranslate = MatrixTranslate(translation.x, translation.y, translation.z); - - result = MatrixMultiply(MatrixMultiply(mRotation, mScale), mTranslate); - - return result; -} - // Returns two matrix multiplication // NOTE: When multiplying matrices... the order matters! Matrix MatrixMultiply(Matrix left, Matrix right) @@ -874,7 +746,7 @@ void PrintMatrix(Matrix m) // Module Functions Definition - Quaternion math //---------------------------------------------------------------------------------- -// Calculates the length of a quaternion +// Computes the length of a quaternion float QuaternionLength(Quaternion quat) { return sqrt(quat.x*quat.x + quat.y*quat.y + quat.z*quat.z + quat.w*quat.w); @@ -948,7 +820,7 @@ Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) return result; } -// Returns a quaternion from a given rotation matrix +// Returns a quaternion for a given rotation matrix Quaternion QuaternionFromMatrix(Matrix matrix) { Quaternion result; @@ -1004,29 +876,7 @@ Quaternion QuaternionFromMatrix(Matrix matrix) return result; } -// Returns rotation quaternion for an angle around an axis -// NOTE: angle must be provided in radians -Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0, 0, 0, 1 }; - - if (VectorLength(axis) != 0.0) - - angle *= 0.5; - - VectorNormalize(&axis); - - result.x = axis.x * (float)sin(angle); - result.y = axis.y * (float)sin(angle); - result.z = axis.z * (float)sin(angle); - result.w = (float)cos(angle); - - QuaternionNormalize(&result); - - return result; -} - -// Calculates the matrix from the given quaternion +// Returns a matrix for a given quaternion Matrix QuaternionToMatrix(Quaternion q) { Matrix result; @@ -1065,12 +915,34 @@ Matrix QuaternionToMatrix(Quaternion q) result.m13 = 0; result.m14 = 0; result.m15 = 1; + + return result; +} + +// Returns rotation quaternion for an angle and axis +// NOTE: angle must be provided in radians +Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis) +{ + Quaternion result = { 0, 0, 0, 1 }; + + if (VectorLength(axis) != 0.0) + + angle *= 0.5; + + VectorNormalize(&axis); + + result.x = axis.x * (float)sin(angle); + result.y = axis.y * (float)sin(angle); + result.z = axis.z * (float)sin(angle); + result.w = (float)cos(angle); + + QuaternionNormalize(&result); return result; } -// Returns the axis and the angle for a given quaternion -void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) +// Returns the rotation angle and axis for a given quaternion +void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis) { if (fabs(q.w) > 1.0f) QuaternionNormalize(&q); diff --git a/src/raymath.h b/src/raymath.h index 133e1881..d93c324c 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -107,15 +107,11 @@ Matrix MatrixIdentity(void); // Returns identity matr Matrix MatrixAdd(Matrix left, Matrix right); // Add two matrices Matrix MatrixSubstract(Matrix left, Matrix right); // Substract two matrices (left - right) Matrix MatrixTranslate(float x, float y, float z); // Returns translation matrix -Matrix MatrixRotate(float axisX, float axisY, float axisZ); // Returns rotation matrix -Matrix MatrixFromAxisAngle(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis -Matrix MatrixFromAxisAngle2(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis (test another implemntation) -Matrix MatrixFromQuaternion(Quaternion q); // Returns rotation matrix for a given quaternion +Matrix MatrixRotate(float angle, Vector3 axis); // Returns rotation matrix for an angle around an specified axis (angle in radians) Matrix MatrixRotateX(float angle); // Returns x-rotation matrix (angle in radians) Matrix MatrixRotateY(float angle); // Returns y-rotation matrix (angle in radians) Matrix MatrixRotateZ(float angle); // Returns z-rotation matrix (angle in radians) Matrix MatrixScale(float x, float y, float z); // Returns scaling matrix -Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale); // Returns transformation matrix for a given translation, rotation and scale Matrix MatrixMultiply(Matrix left, Matrix right); // Returns two matrix multiplication Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far); // Returns perspective projection matrix Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix @@ -126,14 +122,14 @@ void PrintMatrix(Matrix m); // Print matrix utility //------------------------------------------------------------------------------------ // Functions Declaration to work with Quaternions //------------------------------------------------------------------------------------ -float QuaternionLength(Quaternion quat); // Calculates the length of a quaternion +float QuaternionLength(Quaternion quat); // Compute the length of a quaternion void QuaternionNormalize(Quaternion *q); // Normalize provided quaternion Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float slerp); // Calculates spherical linear interpolation between two quaternions -Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion from a given rotation matrix -Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle); // Returns rotation quaternion for an angle around an axis -Matrix QuaternionToMatrix(Quaternion q); // Calculates the matrix from the given quaternion -void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle); // Returns the axis and the angle for a given quaternion +Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix +Matrix QuaternionToMatrix(Quaternion q); // Returns a matrix for a given quaternion +Quaternion QuaternionFromAxisAngle(float angle, Vector3 axis); // Returns rotation quaternion for an angle and axis +void QuaternionToAxisAngle(Quaternion q, float *outAngle, Vector3 *outAxis); // Returns the rotation angle and axis for a given quaternion void QuaternionTransform(Quaternion *q, Matrix mat); // Transform a quaternion given a transformation matrix #ifdef __cplusplus diff --git a/src/rlgl.c b/src/rlgl.c index fbea84a2..7a73623c 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -30,6 +30,7 @@ #include // Standard input / output lib #include // Declares malloc() and free() for memory management, rand() +#include // Declares strcmp(), strlen(), strtok(), strdup() #if defined(GRAPHICS_API_OPENGL_11) #ifdef __APPLE__ // OpenGL include for OSX @@ -63,28 +64,49 @@ #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) +#ifndef GL_SHADING_LANGUAGE_VERSION + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + #ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif #ifndef GL_ETC1_RGB8_OES - #define GL_ETC1_RGB8_OES 0x8D64 + #define GL_ETC1_RGB8_OES 0x8D64 #endif #ifndef GL_COMPRESSED_RGB8_ETC2 - #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 #endif #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC - #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR + #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 #endif +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -215,6 +237,8 @@ static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: It's required in shapes and models modules! unsigned int whiteTexture; +static bool supportedTextureFormat[32]; + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -224,9 +248,6 @@ static Shader LoadSimpleShader(void); static void InitializeBuffers(void); static void InitializeBuffersGPU(void); static void UpdateBuffers(void); -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); - -// Custom shader files loading (external) static char *TextFileRead(char *fn); #endif @@ -235,6 +256,8 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); #endif +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); + //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations //---------------------------------------------------------------------------------- @@ -328,23 +351,11 @@ void rlTranslatef(float x, float y, float z) // Multiply the current matrix by a rotation matrix void rlRotatef(float angleDeg, float x, float y, float z) { - // TODO: Support rotation in multiple axes Matrix rotation = MatrixIdentity(); - // OPTION 1: It works... - if (x == 1) rotation = MatrixRotateX(angleDeg*DEG2RAD); - else if (y == 1) rotation = MatrixRotateY(angleDeg*DEG2RAD); - else if (z == 1) rotation = MatrixRotateZ(angleDeg*DEG2RAD); - - // OPTION 2: Requires review... - //Vector3 axis = (Vector3){ x, y, z }; - //VectorNormalize(&axis); - //rotation = MatrixRotateY(angleDeg*DEG2RAD); //MatrixFromAxisAngle(axis, angleDeg*DEG2RAD); - - // OPTION 3: TODO: Review, it doesn't work! - //Vector3 vec = (Vector3){ x, y, z }; - //VectorNormalize(&vec); - //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x); + Vector3 axis = (Vector3){ x, y, z }; + VectorNormalize(&axis); + rotation = MatrixRotate(angleDeg*DEG2RAD, axis); MatrixTranspose(&rotation); @@ -840,7 +851,7 @@ void rlglInit(void) TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); TraceLog(INFO, "GPU: Version: %s", glGetString(GL_VERSION)); - TraceLog(INFO, "GPU: GLSL: %s", glGetString(0x8B8C)); //GL_SHADING_LANGUAGE_VERSION + TraceLog(INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) //int maxTexSize; @@ -853,6 +864,9 @@ void rlglInit(void) // Show supported extensions // NOTE: We don't need that much data on screen... right now... + + // Check available extensions for compressed textures support + for (int i = 0; i < 32; i++) supportedTextureFormat[i] = false; #if defined(GRAPHICS_API_OPENGL_33) GLint numExt; @@ -861,40 +875,49 @@ void rlglInit(void) for (int i = 0; i < numExt; i++) { //TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); - /* - if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_EXT_texture_compression_s3tc") == 0) + + if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_EXT_texture_compression_s3tc") == 0) { // DDS texture compression support - - // TODO: Check required tokens + supportedTextureFormat[COMPRESSED_DXT1_RGB] = true; + supportedTextureFormat[COMPRESSED_DXT1_RGBA] = true; + supportedTextureFormat[COMPRESSED_DXT3_RGBA] = true; + supportedTextureFormat[COMPRESSED_DXT5_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_OES_compressed_ETC1_RGB8_texture") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_OES_compressed_ETC1_RGB8_texture") == 0) { // ETC1 texture compression support + supportedTextureFormat[COMPRESSED_ETC1_RGB] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) { - //OES_compressed_ETC2_RGB8_texture, - //OES_compressed_ETC2_RGBA8_texture, // ETC2/EAC texture compression support + supportedTextureFormat[COMPRESSED_ETC2_RGB] = true; + supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) { // PVR texture compression support + supportedTextureFormat[COMPRESSED_PVRT_RGB] = true; + supportedTextureFormat[COMPRESSED_PVRT_RGBA] = true; } - else if (strcmp(glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) + else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) { // ASTC texture compression support + supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA] = true; + supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA] = true; } - */ } #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string // NOTE: String could be splitted using strtok() function (string.h) TraceLog(INFO, "Supported extension: %s", extensions); + + //char** ext = StringSplit(extensions, ' '); + //for (int i = 0; i < numExt; i++) printf("%s", ext[i]); + #endif - /* GLint numComp = 0; glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -1258,6 +1281,7 @@ void rlglDrawPostpro(void) } // Draw a 3d model +// NOTE: Model transform can come within model struct void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires) { #if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) @@ -1284,8 +1308,6 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r rlScalef(scale.x, scale.y, scale.z); rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); - // TODO: If rotate in multiple axis, get rotation matrix and use rlMultMatrix() - rlColor4ub(color.r, color.g, color.b, color.a); glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); @@ -1302,13 +1324,17 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(model.shader.id); - // TODO: Use model.transform matrix - - Vector3 rotation = { 0.0f, 0.0f, 0.0f }; + // Apply transformation provided in model.transform matrix + Matrix modelviewworld = MatrixMultiply(model.transform, modelview); // World-space transformation + // Apply transformations provided in function // Get transform matrix (rotation -> scale -> translation) - Matrix transform = MatrixTransform(position, rotation, scale); // Object-space transformation - Matrix modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation + Matrix rotation = MatrixRotate(rotationAngle*DEG2RAD, rotationAxis); + Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); + Matrix translation = MatrixTranslate(position.x, position.y, position.z); + + Matrix transform = MatrixMultiply(MatrixMultiply(rotation, matScale), translation); // Object-space transformation matrix + modelviewworld = MatrixMultiply(transform, modelview); // World-space transformation // Projection: Screen-space transformation @@ -1405,7 +1431,6 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) #endif - // TODO: Review this comment when called from window resize callback TraceLog(INFO, "OpenGL Graphics initialized successfully"); } @@ -1596,75 +1621,6 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma //glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, id); - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used! -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis -#endif - - bool texIsPOT = false; - - // Check if width and height are power-of-two (POT) - if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; - - if (genMipmaps && !texIsPOT) - { - TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); - - genMipmaps = false; - } - - // TODO: Support mipmaps --> if (mipmapCount > 1) - - // If mipmaps are being used, we configure mag-min filters accordingly - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so only GL_LINEAR or GL_NEAREST can be used - if (genMipmaps) - { - // Trilinear filtering with mipmaps - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate use of mipmaps (must be available) - } - else - { - // Not using mipmappings - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - } - -#if defined(GRAPHICS_API_OPENGL_11) - if (genMipmaps) - { - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); - - // Compute required mipmaps - // NOTE: data size is reallocated to fit mipmaps data - int mipmapCount = GenerateMipmaps(data, width, height); - - int offset = 0; - int size = 0; - - int mipWidth = width; - int mipHeight = height; - - // Load the mipmaps - for (int level = 0; level < mipmapCount; level++) - { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset); - - size = mipWidth*mipHeight*4; - offset += size; - - mipWidth /= 2; - mipHeight /= 2; - } - } - else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); -#endif - #if defined(GRAPHICS_API_OPENGL_33) // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) // NOTE: On embedded systems, we let the driver choose the best internal format @@ -1703,24 +1659,20 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; - case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; - case COMPRESSED_ETC1_RGB: TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break;//TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break;//TraceLog(WARNING, "ETC compression not supported"); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - //case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; + case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; + case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[COMPRESSED_ETC2_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 default: TraceLog(WARNING, "Texture format not recognized"); break; } - - if ((mipmapCount == 1) && (genMipmaps)) - { - glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); - } -#elif defined(GRAPHICS_API_OPENGL_ES2) - +#elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA switch (textureFormat) { @@ -1731,17 +1683,63 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_DXT5_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_ETC1_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; - case COMPRESSED_ETC2_RGB: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - //case COMPRESSED_ASTC_RGBA_4x4: LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[COMPRESSED_ETC2_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 default: TraceLog(WARNING, "Texture format not supported"); break; } +#endif + + // Check if texture is power-of-two (POT) to enable mipmap generation + bool texIsPOT = false; + + if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; + + if (genMipmaps && !texIsPOT) + { + TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); + genMipmaps = false; + } + + // Generate mipmaps if required + // TODO: Improve mipmaps support +#if defined(GRAPHICS_API_OPENGL_11) + if (genMipmaps) + { + // Compute required mipmaps + // NOTE: data size is reallocated to fit mipmaps data + int mipmapCount = GenerateMipmaps(data, width, height); + + // TODO: Adjust mipmap size depending on texture format! + int size = width*height*4; + int offset = size; + + int mipWidth = width/2; + int mipHeight = height/2; + + // Load the mipmaps + for (int level = 1; level < mipmapCount; level++) + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset); + size = mipWidth*mipHeight*4; + offset += size; + + mipWidth /= 2; + mipHeight /= 2; + } + + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); + } +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((mipmapCount == 1) && (genMipmaps)) { glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically @@ -1749,7 +1747,30 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma } #endif - // At this point we have the image converted to texture and uploaded to GPU + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if ((mipmapCount > 1) || (genMipmaps)) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) + } +#endif + + // At this point we have the texture loaded in GPU, with mipmaps generated (if desired) and texture parameters configured // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); @@ -2027,6 +2048,7 @@ void rlglSetModelShader(Model *model, Shader shader) // Set custom shader to be used on batch draw void rlglSetCustomShader(Shader shader) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (currentShader.id != shader.id) { rlglDraw(); @@ -2053,12 +2075,15 @@ void rlglSetCustomShader(Shader shader) if (vaoSupported) glBindVertexArray(0); // Unbind VAO */ } +#endif } // Set default shader to be used on batch draw void rlglSetDefaultShader(void) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlglSetCustomShader(defaultShader); +#endif } #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -2441,7 +2466,7 @@ static void InitializeBuffersGPU(void) // Update VBOs with vertex array data // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) -// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays every frame! +// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays static void UpdateBuffers(void) { if (lines.vCounter > 0) @@ -2508,11 +2533,9 @@ static void UpdateBuffers(void) // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); } - #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_11) - // Mipmaps data is generated after image data static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) { diff --git a/src/text.c b/src/text.c index 8e3fc4b9..b4fb54af 100644 --- a/src/text.c +++ b/src/text.c @@ -84,12 +84,6 @@ extern void LoadDefaultFont(void) defaultFont.numChars = 224; // Number of chars included in our default font - Image image; - image.width = 128; // We know our default font image is 128 pixels width - image.height = 128; // We know our default font image is 128 pixels height - image.mipmaps = 1; - image.format = UNCOMPRESSED_R8G8B8A8; - // Default font is directly defined here (data generated from a sprite font image) // This way, we reconstruct SpriteFont without creating large global variables // This data is automatically allocated to Stack and automatically deallocated at the end of this function @@ -151,14 +145,17 @@ extern void LoadDefaultFont(void) // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- - Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + int imWidth = 128; + int imHeight = 128; + + Color *imagePixels = (Color *)malloc(imWidth*imHeight*sizeof(Color)); - for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array + for (int i = 0; i < imWidth*imHeight; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter // Fill imgData with defaultFontData (convert from bit to pixel!) - for (int i = 0; i < image.width * image.height; i += 32) + for (int i = 0; i < imWidth*imHeight; i += 32) { for (int j = 31; j >= 0; j--) { @@ -174,7 +171,7 @@ extern void LoadDefaultFont(void) //fwrite(image.pixels, 1, 128*128*4, myimage); //fclose(myimage); - SetPixelData(&image, imagePixels, 0); + Image image = LoadImageFromData(imagePixels, imWidth, imHeight, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); @@ -507,7 +504,6 @@ static SpriteFont LoadRBMF(const char *fileName) } rbmfInfoHeader; SpriteFont spriteFont; - Image image; rbmfInfoHeader rbmfHeader; unsigned int *rbmfFileData = NULL; @@ -529,11 +525,6 @@ static SpriteFont LoadRBMF(const char *fileName) spriteFont.numChars = (int)rbmfHeader.numChars; - image.width = (int)rbmfHeader.imgWidth; - image.height = (int)rbmfHeader.imgHeight; - image.mipmaps = 1; - image.format = UNCOMPRESSED_R8G8B8A8; - int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); @@ -546,14 +537,14 @@ static SpriteFont LoadRBMF(const char *fileName) // Re-construct image from rbmfFileData //----------------------------------------- - Color *imagePixels = (Color *)malloc(image.width*image.height*sizeof(Color)); + Color *imagePixels = (Color *)malloc(rbmfHeader.imgWidth*rbmfHeader.imgHeight*sizeof(Color)); - for (int i = 0; i < image.width*image.height; i++) imagePixels[i] = BLANK; // Initialize array + for (int i = 0; i < rbmfHeader.imgWidth*rbmfHeader.imgHeight; i++) imagePixels[i] = BLANK; // Initialize array int counter = 0; // Font data elements counter // Fill image data (convert from bit to pixel!) - for (int i = 0; i < image.width * image.height; i += 32) + for (int i = 0; i < rbmfHeader.imgWidth*rbmfHeader.imgHeight; i += 32) { for (int j = 31; j >= 0; j--) { @@ -563,7 +554,7 @@ static SpriteFont LoadRBMF(const char *fileName) counter++; } - SetPixelData(&image, imagePixels, 0); + Image image = LoadImageFromData(imagePixels, rbmfHeader.imgWidth, rbmfHeader.imgHeight, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); @@ -694,7 +685,6 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) print(100,160, 0, "This is a test"); */ - font.numChars = 95; font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); font.texture = LoadTextureFromImage(image, false); diff --git a/src/textures.c b/src/textures.c index d2b2de1d..176ed2d7 100644 --- a/src/textures.c +++ b/src/textures.c @@ -280,7 +280,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) texture.format = 0; texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); - + texture.width = image.width; texture.height = image.height; texture.mipmaps = image.mipmaps; @@ -439,24 +439,141 @@ Color *GetPixelData(Image image) } // Fill image data with pixels Color data (RGBA - 32bit) -// NOTE: Pixels color array size must be coherent with image size -// TODO: Review to support different color modes (TextureFormat) -void SetPixelData(Image *image, Color *pixels, int format) +// NOTE: Data is transformed to desired format +Image LoadImageFromData(Color *pixels, int width, int height, int format) { - free(image->data); - image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char)); - + Image image; + image.data = NULL; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = format; + int k = 0; - for (int i = 0; i < image->width*image->height*4; i += 4) + switch (format) { - ((unsigned char *)image->data)[i] = pixels[k].r; - ((unsigned char *)image->data)[i + 1] = pixels[k].g; - ((unsigned char *)image->data)[i + 2] = pixels[k].b; - ((unsigned char *)image->data)[i + 3] = pixels[k].a; - - k++; + case UNCOMPRESSED_GRAYSCALE: + { + image.data = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height; i++) + { + ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + k++; + } + + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + image.data = (unsigned char *)malloc(image.width*image.height*2*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*2; i += 2) + { + ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + ((unsigned char *)image.data)[i + 1] = pixels[k].a; + k++; + } + + } break; + case UNCOMPRESSED_R5G6B5: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*63/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + + ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; + + k++; + } + + } break; + case UNCOMPRESSED_R8G8B8: + { + image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*3; i += 3) + { + ((unsigned char *)image.data)[i] = pixels[k].r; + ((unsigned char *)image.data)[i + 1] = pixels[k].g; + ((unsigned char *)image.data)[i + 2] = pixels[k].b; + k++; + } + } break; + case UNCOMPRESSED_R5G5B5A1: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a = 1; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*31/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + a = (pixels[k].a > 50) ? 1 : 0; + + ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1| (unsigned short)a; + + k++; + } + + } break; + case UNCOMPRESSED_R4G4B4A4: + { + image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); + + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + + for (int i = 0; i < image.width*image.height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*15/255)); + g = (unsigned char)(round((float)pixels[k].g*15/255)); + b = (unsigned char)(round((float)pixels[k].b*15/255)); + a = (unsigned char)(round((float)pixels[k].a*15/255)); + + ((unsigned short *)image.data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a; + + k++; + } + + } break; + case UNCOMPRESSED_R8G8B8A8: + { + image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*4; i += 4) + { + ((unsigned char *)image.data)[i] = pixels[k].r; + ((unsigned char *)image.data)[i + 1] = pixels[k].g; + ((unsigned char *)image.data)[i + 2] = pixels[k].b; + ((unsigned char *)image.data)[i + 3] = pixels[k].a; + k++; + } + } break; + default: + { + TraceLog(WARNING, "Format not recognized, image could not be loaded"); + + return image; + } break; } + + return image; } // Draw a Texture2D -- cgit v1.2.3 From 4e4b6bef21a3913e924ac83259cf00e18b17f780 Mon Sep 17 00:00:00 2001 From: Gatonevermind Date: Mon, 11 May 2015 17:37:19 +0200 Subject: Corrected bug on PVR textures loading --- src/textures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/textures.c b/src/textures.c index 176ed2d7..d4ad478b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1105,7 +1105,7 @@ static Image LoadPVR(const char *fileName) { image.width = header.width; image.height = header.height; - image.mipmaps = header.numMipmaps + 1; + image.mipmaps = header.numMipmaps; // Check data format if (((header.channels[0] == 'l') && (header.channels[1] == 0)) && (header.channelDepth[0] == 8)) image.format = UNCOMPRESSED_GRAYSCALE; -- cgit v1.2.3 From e25f1227c0dfde7ba5c69237a4bbfe1277841135 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 11 May 2015 18:22:16 +0200 Subject: Updated stb headers --- src/audio.c | 3 +- src/makefile | 6 +- src/stb_image.h | 206 ++++++++++++++++++++------ src/stb_image_write.h | 289 +++++++++++++++++++++++++++++++------ src/stb_vorbis.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++- src/stb_vorbis.h | 384 ------------------------------------------------- 6 files changed, 801 insertions(+), 478 deletions(-) delete mode 100644 src/stb_vorbis.h (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 0e07b8d0..2bed0a59 100644 --- a/src/audio.c +++ b/src/audio.c @@ -39,7 +39,8 @@ #include "utils.h" // rRES data decompression utility function // NOTE: Includes Android fopen function map -#include "stb_vorbis.h" // OGG loading functions +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" // OGG loading functions //---------------------------------------------------------------------------------- // Defines and Macros diff --git a/src/makefile b/src/makefile index 1908b138..11bbea9d 100644 --- a/src/makefile +++ b/src/makefile @@ -93,7 +93,7 @@ else endif # define all object files required -OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o camera.o gestures.o +OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o camera.o gestures.o # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is raylib @@ -144,10 +144,6 @@ audio.o: audio.c utils.o: utils.c $(CC) -c utils.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -# compile stb_vorbis library -stb_vorbis.o: stb_vorbis.c - $(CC) -c stb_vorbis.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) - # compile camera module camera.o: camera.c $(CC) -c camera.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) diff --git a/src/stb_image.h b/src/stb_image.h index 39cbb7ad..1249c303 100644 --- a/src/stb_image.h +++ b/src/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.00b - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -143,6 +143,13 @@ Latest revision history: + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD progressive JPEG @@ -154,8 +161,6 @@ 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) optimize PNG fix bug in interlaced PNG with user-specified channel count - 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG - 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc See end of file for full revision history. @@ -178,7 +183,7 @@ James "moose2000" Brown (iPhone PNG) Roy Eltham Ben "Disch" Wenger (io callbacks) Luke Graham Omar Cornut (1/2/4-bit PNG) Thomas Ruf - John Bartholomew + Nicolas Guillemot (vertical flip) John Bartholomew Ken Hamada Optimizations & bugfixes Cort Stratton Fabian "ryg" Giesen Blazej Dariusz Roszkowski @@ -191,6 +196,12 @@ Ronny Chevalier Michal Cichon Tero Hanninen + Sergio Gonzalez + Cass Everitt + Engin Manap + Martins Mozeiko + Joseph Thomson + Phil Jordan License: This software is in the public domain. Where that dedication is not @@ -371,6 +382,7 @@ License: // and only if iPhone convert-to-rgb processing is on). // + #define STBI_NO_HDR // RaySan: not required by raylib #define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2 @@ -489,6 +501,8 @@ STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultipl // or just pass them through "as-is" STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes @@ -626,7 +640,38 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI_FREE(p) free(p) #endif -#if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)) +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) #define STBI_SSE2 #include @@ -879,7 +924,14 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); @@ -919,6 +971,53 @@ static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} + + #ifndef STBI_NO_STDIO static FILE *stbi__fopen(char const *filename, char const *mode) @@ -949,7 +1048,7 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi_load_main(&s,x,y,comp,req_comp); + result = stbi__load_flip(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); @@ -962,25 +1061,29 @@ STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, i { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi_load_main(&s,x,y,comp,req_comp); + return stbi__load_flip(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_load_main(&s,x,y,comp,req_comp); + return stbi__load_flip(&s,x,y,comp,req_comp); } #ifndef STBI_NO_LINEAR -static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { unsigned char *data; #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) - return stbi__hdr_load(s,x,y,comp,req_comp); + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } #endif - data = stbi_load_main(s, x, y, comp, req_comp); + data = stbi__load_flip(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -990,14 +1093,14 @@ STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, in { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi_loadf_main(&s,x,y,comp,req_comp); + return stbi__loadf_main(&s,x,y,comp,req_comp); } STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_loadf_main(&s,x,y,comp,req_comp); + return stbi__loadf_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_STDIO @@ -1015,7 +1118,7 @@ STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_ { stbi__context s; stbi__start_file(&s,f); - return stbi_loadf_main(&s,x,y,comp,req_comp); + return stbi__loadf_main(&s,x,y,comp,req_comp); } #endif // !STBI_NO_STDIO @@ -1138,6 +1241,10 @@ stbi_inline static int stbi__at_eof(stbi__context *s) static void stbi__skip(stbi__context *s, int n) { + 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) { @@ -1546,6 +1653,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -1730,15 +1838,12 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) if (stbi__jpeg_get_bit(j)) - { - if ((*p & bit)==0) - { + if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } - } } } else { k = j->spec_start; @@ -1754,8 +1859,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block - } else - r = 16; // r=15 is the code for 16 0s + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit @@ -1767,27 +1875,21 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ // advance by r while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k]]; + short *p = &data[stbi__jpeg_dezigzag[k++]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) - { - if ((*p & bit)==0) - { + if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } - } - ++k; } else { if (r == 0) { - if (s) - data[stbi__jpeg_dezigzag[k++]] = s; + *p = (short) s; break; } --r; - ++k; } } } while (k <= j->spec_end); @@ -2207,7 +2309,7 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) // pass 1 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 dct_trn16(row2, row3); - dct_trn16(row4, row5); + dct_trn16(row4, row5); dct_trn16(row6, row7); // pass 2 @@ -2434,7 +2536,6 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); int y2 = (j*z->img_comp[n].v + y); - //int ha = z->img_comp[n].ha; // RaySan: Unused, commented to avoid warning short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; @@ -2701,6 +2802,10 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); @@ -3013,7 +3118,7 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) 128); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); __m128i xw = _mm_set1_epi16(255); // alpha channel for (; i+7 < count; i += 8) { @@ -3380,7 +3485,8 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) ++sizes[sizelist[i]]; sizes[0] = 0; for (i=1; i < 16; ++i) - STBI_ASSERT(sizes[i] <= (1 << 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; @@ -3388,7 +3494,7 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) z->firstsymbol[i] = (stbi__uint16) k; code = (code + sizes[i]); if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG"); + 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]; @@ -3557,9 +3663,9 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) p = (stbi_uc *) (zout - dist); if (dist == 1) { // run of one byte; common in images. stbi_uc v = *p; - do *zout++ = v; while (--len); + if (len) { do *zout++ = v; while (--len); } } else { - do *zout++ = *p++; while (--len); + if (len) { do *zout++ = *p++; while (--len); } } } } @@ -3587,7 +3693,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) n = 0; while (n < hlit + hdist) { int c = stbi__zhuffman_decode(a, &z_codelength); - STBI_ASSERT(c >= 0 && c < 19); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; else if (c == 16) { @@ -4019,7 +4125,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r cur[i*2+0] = cur[i]; } } else { - assert(img_n == 3); + STBI_ASSERT(img_n == 3); for (i=x-1; i >= 0; --i) { cur[i*4+3] = 255; cur[i*4+2] = cur[i*3+2]; @@ -4284,6 +4390,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) 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) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; @@ -4643,7 +4750,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int } } else { for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s)); + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); @@ -4800,7 +4907,7 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int *y = tga_height; if (comp) *comp = tga_comp; - tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp ); + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) @@ -5461,6 +5568,7 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__gif_lzw *p; lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; @@ -6130,7 +6238,7 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD if (stbi__psd_info(s, x, y, comp)) return 1; #endif - + #ifndef STBI_NO_PIC if (stbi__pic_info(s, x, y, comp)) return 1; #endif @@ -6192,6 +6300,13 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) progressive JPEG (stb) @@ -6276,7 +6391,7 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int 1.21 fix use of 'stbi_uc' in header (reported by jon blow) 1.20 added support for Softimage PIC, by Tom Seddon 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 2008-08-02 + 1.18 (2008-08-02) fix a threading bug (local mutable static) 1.17 support interlaced PNG 1.16 major bugfix - stbi__convert_format converted one too many pixels @@ -6321,5 +6436,6 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 0.51 obey req_comp requests, 1-component jpegs return as 1-component, on 'test' only check type, not whether we support this variant - 0.50 first released version + 0.50 (2006-11-19) + first released version */ diff --git a/src/stb_image_write.h b/src/stb_image_write.h index da3f7e22..10489707 100644 --- a/src/stb_image_write.h +++ b/src/stb_image_write.h @@ -1,16 +1,15 @@ -/* stb_image_write - v0.95 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 no warranty implied; use at your own risk -Before including, + Before #including, - #define STB_IMAGE_WRITE_IMPLEMENTATION + #define STB_IMAGE_WRITE_IMPLEMENTATION -in the file that you want to have the implementation. - -Will probably not work correctly with strict-aliasing optimizations. + in the file that you want to have the implementation. + Will probably not work correctly with strict-aliasing optimizations. ABOUT: @@ -22,16 +21,24 @@ ABOUT: for source code compactness and simplicitly, not optimal image file size or run-time performance. +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + USAGE: - There are three functions, one for each image file format: + There are four functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); Each function returns 0 on failure and non-0 on success. - + The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits @@ -44,13 +51,30 @@ USAGE: PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. - + PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + bugfixes: + github:Chribba */ #ifndef INCLUDE_STB_IMAGE_WRITE_H @@ -60,9 +84,10 @@ USAGE: extern "C" { #endif -extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); #ifdef __cplusplus } @@ -76,7 +101,30 @@ extern int stbi_write_tga(char const *filename, int w, int h, int comp, const vo #include #include #include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,sz) realloc(p,sz) +#define STBIW_FREE(p) free(p) +#endif +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT #include +#define STBIW_ASSERT(x) assert(x) +#endif typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; @@ -95,7 +143,7 @@ static void writefv(FILE *f, const char *fmt, va_list v) b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); fwrite(b,4,1,f); break; } default: - assert(0); + STBIW_ASSERT(0); return; } } @@ -108,7 +156,7 @@ static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) fwrite(arr, 3, 1, f); } -static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) +static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { unsigned char bg[3] = { 255, 0, 255}, px[3]; stbiw_uint32 zero = 0; @@ -117,7 +165,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, if (y <= 0) return; - if (vdir < 0) + if (vdir < 0) j_end = -1, j = y-1; else j_end = y, j = 0; @@ -128,8 +176,12 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, if (write_alpha < 0) fwrite(&d[comp-1], 1, 1, f); switch (comp) { - case 1: - case 2: fwrite(d, 1, 1, f); + case 1: fwrite(d, 1, 1, f); + break; + case 2: if (expand_mono) + write3(f, d[0],d[0],d[0]); // monochrome bmp + else + fwrite(d, 1, 1, f); // monochrome TGA break; case 4: if (!write_alpha) { @@ -151,7 +203,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, } } -static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...) +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { FILE *f; if (y < 0 || x < 0) return 0; @@ -161,7 +213,7 @@ static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, in va_start(v, fmt); writefv(f, fmt, v); va_end(v); - write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); + write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono); fclose(f); } return f != NULL; @@ -170,7 +222,7 @@ static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, in int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; - return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad, + return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header @@ -181,10 +233,159 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0, + return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8); } +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(FILE *f, int length, unsigned char databyte) +{ + unsigned char lengthbyte = (unsigned char) (length+128); + STBIW_ASSERT(length+128 <= 255); + fwrite(&lengthbyte, 1, 1, f); + fwrite(&databyte, 1, 1, f); +} + +void stbiw__write_dump_data(FILE *f, int length, unsigned char *data) +{ + unsigned char lengthbyte = (unsigned char )(length & 0xff); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + fwrite(&lengthbyte, 1, 1, f); + fwrite(data, length, 1, f); +} + +void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (comp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*comp + 2]; + linear[1] = scanline[x*comp + 1]; + linear[0] = scanline[x*comp + 0]; + break; + case 2: /* fallthrough */ + case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + fwrite(rgbe, 4, 1, f); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(comp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*comp + 2]; + linear[1] = scanline[x*comp + 1]; + linear[0] = scanline[x*comp + 0]; + break; + case 2: /* fallthrough */ + case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + fwrite(scanlineheader, 4, 1, f); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(f, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(f, len, comp[x]); + x += len; + } + } + } + } + } +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + int i; + FILE *f; + if (y <= 0 || x <= 0 || data == NULL) return 0; + f = fopen(filename, "wb"); + if (f) { + /* Each component is stored separately. Allocate scratch space for full output scanline. */ + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" ); + fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x); + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + fclose(f); + } + return f != NULL; +} + +///////////////////////////////////////////////////////// +// PNG + // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] @@ -196,13 +397,13 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat #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) ? free(stbiw__sbraw(a)),0 : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = realloc(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); - assert(p); + void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); @@ -287,7 +488,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l i=0; while (i < data_len-3) { - // hash next 3 bytes of data to be compressed + // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; @@ -300,7 +501,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { - memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + STBIW_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); @@ -323,7 +524,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l if (bestloc) { int d = (int) (data+i - bestloc); // distance back - assert(d <= 32767 && best <= 258); + STBIW_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]); @@ -364,7 +565,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l } *out_len = stbiw__sbn(out); // make returned pointer freeable - memmove(stbiw__sbraw(out), out, *out_len); + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); } @@ -411,8 +612,8 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in if (stride_bytes == 0) stride_bytes = x * n; - filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0; - line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; @@ -451,20 +652,20 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in } // when we get here, best contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) best; - memcpy(filt+j*(x*n+1)+1, line_buffer, x*n); + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } - free(line_buffer); + STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory - free(filt); + STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead - out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12); + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; - memcpy(o,sig,8); o+= 8; + STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); @@ -478,14 +679,16 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); - memcpy(o, zlib, zlen); o += zlen; free(zlib); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); - assert(o == out + *out_len); + STBIW_ASSERT(o == out + *out_len); return out; } @@ -497,16 +700,22 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (!png) return 0; f = fopen(filename, "wb"); - if (!f) { free(png); return 0; } + if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); - free(png); + STBIW_FREE(png); return 1; } #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history - + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c index 8bee7e1a..4484b130 100644 --- a/src/stb_vorbis.c +++ b/src/stb_vorbis.c @@ -1,5 +1,389 @@ +// Ogg Vorbis audio decoder - v1.05 - public domain +// http://nothings.org/stb_vorbis/ +// +// Written by Sean Barrett in 2007, last updated in 2014 +// Sponsored by RAD Game Tools. +// +// Placed in the public domain April 2007 by the author: no copyright +// is claimed, and you may use it for any purpose you like. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - seeking not supported except manually via PUSHDATA api +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster "alxprd"@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// (If you reported a bug but do not appear in this list, it is because +// someone else reported the bug before you. There were too many of you to +// list them all because I was lax about updating for a long time, sorry.) +// +// Partial history: +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// 0.99996 - - bracket #include for macintosh compilation +// 0.99995 - - avoid alias-optimization issue in float-to-int conversion +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +// NOTE: Added to work with raylib on Android +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + +// RaySan: Added for Linux +#ifdef __linux + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); -#include "stb_vorbis.h" +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// NOT WORKING YET +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0), but it +// actually works + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of samples per channel. the +// data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. Note that for interleaved data, you pass in the number of +// shorts (the size of your array), but the return value is the number of +// samples per channel, not the total number of samples. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// #ifndef STB_VORBIS_HEADER_ONLY @@ -180,7 +564,7 @@ #define NULL 0 #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !(defined(__MINGW32__) && defined(__forceinline)) #if __GNUC__ #define __forceinline inline #else @@ -3562,7 +3946,7 @@ static int start_decoder(vorb *f) g->sorted_order[j] = (uint8) p[j].y; // precompute the neighbors for (j=2; j < g->values; ++j) { - int low = 0,hi = 0; + int low,hi; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -5024,6 +5408,7 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in #endif // STB_VORBIS_NO_PULLDATA_API /* Version history + 1.05 - 2015/04/19 - don't define __forceinline if it's redundant 1.04 - 2014/08/27 - fix missing const-correct case in API 1.03 - 2014/08/07 - Warning fixes 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h deleted file mode 100644 index 8b91bd36..00000000 --- a/src/stb_vorbis.h +++ /dev/null @@ -1,384 +0,0 @@ -// Ogg Vorbis audio decoder - v1.04 - public domain -// http://nothings.org/stb_vorbis/ -// -// Written by Sean Barrett in 2007, last updated in 2014 -// Sponsored by RAD Game Tools. -// -// Placed in the public domain April 2007 by the author: no copyright -// is claimed, and you may use it for any purpose you like. -// -// No warranty for any purpose is expressed or implied by the author (nor -// by RAD Game Tools). Report bugs and send enhancements to the author. -// -// Limitations: -// -// - seeking not supported except manually via PUSHDATA api -// - floor 0 not supported (used in old ogg vorbis files pre-2004) -// - lossless sample-truncation at beginning ignored -// - cannot concatenate multiple vorbis streams -// - sample positions are 32-bit, limiting seekable 192Khz -// files to around 6 hours (Ogg supports 64-bit) -// -// Bugfix/warning contributors: -// Terje Mathisen Niklas Frykholm Andy Hill -// Casey Muratori John Bolton Gargaj -// Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster "alxprd"@github -// Tom Beaumont Ingo Leitgeb -// (If you reported a bug but do not appear in this list, it is because -// someone else reported the bug before you. There were too many of you to -// list them all because I was lax about updating for a long time, sorry.) -// -// Partial history: -// 1.04 - 2014/08/27 - fix missing const-correct case in API -// 1.03 - 2014/08/07 - warning fixes -// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows -// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) -// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; -// (API change) report sample rate for decode-full-file funcs -// 0.99996 - - bracket #include for macintosh compilation -// 0.99995 - - avoid alias-optimization issue in float-to-int conversion -// -// See end of file for full version history. - - -////////////////////////////////////////////////////////////////////////////// -// -// HEADER BEGINS HERE -// - -#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define STB_VORBIS_INCLUDE_STB_VORBIS_H - -#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) -#define STB_VORBIS_NO_STDIO 1 -#endif - -#ifndef STB_VORBIS_NO_STDIO -#include -#endif - -// NOTE: Added to work with raylib on Android -#if defined(PLATFORM_ANDROID) - #include "utils.h" // Android fopen function map -#endif - -#ifdef __linux - #include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/////////// THREAD SAFETY - -// Individual stb_vorbis* handles are not thread-safe; you cannot decode from -// them from multiple threads at the same time. However, you can have multiple -// stb_vorbis* handles and decode from them independently in multiple thrads. - - -/////////// MEMORY ALLOCATION - -// normally stb_vorbis uses malloc() to allocate memory at startup, -// and alloca() to allocate temporary memory during a frame on the -// stack. (Memory consumption will depend on the amount of setup -// data in the file and how you set the compile flags for speed -// vs. size. In my test files the maximal-size usage is ~150KB.) -// -// You can modify the wrapper functions in the source (setup_malloc, -// setup_temp_malloc, temp_malloc) to change this behavior, or you -// can use a simpler allocation model: you pass in a buffer from -// which stb_vorbis will allocate _all_ its memory (including the -// temp memory). "open" may fail with a VORBIS_outofmem if you -// do not pass in enough data; there is no way to determine how -// much you do need except to succeed (at which point you can -// query get_info to find the exact amount required. yes I know -// this is lame). -// -// If you pass in a non-NULL buffer of the type below, allocation -// will occur from it as described above. Otherwise just pass NULL -// to use malloc()/alloca() - -typedef struct -{ - char *alloc_buffer; - int alloc_buffer_length_in_bytes; -} stb_vorbis_alloc; - - -/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES - -typedef struct stb_vorbis stb_vorbis; - -typedef struct -{ - unsigned int sample_rate; - int channels; - - unsigned int setup_memory_required; - unsigned int setup_temp_memory_required; - unsigned int temp_memory_required; - - int max_frame_size; -} stb_vorbis_info; - -// get general information about the file -extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); - -// get the last error detected (clears it, too) -extern int stb_vorbis_get_error(stb_vorbis *f); - -// close an ogg vorbis file and free all memory in use -extern void stb_vorbis_close(stb_vorbis *f); - -// this function returns the offset (in samples) from the beginning of the -// file that will be returned by the next decode, if it is known, or -1 -// otherwise. after a flush_pushdata() call, this may take a while before -// it becomes valid again. -// NOT WORKING YET after a seek with PULLDATA API -extern int stb_vorbis_get_sample_offset(stb_vorbis *f); - -// returns the current seek point within the file, or offset from the beginning -// of the memory buffer. In pushdata mode it returns 0. -extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); - -/////////// PUSHDATA API - -#ifndef STB_VORBIS_NO_PUSHDATA_API - -// this API allows you to get blocks of data from any source and hand -// them to stb_vorbis. you have to buffer them; stb_vorbis will tell -// you how much it used, and you have to give it the rest next time; -// and stb_vorbis may not have enough data to work with and you will -// need to give it the same data again PLUS more. Note that the Vorbis -// specification does not bound the size of an individual frame. - -extern stb_vorbis *stb_vorbis_open_pushdata( - unsigned char *datablock, int datablock_length_in_bytes, - int *datablock_memory_consumed_in_bytes, - int *error, - stb_vorbis_alloc *alloc_buffer); -// create a vorbis decoder by passing in the initial data block containing -// the ogg&vorbis headers (you don't need to do parse them, just provide -// the first N bytes of the file--you're told if it's not enough, see below) -// on success, returns an stb_vorbis *, does not set error, returns the amount of -// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; -// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed -// if returns NULL and *error is VORBIS_need_more_data, then the input block was -// incomplete and you need to pass in a larger block from the start of the file - -extern int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ); -// decode a frame of audio sample data if possible from the passed-in data block -// -// return value: number of bytes we used from datablock -// -// possible cases: -// 0 bytes used, 0 samples output (need more data) -// N bytes used, 0 samples output (resynching the stream, keep going) -// N bytes used, M samples output (one frame of data) -// note that after opening a file, you will ALWAYS get one N-bytes,0-sample -// frame, because Vorbis always "discards" the first frame. -// -// Note that on resynch, stb_vorbis will rarely consume all of the buffer, -// instead only datablock_length_in_bytes-3 or less. This is because it wants -// to avoid missing parts of a page header if they cross a datablock boundary, -// without writing state-machiney code to record a partial detection. -// -// The number of channels returned are stored in *channels (which can be -// NULL--it is always the same as the number of channels reported by -// get_info). *output will contain an array of float* buffers, one per -// channel. In other words, (*output)[0][0] contains the first sample from -// the first channel, and (*output)[1][0] contains the first sample from -// the second channel. - -extern void stb_vorbis_flush_pushdata(stb_vorbis *f); -// inform stb_vorbis that your next datablock will not be contiguous with -// previous ones (e.g. you've seeked in the data); future attempts to decode -// frames will cause stb_vorbis to resynchronize (as noted above), and -// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it -// will begin decoding the _next_ frame. -// -// if you want to seek using pushdata, you need to seek in your file, then -// call stb_vorbis_flush_pushdata(), then start calling decoding, then once -// decoding is returning you data, call stb_vorbis_get_sample_offset, and -// if you don't like the result, seek your file again and repeat. -#endif - - -////////// PULLING INPUT API - -#ifndef STB_VORBIS_NO_PULLDATA_API -// This API assumes stb_vorbis is allowed to pull data from a source-- -// either a block of memory containing the _entire_ vorbis stream, or a -// FILE * that you or it create, or possibly some other reading mechanism -// if you go modify the source to replace the FILE * case with some kind -// of callback to your code. (But if you don't support seeking, you may -// just want to go ahead and use pushdata.) - -#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); -#endif -#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); -#endif -// decode an entire file and output the data interleaved into a malloc()ed -// buffer stored in *output. The return value is the number of samples -// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. -// When you're done with it, just free() the pointer returned in *output. - -extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an ogg vorbis stream in memory (note -// this must be the entire stream!). on failure, returns NULL and sets *error - -#ifndef STB_VORBIS_NO_STDIO -extern stb_vorbis * stb_vorbis_open_filename(const char *filename, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from a filename via fopen(). on failure, -// returns NULL and sets *error (possibly to VORBIS_file_open_failure). - -extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell). on failure, returns NULL and sets *error. -// note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to -// perform stb_vorbis_seek_*() operations on this file, it will assume it -// owns the _entire_ rest of the file after the start point. Use the next -// function, stb_vorbis_open_file_section(), to limit it. - -extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, - int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell); the stream will be of length 'len' bytes. -// on failure, returns NULL and sets *error. note that stb_vorbis must "own" -// this stream; if you seek it in between calls to stb_vorbis, it will become -// confused. -#endif - -extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); -extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); -// NOT WORKING YET -// these functions seek in the Vorbis file to (approximately) 'sample_number'. -// after calling seek_frame(), the next call to get_frame_*() will include -// the specified sample. after calling stb_vorbis_seek(), the next call to -// stb_vorbis_get_samples_* will start with the specified sample. If you -// do not need to seek to EXACTLY the target sample when using get_samples_*, -// you can also use seek_frame(). - -extern void stb_vorbis_seek_start(stb_vorbis *f); -// this function is equivalent to stb_vorbis_seek(f,0), but it -// actually works - -extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); -extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); -// these functions return the total length of the vorbis stream - -extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); -// decode the next frame and return the number of samples. the number of -// channels returned are stored in *channels (which can be NULL--it is always -// the same as the number of channels reported by get_info). *output will -// contain an array of float* buffers, one per channel. These outputs will -// be overwritten on the next call to stb_vorbis_get_frame_*. -// -// You generally should not intermix calls to stb_vorbis_get_frame_*() -// and stb_vorbis_get_samples_*(), since the latter calls the former. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); -extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); -#endif -// decode the next frame and return the number of samples per channel. the -// data is coerced to the number of channels you request according to the -// channel coercion rules (see below). You must pass in the size of your -// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. -// The maximum buffer size needed can be gotten from get_info(); however, -// the Vorbis I specification implies an absolute maximum of 4096 samples -// per channel. Note that for interleaved data, you pass in the number of -// shorts (the size of your array), but the return value is the number of -// samples per channel, not the total number of samples. - -// Channel coercion rules: -// Let M be the number of channels requested, and N the number of channels present, -// and Cn be the nth channel; let stereo L be the sum of all L and center channels, -// and stereo R be the sum of all R and center channels (channel assignment from the -// vorbis spec). -// M N output -// 1 k sum(Ck) for all k -// 2 * stereo L, stereo R -// k l k > l, the first l channels, then 0s -// k l k <= l, the first k channels -// Note that this is not _good_ surround etc. mixing at all! It's just so -// you get something useful. - -extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); -extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. -// Returns the number of samples stored per channel; it may be less than requested -// at the end of the file. If there are no more samples in the file, returns 0. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); -extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); -#endif -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. Applies the coercion rules above -// to produce 'channels' channels. Returns the number of samples stored per channel; -// it may be less than requested at the end of the file. If there are no more -// samples in the file, returns 0. - -#endif - -//////// ERROR CODES - -enum STBVorbisError -{ - VORBIS__no_error, - - VORBIS_need_more_data=1, // not a real error - - VORBIS_invalid_api_mixing, // can't mix API modes - VORBIS_outofmem, // not enough memory - VORBIS_feature_not_supported, // uses floor 0 - VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small - VORBIS_file_open_failure, // fopen() failed - VORBIS_seek_without_length, // can't seek in unknown-length file - - VORBIS_unexpected_eof=10, // file is truncated? - VORBIS_seek_invalid, // seek past EOF - - // decoding errors (corrupt/invalid stream) -- you probably - // don't care about the exact details of these - - // vorbis errors: - VORBIS_invalid_setup=20, - VORBIS_invalid_stream, - - // ogg errors: - VORBIS_missing_capture_pattern=30, - VORBIS_invalid_stream_structure_version, - VORBIS_continued_packet_flag_invalid, - VORBIS_incorrect_stream_serial_number, - VORBIS_invalid_first_page, - VORBIS_bad_packet_type, - VORBIS_cant_find_last_page, - VORBIS_seek_failed, -}; - - -#ifdef __cplusplus -} -#endif - -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H -// -// HEADER ENDS HERE -// -////////////////////////////////////////////////////////////////////////////// \ No newline at end of file -- cgit v1.2.3 From 19721c70ff8f8567a27f378102f8aa460f344e4c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 11 May 2015 18:22:29 +0200 Subject: Removed unused shaders --- src/simple150.frag | 14 -------------- src/simple150.vert | 21 --------------------- 2 files changed, 35 deletions(-) delete mode 100644 src/simple150.frag delete mode 100644 src/simple150.vert (limited to 'src') diff --git a/src/simple150.frag b/src/simple150.frag deleted file mode 100644 index 74a727dd..00000000 --- a/src/simple150.frag +++ /dev/null @@ -1,14 +0,0 @@ -#version 150 - -uniform sampler2D texture0; - -in vec2 fragTexCoord; -in vec4 fragColor; - -out vec4 pixelColor; - -void main() -{ - // Output pixel color - pixelColor = texture(texture0, fragTexCoord) * fragColor; -} \ No newline at end of file diff --git a/src/simple150.vert b/src/simple150.vert deleted file mode 100644 index 8c23b731..00000000 --- a/src/simple150.vert +++ /dev/null @@ -1,21 +0,0 @@ -#version 150 - -uniform mat4 projectionMatrix; -uniform mat4 modelviewMatrix; - -in vec3 vertexPosition; -in vec2 vertexTexCoord; -in vec4 vertexColor; - -out vec2 fragTexCoord; -out vec4 fragColor; - -void main() -{ - // Pass some variables to the fragment shader - fragTexCoord = vertexTexCoord; - fragColor = vertexColor; - - // Apply all matrix transformations to vertex - gl_Position = projectionMatrix * modelviewMatrix * vec4(vertexPosition, 1.0); -} \ No newline at end of file -- cgit v1.2.3 From c7ae28aaa2d2cb0da8bf3892720f5cb12ccb56ba Mon Sep 17 00:00:00 2001 From: Gatonevermind Date: Mon, 18 May 2015 13:12:24 +0200 Subject: Small tweak --- src/rlgl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index 7a73623c..7349af53 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1618,7 +1618,10 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma glGenTextures(1, &id); // Generate Pointer to the texture - //glActiveTexture(GL_TEXTURE0); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + //glActiveTexture(GL_TEXTURE0); // If not defined, using GL_TEXTURE0 by default (shader texture) +#endif + glBindTexture(GL_TEXTURE_2D, id); #if defined(GRAPHICS_API_OPENGL_33) -- cgit v1.2.3 From 9e450df053c9b6f13a89553d65425c350d4399dd Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 21 May 2015 00:18:22 +0200 Subject: Added extensions check on rlglInit() and more Corrected shader version depending on OGL version Corrected bug in gestures module --- src/core.c | 2 +- src/gestures.c | 12 +- src/rlgl.c | 418 +++++++++++++++++++++++++++++++++------------------------ 3 files changed, 252 insertions(+), 180 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index cc8f31fd..ff704ed4 100644 --- a/src/core.c +++ b/src/core.c @@ -211,7 +211,7 @@ extern void InitAndroidGestures(struct android_app *app); #endif #if defined(PLATFORM_WEB) -extern void InitWebGestures(void); +extern void InitWebGestures(void); // [Module: gestures] Initializes emscripten gestures for web #endif //---------------------------------------------------------------------------------- diff --git a/src/gestures.c b/src/gestures.c index 2574aa02..55bae328 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -531,6 +531,7 @@ static float DotProduct(Vector2 v1, Vector2 v2) static double GetCurrentTime() { + double time = 0; #if defined(_WIN32) /* // NOTE: Requires Windows.h @@ -538,7 +539,7 @@ static double GetCurrentTime() GetSystemTimePreciseAsFileTime(&tm); ULONGLONG nowTime = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; // Time provided in 100-nanosecond intervals - return ((double)nowTime/10000000.0); // Return time in seconds + time = ((double)nowTime/10000000.0); // time in seconds */ #endif @@ -548,8 +549,10 @@ static double GetCurrentTime() clock_gettime(CLOCK_MONOTONIC, &now); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time provided in nanoseconds - return ((double)nowTime/1000000.0); // Return time in miliseconds + time = ((double)nowTime/1000000.0); // time in miliseconds #endif + + return time; } #if defined(PLATFORM_ANDROID) @@ -618,9 +621,6 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); } */ - - touchPosition = gestureEvent.position[0]; - GestureEvent gestureEvent; // Action @@ -635,6 +635,8 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY }; gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY }; + touchPosition = gestureEvent.position[0]; + ProcessMotionEvent(gestureEvent); return 1; diff --git a/src/rlgl.c b/src/rlgl.c index 7349af53..49e51406 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -30,7 +30,7 @@ #include // Standard input / output lib #include // Declares malloc() and free() for memory management, rand() -#include // Declares strcmp(), strlen(), strtok(), strdup() +#include // Declares strcmp(), strlen(), strtok() #if defined(GRAPHICS_API_OPENGL_11) #ifdef __APPLE__ // OpenGL include for OSX @@ -216,8 +216,16 @@ static Vector3 *tempBuffer; static int tempBufferCount = 0; static bool useTempBuffer = false; -// Support for VAOs (OpenGL ES2 could not support VAO extensions) -static bool vaoSupported = false; +// Flags for supported extensions +static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) +static bool npotSupported = false; // NPOT textures full support + +// Compressed textures support flags +static bool texCompDXTSupported = false; // DDS texture compression support +static bool texCompETC1Supported = false; // ETC1 texture compression support +static bool texCompETC2Supported = false; // ETC2/EAC texture compression support +static bool texCompPVRTSupported = false; // PVR texture compression support +static bool texCompASTCSupported = false; // ASTC texture compression support // Framebuffer object and texture static GLuint fbo, fboColorTexture, fboDepthTexture; @@ -226,19 +234,16 @@ static Model postproQuad; #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) -// emscripten does not support VAOs static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; -static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; +//static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: Fails in WebGL, omitted #endif // White texture useful for plain color polys (required by shader) // NOTE: It's required in shapes and models modules! unsigned int whiteTexture; -static bool supportedTextureFormat[32]; - //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -249,6 +254,8 @@ static void InitializeBuffers(void); static void InitializeBuffersGPU(void); static void UpdateBuffers(void); static char *TextFileRead(char *fn); + +static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); #endif #if defined(GRAPHICS_API_OPENGL_11) @@ -256,7 +263,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); #endif -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); +static char** StringSplit(char *baseString, const char delimiter, int *numExt); //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations @@ -795,58 +802,9 @@ int rlGetVersion(void) // Init OpenGL 3.3+ required data void rlglInit(void) { -#if defined(GRAPHICS_API_OPENGL_33) - // Loading extensions the hard way (Example) -/* - GLint numExt; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); - - for (int i = 0; i < numExt; i++) - { - const GLubyte *extensionName = glGetStringi(GL_EXTENSIONS, i); - if (strcmp(extensionName, (const GLubyte *)"GL_ARB_vertex_array_object") == 0) - { - // The extension is supported by our hardware and driver, try to get related functions pointers - glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)wglGetProcAddress("glGenVertexArrays"); - glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)wglGetProcAddress("glBindVertexArray"); - glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)wglGetProcAddress("glDeleteVertexArrays"); - glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)wglGetProcAddress("glIsVertexArray"); - } - } -*/ - - // Initialize extensions using GLEW - glewExperimental = 1; // Needed for core profile - - GLenum error = glewInit(); - - if (error != GLEW_OK) TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error)); - - if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 extensions supported"); - - // NOTE: GLEW is a big library that loads ALL extensions, using glad we can only load required ones... - //if (!gladLoadGL()) TraceLog("ERROR: Failed to initialize glad\n"); - - vaoSupported = true; -#endif - -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... -#if !defined(PLATFORM_WEB) - glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); - glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); - glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); - glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); -#endif - - if (glGenVertexArrays == NULL) TraceLog(WARNING, "Could not initialize VAO extensions, VAOs not supported"); - else - { - vaoSupported = true; - TraceLog(INFO, "VAO extensions initialized successfully"); - } -#endif - + // Check OpenGL information and capabilities + //------------------------------------------------------------------------------ + // Print current OpenGL and GLSL version TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); @@ -857,80 +815,129 @@ void rlglInit(void) //int maxTexSize; //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); //TraceLog(INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); + + //GL_MAX_TEXTURE_IMAGE_UNITS + //GL_MAX_VIEWPORT_DIMS //int numAuxBuffers; //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); //TraceLog(INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); + + //GLint numComp = 0; + //GLint format[32] = { 0 }; + //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); + //glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); + //for (int i = 0; i < numComp; i++) TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); - // Show supported extensions // NOTE: We don't need that much data on screen... right now... - - // Check available extensions for compressed textures support - for (int i = 0; i < 32; i++) supportedTextureFormat[i] = false; -#if defined(GRAPHICS_API_OPENGL_33) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Get supported extensions list GLint numExt; + +#if defined(GRAPHICS_API_OPENGL_33) + // Initialize extensions using GLEW + glewExperimental = 1; // Needed for core profile + + GLenum error = glewInit(); + + if (error != GLEW_OK) TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error)); + + if (glewIsSupported("GL_VERSION_3_3")) + { + TraceLog(INFO, "OpenGL 3.3 Core profile"); + + vaoSupported = true; + npotSupported = true; + } + + // NOTE: GLEW is a big library that loads ALL extensions, we can use some alternative to load only required ones + // Alternatives: glLoadGen, glad, libepoxy + //if (!gladLoadGL()) TraceLog("ERROR: Failed to initialize glad\n"); + + // With GLEW we can check if an extension has been loaded in two ways: + //if (GLEW_ARB_vertex_array_object) { } + //if (glewIsSupported("GL_ARB_vertex_array_object")) { } + + + // NOTE: We don't need to check again supported extensions but we do (in case GLEW is replaced sometime) + // We get a list of available extensions and we check for some of them (compressed textures) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + const char *ext[numExt]; + + for (int i = 0; i < numExt; i++) ext[i] = (char *)glGetStringi(GL_EXTENSIONS, i); + +#elif defined(GRAPHICS_API_OPENGL_ES2) + char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string + + // NOTE: String could be splitted using strtok() function (string.h) + char **ext = StringSplit(extensions, ' ', &numExt); +#endif + + TraceLog(INFO, "Number of supported extensions: %i", numExt); + // Show supported extensions + for (int i = 0; i < numExt; i++) TraceLog(INFO, "Supported extension: %s", ext[i]); + + // Check required extensions for (int i = 0; i < numExt; i++) { - //TraceLog(INFO, "Supported extension: %s", glGetStringi(GL_EXTENSIONS, i)); - - if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_EXT_texture_compression_s3tc") == 0) - { - // DDS texture compression support - supportedTextureFormat[COMPRESSED_DXT1_RGB] = true; - supportedTextureFormat[COMPRESSED_DXT1_RGBA] = true; - supportedTextureFormat[COMPRESSED_DXT3_RGBA] = true; - supportedTextureFormat[COMPRESSED_DXT5_RGBA] = true; - } - else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i), "GL_OES_compressed_ETC1_RGB8_texture") == 0) - { - // ETC1 texture compression support - supportedTextureFormat[COMPRESSED_ETC1_RGB] = true; - } - else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_ARB_ES3_compatibility") == 0) - { - // ETC2/EAC texture compression support - supportedTextureFormat[COMPRESSED_ETC2_RGB] = true; - supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA] = true; - } - else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_IMG_texture_compression_pvrtc") == 0) - { - // PVR texture compression support - supportedTextureFormat[COMPRESSED_PVRT_RGB] = true; - supportedTextureFormat[COMPRESSED_PVRT_RGBA] = true; - } - else if (strcmp((char *)glGetStringi(GL_EXTENSIONS, i),"GL_KHR_texture_compression_astc_hdr") == 0) +#if defined(GRAPHICS_API_OPENGL_ES2) + // Check VAO support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has VAO support as core feature + if (strcmp(ext[i], (const char *)"GL_OES_vertex_array_object") == 0) { - // ASTC texture compression support - supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA] = true; - supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA] = true; + vaoSupported = true; + + // The extension is supported by our hardware and driver, try to get related functions pointers + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted } - } -#elif defined(GRAPHICS_API_OPENGL_ES2) - char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big string + + // Check NPOT textures support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature + if (strcmp(ext[i], (const char *)"GL_OES_texture_npot") == 0) npotSupported = true; +#endif + + // DDS texture compression support + if (strcmp(ext[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) texCompDXTSupported = true; + + // ETC1 texture compression support + if (strcmp(ext[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) texCompETC1Supported = true; - // NOTE: String could be splitted using strtok() function (string.h) - TraceLog(INFO, "Supported extension: %s", extensions); + // ETC2/EAC texture compression support + if (strcmp(ext[i], (const char *)"GL_ARB_ES3_compatibility") == 0) texCompETC2Supported = true; + + // PVR texture compression support + if (strcmp(ext[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) texCompPVRTSupported = true; + + // ASTC texture compression support + if (strcmp(ext[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; + } - //char** ext = StringSplit(extensions, ' '); - //for (int i = 0; i < numExt; i++) printf("%s", ext[i]); +#if defined(GRAPHICS_API_OPENGL_ES2) + if (vaoSupported) TraceLog(INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); + else TraceLog(WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); -#endif -/* - GLint numComp = 0; - glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); + if (npotSupported) TraceLog(INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); + else TraceLog(WARNING, "[EXTENSION] NPOT textures extension not found, NPOT textures not supported"); - GLint format[32] = { 0 }; - glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); + // Once supported extensions have been checked, we should free strings memory + free(ext); +#endif - for (int i = 0; i < numComp; i++) - { - TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); - } -*/ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (texCompDXTSupported) TraceLog(INFO, "[EXTENSION] DXT compressed textures supported"); + if (texCompETC1Supported) TraceLog(INFO, "[EXTENSION] ETC1 compressed textures supported"); + if (texCompETC2Supported) TraceLog(INFO, "[EXTENSION] ETC2/EAC compressed textures supported"); + if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported"); + if (texCompASTCSupported) TraceLog(INFO, "[EXTENSION] ASTC compressed textures supported"); + + // Initialize buffers, default shaders and default textures + //---------------------------------------------------------- + // Set default draw mode currentDrawMode = RL_TRIANGLES; @@ -1435,6 +1442,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) } // Get world coordinates from screen coordinates +// TODO: It doesn't work! It drives me crazy! Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) { //GLint viewport[4]; @@ -1601,19 +1609,13 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma { glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding - GLuint id; + GLuint id = 0; - // Check compressed textures support by OpenGL 1.1 - if (rlGetVersion() == OPENGL_11) + // Check texture format support by OpenGL 1.1 (compressed textures not supported) + if ((rlGetVersion() == OPENGL_11) && (textureFormat >= 8)) { - if ((textureFormat == COMPRESSED_ETC1_RGB) || (textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA) || - (textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA) || - (textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA)) - { - id = 0; - TraceLog(WARNING, "Required GPU compressed texture format not supported"); - return id; - } + TraceLog(WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); + return id; } glGenTextures(1, &id); // Generate Pointer to the texture @@ -1657,22 +1659,23 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } break; + case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; - case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; - case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[COMPRESSED_ETC2_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 default: TraceLog(WARNING, "Texture format not recognized"); break; } #elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) @@ -1686,17 +1689,19 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case COMPRESSED_DXT1_RGB: if (supportedTextureFormat[COMPRESSED_DXT1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: if (supportedTextureFormat[COMPRESSED_DXT1_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: if (supportedTextureFormat[COMPRESSED_DXT3_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_DXT5_RGBA: if (supportedTextureFormat[COMPRESSED_DXT5_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_ETC1_RGB: if (supportedTextureFormat[COMPRESSED_ETC1_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (supportedTextureFormat[COMPRESSED_ETC2_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (supportedTextureFormat[COMPRESSED_ETC2_EAC_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (supportedTextureFormat[COMPRESSED_PVRT_RGB]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (supportedTextureFormat[COMPRESSED_PVRT_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_4x4_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (supportedTextureFormat[COMPRESSED_ASTC_8x8_RGBA]) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 +#if defined(GRAPHICS_API_OPENGL_ES2) + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 +#endif default: TraceLog(WARNING, "Texture format not supported"); break; } #endif @@ -1778,7 +1783,8 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); - TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); + if (id > 0) TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); + else TraceLog(WARNING, "Texture could not be created"); return id; } @@ -2154,37 +2160,45 @@ static Shader LoadDefaultShader(void) // Vertex shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = " #version 110 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char vShaderStr[] = " #version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 tintColor; \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! -#endif - "uniform mat4 projectionMatrix; \n" - "uniform mat4 modelviewMatrix; \n" + char vShaderStr[] = " #version 100 \n" "attribute vec3 vertexPosition; \n" "attribute vec2 vertexTexCoord; \n" "attribute vec4 vertexColor; \n" "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" + "varying vec4 tintColor; \n" +#endif + "uniform mat4 projectionMatrix; \n" + "uniform mat4 modelviewMatrix; \n" "void main() \n" "{ \n" " fragTexCoord = vertexTexCoord; \n" - " fragColor = vertexColor; \n" - " gl_Position = projectionMatrix * modelviewMatrix * vec4(vertexPosition, 1.0); \n" + " tintColor = vertexColor; \n" + " gl_Position = projectionMatrix*modelviewMatrix*vec4(vertexPosition, 1.0); \n" "} \n"; // Fragment shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = " #version 110 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char fShaderStr[] = " #version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 tintColor; \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! - "precision mediump float; \n" // WebGL, required for emscripten + char fShaderStr[] = " #version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) + "varying vec2 fragTexCoord; \n" + "varying vec4 tintColor; \n" #endif "uniform sampler2D texture0; \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" "void main() \n" "{ \n" - " gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead + " gl_FragColor = texelColor*tintColor; \n" "} \n"; shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); @@ -2222,35 +2236,41 @@ static Shader LoadSimpleShader(void) // Vertex shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char vShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char vShaderStr[] = " #version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec3 vertexNormal; \n" + "out vec2 fragTexCoord; \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char vShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! -#endif - "uniform mat4 projectionMatrix; \n" - "uniform mat4 modelviewMatrix; \n" + char vShaderStr[] = " #version 100 \n" "attribute vec3 vertexPosition; \n" "attribute vec2 vertexTexCoord; \n" "attribute vec3 vertexNormal; \n" "varying vec2 fragTexCoord; \n" +#endif + "uniform mat4 projectionMatrix; \n" + "uniform mat4 modelviewMatrix; \n" "void main() \n" "{ \n" " fragTexCoord = vertexTexCoord; \n" - " gl_Position = projectionMatrix * modelviewMatrix * vec4(vertexPosition, 1.0); \n" + " gl_Position = projectionMatrix*modelviewMatrix*vec4(vertexPosition, 1.0); \n" "} \n"; // Fragment shader directly defined, no external file required #if defined(GRAPHICS_API_OPENGL_33) - char fShaderStr[] = " #version 330 \n" // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2) + char fShaderStr[] = " #version 330 \n" + "in vec2 fragTexCoord; \n" #elif defined(GRAPHICS_API_OPENGL_ES2) - char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! + char fShaderStr[] = " #version 100 \n" "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) + "varying vec2 fragTexCoord; \n" #endif "uniform sampler2D texture0; \n" - "varying vec2 fragTexCoord; \n" "uniform vec4 tintColor; \n" "void main() \n" "{ \n" - " gl_FragColor = texture2D(texture0, fragTexCoord) * tintColor; \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead + " gl_FragColor = texelColor*tintColor; \n" "} \n"; shader.id = rlglLoadShaderFromText(vShaderStr, fShaderStr); @@ -2692,4 +2712,54 @@ void TraceLog(int msgType, const char *text, ...) if (msgType == ERROR) exit(1); } -#endif \ No newline at end of file +#endif + +static char **StringSplit(char *baseString, const char delimiter, int *numExt) +{ + char **result = 0; + int count = 0; + char *tmp = baseString; + char *lastComma = 0; + char delim[2]; + + delim[0] = delimiter; + delim[1] = 0; + + // Count how many elements will be extracted + while (*tmp) + { + if (delimiter == *tmp) + { + count++; + lastComma = tmp; + } + + tmp++; + } + + // Add space for trailing token + count += lastComma < (baseString + strlen(baseString) - 1); + + // Add space for terminating null string + count++; + + result = malloc(sizeof(char *)*count); + + if (result) + { + int idx = 0; + char *token = strtok(baseString, delim); + + while (token) + { + *(result + idx++) = token; + token = strtok(0, delim); + } + + *(result + idx) = 0; + } + + *numExt = (count - 1); + + return result; +} \ No newline at end of file -- cgit v1.2.3 From fd851d1d8b4919d802bcc5041f9a9ff37669d159 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 21 May 2015 14:13:51 +0200 Subject: Improved custom shaders support Corrected issues with textures loading --- src/audio.c | 4 +- src/raylib.h | 4 + src/rlgl.c | 69 +++++++++- src/stb_vorbis.c | 387 +------------------------------------------------------ src/stb_vorbis.h | 386 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/textures.c | 24 +++- 6 files changed, 478 insertions(+), 396 deletions(-) create mode 100644 src/stb_vorbis.h (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 2bed0a59..9653c091 100644 --- a/src/audio.c +++ b/src/audio.c @@ -39,8 +39,8 @@ #include "utils.h" // rRES data decompression utility function // NOTE: Includes Android fopen function map -#define STB_VORBIS_HEADER_ONLY -#include "stb_vorbis.c" // OGG loading functions +//#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.h" // OGG loading functions //---------------------------------------------------------------------------------- // Defines and Macros diff --git a/src/raylib.h b/src/raylib.h index 0eafa390..851521e4 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -430,6 +430,10 @@ void SetPawnControl(int pawnControlKey); void SetFnControl(int fnControlKey); void SetSmoothZoomControl(int smoothZoomControlKey); +int GetShaderLocation(Shader shader, const char *uniformName); +void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); +void SetShaderTexture(Shader shader, int uniformLoc, Texture2D texture); + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ diff --git a/src/rlgl.c b/src/rlgl.c index 49e51406..6a938df3 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1618,10 +1618,41 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma return id; } + if ((!texCompDXTSupported) && ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT1_RGBA) || + (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA))) + { + TraceLog(WARNING, "DXT compressed texture format not supported"); + return id; + } + + if ((!texCompETC1Supported) && (textureFormat == COMPRESSED_ETC1_RGB)) + { + TraceLog(WARNING, "ETC1 compressed texture format not supported"); + return id; + } + + if ((!texCompETC2Supported) && ((textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA))) + { + TraceLog(WARNING, "ETC2 compressed texture format not supported"); + return id; + } + + if ((!texCompPVRTSupported) && ((textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA))) + { + TraceLog(WARNING, "PVRT compressed texture format not supported"); + return id; + } + + if ((!texCompASTCSupported) && ((textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA))) + { + TraceLog(WARNING, "ASTC compressed texture format not supported"); + return id; + } + glGenTextures(1, &id); // Generate Pointer to the texture #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - //glActiveTexture(GL_TEXTURE0); // If not defined, using GL_TEXTURE0 by default (shader texture) + //glActiveTexture(GL_TEXTURE0); // If not defined, using GL_TEXTURE0 by default (shader texture) #endif glBindTexture(GL_TEXTURE_2D, id); @@ -2095,6 +2126,42 @@ void rlglSetDefaultShader(void) #endif } +int GetShaderLocation(Shader shader, const char *uniformName) +{ + int location = 0; + + location = glGetUniformLocation(shader.id, uniformName); + + if (location == 0) TraceLog(WARNING, "[SHDR %i] Shader location for %s could not be found", shader.id, uniformName); + + return location; +} + +void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) +{ + glUseProgram(shader.id); + + if (size == 1) glUniform1fv(uniformLoc, 1, value); // Shader uniform type: float + else if (size == 2) glUniform2fv(uniformLoc, 1, value); // Shader uniform type: vec2 + else if (size == 3) glUniform3fv(uniformLoc, 1, value); // Shader uniform type: vec3 + else if (size == 4) glUniform4fv(uniformLoc, 1, value); // Shader uniform type: vec4 + else TraceLog(WARNING, "Shader value float array size not recognized"); + + glUseProgram(0); +} + +void SetShaderTexture(Shader shader, int uniformLoc, Texture2D texture) +{ + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture.id); + + glUniform1i(uniformLoc, 1); // Texture fits in texture unit 1 (Check glActiveTexture()) + + glActiveTexture(GL_TEXTURE0); + glUseProgram(0); +} + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix(void) { diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c index 4484b130..e2700157 100644 --- a/src/stb_vorbis.c +++ b/src/stb_vorbis.c @@ -1,389 +1,4 @@ -// Ogg Vorbis audio decoder - v1.05 - public domain -// http://nothings.org/stb_vorbis/ -// -// Written by Sean Barrett in 2007, last updated in 2014 -// Sponsored by RAD Game Tools. -// -// Placed in the public domain April 2007 by the author: no copyright -// is claimed, and you may use it for any purpose you like. -// -// No warranty for any purpose is expressed or implied by the author (nor -// by RAD Game Tools). Report bugs and send enhancements to the author. -// -// Limitations: -// -// - seeking not supported except manually via PUSHDATA api -// - floor 0 not supported (used in old ogg vorbis files pre-2004) -// - lossless sample-truncation at beginning ignored -// - cannot concatenate multiple vorbis streams -// - sample positions are 32-bit, limiting seekable 192Khz -// files to around 6 hours (Ogg supports 64-bit) -// -// Bugfix/warning contributors: -// Terje Mathisen Niklas Frykholm Andy Hill -// Casey Muratori John Bolton Gargaj -// Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster "alxprd"@github -// Tom Beaumont Ingo Leitgeb Nicolas Guillemot -// (If you reported a bug but do not appear in this list, it is because -// someone else reported the bug before you. There were too many of you to -// list them all because I was lax about updating for a long time, sorry.) -// -// Partial history: -// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant -// 1.04 - 2014/08/27 - fix missing const-correct case in API -// 1.03 - 2014/08/07 - warning fixes -// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows -// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) -// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; -// (API change) report sample rate for decode-full-file funcs -// 0.99996 - - bracket #include for macintosh compilation -// 0.99995 - - avoid alias-optimization issue in float-to-int conversion -// -// See end of file for full version history. - - -////////////////////////////////////////////////////////////////////////////// -// -// HEADER BEGINS HERE -// - -#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define STB_VORBIS_INCLUDE_STB_VORBIS_H - -#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) -#define STB_VORBIS_NO_STDIO 1 -#endif - -#ifndef STB_VORBIS_NO_STDIO -#include -#endif - -// NOTE: Added to work with raylib on Android -#if defined(PLATFORM_ANDROID) - #include "utils.h" // Android fopen function map -#endif - -// RaySan: Added for Linux -#ifdef __linux - #include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/////////// THREAD SAFETY - -// Individual stb_vorbis* handles are not thread-safe; you cannot decode from -// them from multiple threads at the same time. However, you can have multiple -// stb_vorbis* handles and decode from them independently in multiple thrads. - - -/////////// MEMORY ALLOCATION - -// normally stb_vorbis uses malloc() to allocate memory at startup, -// and alloca() to allocate temporary memory during a frame on the -// stack. (Memory consumption will depend on the amount of setup -// data in the file and how you set the compile flags for speed -// vs. size. In my test files the maximal-size usage is ~150KB.) -// -// You can modify the wrapper functions in the source (setup_malloc, -// setup_temp_malloc, temp_malloc) to change this behavior, or you -// can use a simpler allocation model: you pass in a buffer from -// which stb_vorbis will allocate _all_ its memory (including the -// temp memory). "open" may fail with a VORBIS_outofmem if you -// do not pass in enough data; there is no way to determine how -// much you do need except to succeed (at which point you can -// query get_info to find the exact amount required. yes I know -// this is lame). -// -// If you pass in a non-NULL buffer of the type below, allocation -// will occur from it as described above. Otherwise just pass NULL -// to use malloc()/alloca() - -typedef struct -{ - char *alloc_buffer; - int alloc_buffer_length_in_bytes; -} stb_vorbis_alloc; - - -/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES - -typedef struct stb_vorbis stb_vorbis; - -typedef struct -{ - unsigned int sample_rate; - int channels; - - unsigned int setup_memory_required; - unsigned int setup_temp_memory_required; - unsigned int temp_memory_required; - - int max_frame_size; -} stb_vorbis_info; - -// get general information about the file -extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); - -// get the last error detected (clears it, too) -extern int stb_vorbis_get_error(stb_vorbis *f); - -// close an ogg vorbis file and free all memory in use -extern void stb_vorbis_close(stb_vorbis *f); - -// this function returns the offset (in samples) from the beginning of the -// file that will be returned by the next decode, if it is known, or -1 -// otherwise. after a flush_pushdata() call, this may take a while before -// it becomes valid again. -// NOT WORKING YET after a seek with PULLDATA API -extern int stb_vorbis_get_sample_offset(stb_vorbis *f); - -// returns the current seek point within the file, or offset from the beginning -// of the memory buffer. In pushdata mode it returns 0. -extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); - -/////////// PUSHDATA API - -#ifndef STB_VORBIS_NO_PUSHDATA_API - -// this API allows you to get blocks of data from any source and hand -// them to stb_vorbis. you have to buffer them; stb_vorbis will tell -// you how much it used, and you have to give it the rest next time; -// and stb_vorbis may not have enough data to work with and you will -// need to give it the same data again PLUS more. Note that the Vorbis -// specification does not bound the size of an individual frame. - -extern stb_vorbis *stb_vorbis_open_pushdata( - unsigned char *datablock, int datablock_length_in_bytes, - int *datablock_memory_consumed_in_bytes, - int *error, - stb_vorbis_alloc *alloc_buffer); -// create a vorbis decoder by passing in the initial data block containing -// the ogg&vorbis headers (you don't need to do parse them, just provide -// the first N bytes of the file--you're told if it's not enough, see below) -// on success, returns an stb_vorbis *, does not set error, returns the amount of -// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; -// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed -// if returns NULL and *error is VORBIS_need_more_data, then the input block was -// incomplete and you need to pass in a larger block from the start of the file - -extern int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ); -// decode a frame of audio sample data if possible from the passed-in data block -// -// return value: number of bytes we used from datablock -// -// possible cases: -// 0 bytes used, 0 samples output (need more data) -// N bytes used, 0 samples output (resynching the stream, keep going) -// N bytes used, M samples output (one frame of data) -// note that after opening a file, you will ALWAYS get one N-bytes,0-sample -// frame, because Vorbis always "discards" the first frame. -// -// Note that on resynch, stb_vorbis will rarely consume all of the buffer, -// instead only datablock_length_in_bytes-3 or less. This is because it wants -// to avoid missing parts of a page header if they cross a datablock boundary, -// without writing state-machiney code to record a partial detection. -// -// The number of channels returned are stored in *channels (which can be -// NULL--it is always the same as the number of channels reported by -// get_info). *output will contain an array of float* buffers, one per -// channel. In other words, (*output)[0][0] contains the first sample from -// the first channel, and (*output)[1][0] contains the first sample from -// the second channel. - -extern void stb_vorbis_flush_pushdata(stb_vorbis *f); -// inform stb_vorbis that your next datablock will not be contiguous with -// previous ones (e.g. you've seeked in the data); future attempts to decode -// frames will cause stb_vorbis to resynchronize (as noted above), and -// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it -// will begin decoding the _next_ frame. -// -// if you want to seek using pushdata, you need to seek in your file, then -// call stb_vorbis_flush_pushdata(), then start calling decoding, then once -// decoding is returning you data, call stb_vorbis_get_sample_offset, and -// if you don't like the result, seek your file again and repeat. -#endif - - -////////// PULLING INPUT API - -#ifndef STB_VORBIS_NO_PULLDATA_API -// This API assumes stb_vorbis is allowed to pull data from a source-- -// either a block of memory containing the _entire_ vorbis stream, or a -// FILE * that you or it create, or possibly some other reading mechanism -// if you go modify the source to replace the FILE * case with some kind -// of callback to your code. (But if you don't support seeking, you may -// just want to go ahead and use pushdata.) - -#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); -#endif -#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); -#endif -// decode an entire file and output the data interleaved into a malloc()ed -// buffer stored in *output. The return value is the number of samples -// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. -// When you're done with it, just free() the pointer returned in *output. - -extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an ogg vorbis stream in memory (note -// this must be the entire stream!). on failure, returns NULL and sets *error - -#ifndef STB_VORBIS_NO_STDIO -extern stb_vorbis * stb_vorbis_open_filename(const char *filename, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from a filename via fopen(). on failure, -// returns NULL and sets *error (possibly to VORBIS_file_open_failure). - -extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, - int *error, stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell). on failure, returns NULL and sets *error. -// note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to -// perform stb_vorbis_seek_*() operations on this file, it will assume it -// owns the _entire_ rest of the file after the start point. Use the next -// function, stb_vorbis_open_file_section(), to limit it. - -extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, - int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell); the stream will be of length 'len' bytes. -// on failure, returns NULL and sets *error. note that stb_vorbis must "own" -// this stream; if you seek it in between calls to stb_vorbis, it will become -// confused. -#endif - -extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); -extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); -// NOT WORKING YET -// these functions seek in the Vorbis file to (approximately) 'sample_number'. -// after calling seek_frame(), the next call to get_frame_*() will include -// the specified sample. after calling stb_vorbis_seek(), the next call to -// stb_vorbis_get_samples_* will start with the specified sample. If you -// do not need to seek to EXACTLY the target sample when using get_samples_*, -// you can also use seek_frame(). - -extern void stb_vorbis_seek_start(stb_vorbis *f); -// this function is equivalent to stb_vorbis_seek(f,0), but it -// actually works - -extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); -extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); -// these functions return the total length of the vorbis stream - -extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); -// decode the next frame and return the number of samples. the number of -// channels returned are stored in *channels (which can be NULL--it is always -// the same as the number of channels reported by get_info). *output will -// contain an array of float* buffers, one per channel. These outputs will -// be overwritten on the next call to stb_vorbis_get_frame_*. -// -// You generally should not intermix calls to stb_vorbis_get_frame_*() -// and stb_vorbis_get_samples_*(), since the latter calls the former. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); -extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); -#endif -// decode the next frame and return the number of samples per channel. the -// data is coerced to the number of channels you request according to the -// channel coercion rules (see below). You must pass in the size of your -// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. -// The maximum buffer size needed can be gotten from get_info(); however, -// the Vorbis I specification implies an absolute maximum of 4096 samples -// per channel. Note that for interleaved data, you pass in the number of -// shorts (the size of your array), but the return value is the number of -// samples per channel, not the total number of samples. - -// Channel coercion rules: -// Let M be the number of channels requested, and N the number of channels present, -// and Cn be the nth channel; let stereo L be the sum of all L and center channels, -// and stereo R be the sum of all R and center channels (channel assignment from the -// vorbis spec). -// M N output -// 1 k sum(Ck) for all k -// 2 * stereo L, stereo R -// k l k > l, the first l channels, then 0s -// k l k <= l, the first k channels -// Note that this is not _good_ surround etc. mixing at all! It's just so -// you get something useful. - -extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); -extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. -// Returns the number of samples stored per channel; it may be less than requested -// at the end of the file. If there are no more samples in the file, returns 0. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); -extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); -#endif -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. Applies the coercion rules above -// to produce 'channels' channels. Returns the number of samples stored per channel; -// it may be less than requested at the end of the file. If there are no more -// samples in the file, returns 0. - -#endif - -//////// ERROR CODES - -enum STBVorbisError -{ - VORBIS__no_error, - - VORBIS_need_more_data=1, // not a real error - - VORBIS_invalid_api_mixing, // can't mix API modes - VORBIS_outofmem, // not enough memory - VORBIS_feature_not_supported, // uses floor 0 - VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small - VORBIS_file_open_failure, // fopen() failed - VORBIS_seek_without_length, // can't seek in unknown-length file - - VORBIS_unexpected_eof=10, // file is truncated? - VORBIS_seek_invalid, // seek past EOF - - // decoding errors (corrupt/invalid stream) -- you probably - // don't care about the exact details of these - - // vorbis errors: - VORBIS_invalid_setup=20, - VORBIS_invalid_stream, - - // ogg errors: - VORBIS_missing_capture_pattern=30, - VORBIS_invalid_stream_structure_version, - VORBIS_continued_packet_flag_invalid, - VORBIS_incorrect_stream_serial_number, - VORBIS_invalid_first_page, - VORBIS_bad_packet_type, - VORBIS_cant_find_last_page, - VORBIS_seek_failed, -}; - - -#ifdef __cplusplus -} -#endif - -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H -// -// HEADER ENDS HERE -// -////////////////////////////////////////////////////////////////////////////// +#include "stb_vorbis.h" #ifndef STB_VORBIS_HEADER_ONLY diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h new file mode 100644 index 00000000..2a1460e1 --- /dev/null +++ b/src/stb_vorbis.h @@ -0,0 +1,386 @@ +// Ogg Vorbis audio decoder - v1.05 - public domain +// http://nothings.org/stb_vorbis/ +// +// Written by Sean Barrett in 2007, last updated in 2014 +// Sponsored by RAD Game Tools. +// +// Placed in the public domain April 2007 by the author: no copyright +// is claimed, and you may use it for any purpose you like. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - seeking not supported except manually via PUSHDATA api +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster "alxprd"@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// (If you reported a bug but do not appear in this list, it is because +// someone else reported the bug before you. There were too many of you to +// list them all because I was lax about updating for a long time, sorry.) +// +// Partial history: +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// 0.99996 - - bracket #include for macintosh compilation +// 0.99995 - - avoid alias-optimization issue in float-to-int conversion +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +// NOTE: Added to work with raylib on Android +#if defined(PLATFORM_ANDROID) + #include "utils.h" // Android fopen function map +#endif + +// RaySan: Added for Linux +#ifdef __linux + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// NOT WORKING YET +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0), but it +// actually works + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of samples per channel. the +// data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. Note that for interleaved data, you pass in the number of +// shorts (the size of your array), but the return value is the number of +// samples per channel, not the total number of samples. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/textures.c b/src/textures.c index d4ad478b..e6c6cccb 100644 --- a/src/textures.c +++ b/src/textures.c @@ -246,8 +246,17 @@ Texture2D LoadTexture(const char *fileName) ConvertToPOT(&image, BLANK); #endif - texture = LoadTextureFromImage(image, false); - UnloadImage(image); + if (image.data != NULL) + { + texture = LoadTextureFromImage(image, false); + UnloadImage(image); + } + else + { + TraceLog(WARNING, "Texture could not be created"); + + texture.id = 0; + } return texture; } @@ -985,9 +994,9 @@ static Image LoadKTX(const char *fileName) image.height = header.height; image.mipmaps = header.mipmapLevels; - TraceLog(INFO, "KTX (ETC) image width: %i", header.width); - TraceLog(INFO, "KTX (ETC) image height: %i", header.height); - TraceLog(INFO, "KTX (ETC) image format: 0x%x", header.glInternalFormat); + TraceLog(DEBUG, "KTX (ETC) image width: %i", header.width); + TraceLog(DEBUG, "KTX (ETC) image height: %i", header.height); + TraceLog(DEBUG, "KTX (ETC) image format: 0x%x", header.glInternalFormat); unsigned char unused; @@ -1212,8 +1221,9 @@ static Image LoadASTC(const char *fileName) } else { - image.width = 0x00000000 | ((int)header.width[0] << 16) | ((int)header.width[1] << 8) | ((int)header.width[2]); - image.height = 0x00000000 | ((int)header.height[0] << 16) | ((int)header.height[1] << 8) | ((int)header.height[2]); + // NOTE: Assuming Little Endian (could it be wrong?) + image.width = 0x00000000 | ((int)header.width[2] << 16) | ((int)header.width[1] << 8) | ((int)header.width[0]); + image.height = 0x00000000 | ((int)header.height[2] << 16) | ((int)header.height[1] << 8) | ((int)header.height[0]); image.mipmaps = 1; TraceLog(DEBUG, "ASTC image width: %i", image.width); -- cgit v1.2.3 From 6a4afae5cc3c6b8677c8c22f02bdf2b926d517a8 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 2 Jun 2015 09:54:51 +0200 Subject: Improved ResolveCollisionCubicmap() Now it supports multiple maps one next to the other --- src/models.c | 308 +++++++++++++++++++++++++++++++++-------------------------- src/rlgl.c | 14 +-- 2 files changed, 183 insertions(+), 139 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index be1b949a..179032d4 100644 --- a/src/models.c +++ b/src/models.c @@ -1171,7 +1171,10 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, MatrixTranspose(&viewMatrix); Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; - Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; + //Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; + + // NOTE: Billboard locked to axis-Y + Vector3 up = { 0, 1, 0 }; /* a-------b | | @@ -1351,180 +1354,219 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p int locationCellX = 0; int locationCellY = 0; - locationCellX = floor(playerPosition->x + mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE); - locationCellY = floor(playerPosition->z + mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE); - - // Multiple Axis -------------------------------------------------------------------------------------------- + locationCellX = floor(playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE); + locationCellY = floor(playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE); - // Axis x-, y- - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + if (locationCellX >= 0 && locationCellY >= 0 && locationCellX < cubicmap.width && locationCellY < cubicmap.height) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + // Multiple Axis -------------------------------------------------------------------------------------------- + + // Axis x-, y- + if (locationCellX > 0 && locationCellY > 0) { - playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 1}; + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + { + playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; + } + } } - } - // Axis x-, y+ - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + // Axis x-, y+ + if (locationCellX > 0 && locationCellY < cubicmap.height - 1) { - playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 1}; + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + { + playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; + } + } } - } - // Axis x+, y- - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + // Axis x+, y- + if (locationCellX < cubicmap.width - 1 && locationCellY > 0) { - playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 1}; + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + { + playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; + } + } } - } - // Axis x+, y+ - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + // Axis x+, y+ + if (locationCellX < cubicmap.width - 1 && locationCellY < cubicmap.height - 1) { - playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 1}; + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + { + playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 1}; + } + } } - } - // Single Axis --------------------------------------------------------------------------------------------------- + // Single Axis --------------------------------------------------------------------------------------------------- - // Axis x- - if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) - { - if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) + // Axis x- + if (locationCellX > 0) { - playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 0}; + if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r != 0) + { + if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) + { + playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 0}; + } + } } - } - // Axis x+ - if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) - { - if ((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) + // Axis x+ + if (locationCellX < cubicmap.width - 1) { - playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 1, 0, 0}; + if (cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r != 0) + { + if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) + { + playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 1, 0, 0}; + } + } } - } - // Axis y- - if (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) - { - if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius) + // Axis y- + if (locationCellY > 0) { - playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 0, 0, 1}; + if (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius) + { + playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 0, 0, 1}; + } + } } - } - // Axis y+ - if (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) - { - if ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius) + // Axis y+ + if (locationCellY < cubicmap.height - 1) { - playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - impactDirection = (Vector3) { 0, 0, 1}; + if (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r != 0) + { + if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius) + { + playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + impactDirection = (Vector3) { 0, 0, 1}; + } + } } - } - // Diagonals ------------------------------------------------------------------------------------------------------- + // Diagonals ------------------------------------------------------------------------------------------------------- - // Axis x-, y- - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + // Axis x-, y- + if (locationCellX > 0 && locationCellY > 0) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - - // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - impactDirection = (Vector3) { 1, 0, 1}; + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } + } } } - } - // Axis x-, y+ - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + // Axis x-, y+ + if (locationCellX > 0 && locationCellY < cubicmap.height - 1) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - - // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX - 1)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX - 1)].r != 0)) { - impactDirection = (Vector3) { 1, 0, 1}; + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } + } } } - } - // Axis x+, y- - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + // Axis x+, y- + if (locationCellX < cubicmap.width - 1 && locationCellY > 0) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - else playerPosition->z = locationCellY - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - - // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY - 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - impactDirection = (Vector3) { 1, 0, 1}; + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } + } } } - } - // Axis x+, y+ - if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && - (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) - { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + // Axis x+, y+ + if (locationCellX < cubicmap.width - 1 && locationCellY < cubicmap.height - 1) { - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - else playerPosition->z = locationCellY + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); - - // Return ricochet - if (((playerPosition->x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + if ((cubicmapPixels[locationCellY * cubicmap.width + (locationCellX + 1)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX)].r == 0) && + (cubicmapPixels[(locationCellY + 1) * cubicmap.width + (locationCellX + 1)].r != 0)) { - impactDirection = (Vector3) { 1, 0, 1}; + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)) + { + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius); + + // Return ricochet + if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) && + ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3)) + { + impactDirection = (Vector3) { 1, 0, 1}; + } + } } } } diff --git a/src/rlgl.c b/src/rlgl.c index 6a938df3..9f9ab169 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -877,7 +877,7 @@ void rlglInit(void) TraceLog(INFO, "Number of supported extensions: %i", numExt); // Show supported extensions - for (int i = 0; i < numExt; i++) TraceLog(INFO, "Supported extension: %s", ext[i]); + //for (int i = 0; i < numExt; i++) TraceLog(INFO, "Supported extension: %s", ext[i]); // Check required extensions for (int i = 0; i < numExt; i++) @@ -1377,12 +1377,16 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r glEnableVertexAttribArray(model.shader.normalLoc); } + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, model.texture.id); + //glActiveTexture(GL_TEXTURE1); + //glBindTexture(GL_TEXTURE_2D, 4); glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - + glActiveTexture(GL_TEXTURE0); + if (vaoSupported) glBindVertexArray(0); // Unbind VAO else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs @@ -2128,11 +2132,9 @@ void rlglSetDefaultShader(void) int GetShaderLocation(Shader shader, const char *uniformName) { - int location = 0; - - location = glGetUniformLocation(shader.id, uniformName); + int location = glGetUniformLocation(shader.id, uniformName); - if (location == 0) TraceLog(WARNING, "[SHDR %i] Shader location for %s could not be found", shader.id, uniformName); + if (location == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader.id, uniformName); return location; } -- cgit v1.2.3 From fca60cb597c141f1f1450eee1eb63f68d36db396 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 3 Jun 2015 17:01:57 +0200 Subject: Corrected camera issue --- src/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/camera.c b/src/camera.c index b960afdf..b016308e 100644 --- a/src/camera.c +++ b/src/camera.c @@ -35,7 +35,7 @@ // FREE_CAMERA #define FREE_CAMERA_MOUSE_SENSITIVITY 0.01 #define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3 -#define FREE_CAMERA_DISTANCE_MAX_CLAMP 12 +#define FREE_CAMERA_DISTANCE_MAX_CLAMP 120 #define FREE_CAMERA_MIN_CLAMP 85 #define FREE_CAMERA_MAX_CLAMP -85 #define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05 -- cgit v1.2.3 From 4c6c18202331b53e6c94cecc52db9e55f7a81517 Mon Sep 17 00:00:00 2001 From: Marc Palau Date: Wed, 3 Jun 2015 17:04:17 +0200 Subject: Corrected gesture tap issue and text scaling issue --- src/gestures.c | 31 +++++++++++++------------------ src/text.c | 6 ++++-- 2 files changed, 17 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/gestures.c b/src/gestures.c index 55bae328..13209b32 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -175,8 +175,8 @@ bool IsGestureDetected(void) else if (currentGesture == GESTURE_PINCH_IN) TraceLog(INFO, "PINCH IN"); else if (currentGesture == GESTURE_PINCH_OUT) TraceLog(INFO, "PINCH OUT"); - if (currentGesture != GESTURE_NONE) return false; - else return true; + if (currentGesture != GESTURE_NONE) return true; + else return false; } // Check gesture type @@ -225,7 +225,8 @@ float GetPinchAngle(void) extern void ResetGestures(void) { - if (currentGesture != GESTURE_HOLD) currentGesture = GESTURE_NONE; + if (currentGesture == GESTURE_TAP) currentGesture = GESTURE_HOLD; + else if (currentGesture != GESTURE_HOLD) currentGesture = GESTURE_NONE; } #if defined(PLATFORM_WEB) @@ -287,31 +288,24 @@ extern void ProcessMotionEvent(GestureEvent event) eventTime = GetCurrentTime(); // Set hold - currentGesture = GESTURE_HOLD; + if (doubleTapping) currentGesture = GESTURE_DOUBLETAP; + else currentGesture = GESTURE_TAP; } } else if (event.action == UP) { + currentGesture = GESTURE_NONE; + // Detect that we are tapping instead of holding if (GetCurrentTime() - eventTime < TAP_TIMEOUT) { - if (doubleTapping) - { - // If we tapped before we define it as double tap - currentGesture = GESTURE_DOUBLETAP; - untap = false; - } - else - { - // Simple tap - currentGesture = GESTURE_TAP; - untap = true; - } + if (doubleTapping) untap = false; + else untap = true; } - else currentGesture = GESTURE_NONE; // Tap finished doubleTapping = false; + // Update our event time eventTime = GetCurrentTime(); } @@ -556,7 +550,7 @@ static double GetCurrentTime() } #if defined(PLATFORM_ANDROID) -// Android: Process activity input events +// Android: Get input events static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { int type = AInputEvent_getType(event); @@ -596,6 +590,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) #endif #if defined(PLATFORM_WEB) +// Web: Get input events static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { /* diff --git a/src/text.c b/src/text.c index b4fb54af..1647070c 100644 --- a/src/text.c +++ b/src/text.c @@ -295,8 +295,10 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f Character c; - if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; - else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; + //if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; + //else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; + + scaleFactor = (float)fontSize/spriteFont.charSet[0].h; for(int i = 0; i < length; i++) { -- cgit v1.2.3 From d9c5614342aa133218429d17dcc3267db5c0eef7 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 16 Jun 2015 10:52:26 +0200 Subject: Updated shader system --- src/raylib.h | 33 ++++++++++----- src/rlgl.c | 133 +++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 125 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/raylib.h b/src/raylib.h index 851521e4..0d16a9af 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -282,26 +282,34 @@ typedef struct VertexData { // Shader type typedef struct Shader { - unsigned int id; // Shader program id + unsigned int id; // Shader program id + unsigned int texDiffuseId; // Diffuse texture id + unsigned int texNormalId; // Normal texture id + unsigned int texSpecularId; // Specular texture id + // Variable attributes - unsigned int vertexLoc; // Vertex attribute location point (vertex shader) - unsigned int texcoordLoc; // Texcoord attribute location point (vertex shader) - unsigned int normalLoc; // Normal attribute location point (vertex shader) - unsigned int colorLoc; // Color attibute location point (vertex shader) + int vertexLoc; // Vertex attribute location point (vertex shader) + int texcoordLoc; // Texcoord attribute location point (vertex shader) + int normalLoc; // Normal attribute location point (vertex shader) + int colorLoc; // Color attibute location point (vertex shader) // Uniforms - unsigned int projectionLoc; // Projection matrix uniform location point (vertex shader) - unsigned int modelviewLoc; // ModeView matrix uniform location point (vertex shader) - unsigned int textureLoc; // Texture uniform location point (fragment shader) - unsigned int tintColorLoc; // Color uniform location point (fragment shader) + int projectionLoc; // Projection matrix uniform location point (vertex shader) + int modelviewLoc; // ModeView matrix uniform location point (vertex shader) + + int tintColorLoc; // Color uniform location point (fragment shader) + + int mapDiffuseLoc; // Diffuse map texture uniform location point (fragment shader) + int mapNormalLoc; // Normal map texture uniform location point (fragment shader) + int mapSpecularLoc; // Specular map texture uniform location point (fragment shader) } Shader; // 3d Model type typedef struct Model { VertexData mesh; Matrix transform; - Texture2D texture; + Texture2D texture; // Only for OpenGL 1.1, on newer versions this should be in the shader Shader shader; } Model; @@ -432,7 +440,10 @@ void SetSmoothZoomControl(int smoothZoomControlKey); int GetShaderLocation(Shader shader, const char *uniformName); void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); -void SetShaderTexture(Shader shader, int uniformLoc, Texture2D texture); + +void SetShaderMapDiffuse(Shader *shader, Texture2D texture); +void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture); +void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture); //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) diff --git a/src/rlgl.c b/src/rlgl.c index 9f9ab169..c1dbc722 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1140,7 +1140,7 @@ void rlglDraw(void) glUniformMatrix4fv(currentShader.projectionLoc, 1, false, GetMatrixVector(projection)); glUniformMatrix4fv(currentShader.modelviewLoc, 1, false, GetMatrixVector(modelview)); - glUniform1i(currentShader.textureLoc, 0); + glUniform1i(currentShader.mapDiffuseLoc, 0); } // NOTE: We draw in this order: triangle shapes, textured quads and lines @@ -1159,9 +1159,12 @@ void rlglDraw(void) glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.vertexLoc); - glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); + if (currentShader.colorLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + } } glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); @@ -1191,10 +1194,13 @@ void rlglDraw(void) glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.texcoordLoc); - glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); - + if (currentShader.colorLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]); } @@ -1244,9 +1250,12 @@ void rlglDraw(void) glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(currentShader.vertexLoc); - glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); + if (currentShader.colorLoc != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]); + glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.colorLoc); + } } glDrawArrays(GL_LINES, 0, lines.vCounter); @@ -1348,13 +1357,16 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader glUniformMatrix4fv(model.shader.projectionLoc, 1, false, GetMatrixVector(projection)); glUniformMatrix4fv(model.shader.modelviewLoc, 1, false, GetMatrixVector(modelviewworld)); - glUniform1i(model.shader.textureLoc, 0); // Texture fits in texture unit 0 (Check glActiveTexture()) - + // Apply color tinting to model // NOTE: Just update one uniform on fragment shader float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 }; - glUniform4fv(model.shader.tintColorLoc, 1, vColor); + + // Set shader textures (diffuse, normal, specular) + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); + //glUniform1i(model.shader.mapDiffuseLoc, 0); // Diffuse texture fits in texture unit 0 if (vaoSupported) { @@ -1377,20 +1389,16 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r glEnableVertexAttribArray(model.shader.normalLoc); } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, model.texture.id); - //glActiveTexture(GL_TEXTURE1); - //glBindTexture(GL_TEXTURE_2D, 4); - + // Draw call! glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount); glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - glActiveTexture(GL_TEXTURE0); - + glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 + if (vaoSupported) glBindVertexArray(0); // Unbind VAO else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs - glUseProgram(0); + glUseProgram(0); // Unbind shader program #endif #if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) @@ -1968,6 +1976,8 @@ unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr) glAttachShader(program, fragmentShader); glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links glGetProgramiv(program, GL_LINK_STATUS, &success); @@ -2041,6 +2051,11 @@ Shader rlglLoadShader(char *vsFileName, char *fsFileName) // Shader strings must be freed free(vShaderStr); free(fShaderStr); + + // Set shader textures ids (all 0 by default) + shader.texDiffuseId = 0; + shader.texNormalId = 0; + shader.texSpecularId = 0; // Get handles to GLSL input attibute locations //------------------------------------------------------------------- @@ -2048,14 +2063,17 @@ Shader rlglLoadShader(char *vsFileName, char *fsFileName) shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); // NOTE: custom shader does not use colorLoc + shader.colorLoc = -1; // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); // Get handles to GLSL uniform locations (fragment shader) - shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); shader.tintColorLoc = glGetUniformLocation(shader.id, "tintColor"); + shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); + shader.mapNormalLoc = -1; // It can be set later + shader.mapSpecularLoc = -1; // It can be set later //-------------------------------------------------------------------- #endif @@ -2152,18 +2170,66 @@ void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) glUseProgram(0); } -void SetShaderTexture(Shader shader, int uniformLoc, Texture2D texture) +void SetShaderMapDiffuse(Shader *shader, Texture2D texture) { - glUseProgram(shader.id); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, texture.id); + shader->texDiffuseId = texture.id; - glUniform1i(uniformLoc, 1); // Texture fits in texture unit 1 (Check glActiveTexture()) + glUseProgram(shader->id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, shader->texDiffuseId); + + glUniform1i(shader->mapDiffuseLoc, 0); // Texture fits in active texture unit 0 + + glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glUseProgram(0); } +void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture) +{ + shader->mapNormalLoc = glGetUniformLocation(shader->id, uniformName); + + if (shader->mapNormalLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); + else + { + shader->texNormalId = texture.id; + + glUseProgram(shader->id); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, shader->texNormalId); + + glUniform1i(shader->mapNormalLoc, 1); // Texture fits in active texture unit 1 + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glUseProgram(0); + } +} + +void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture) +{ + shader->mapSpecularLoc = glGetUniformLocation(shader->id, uniformName); + + if (shader->mapSpecularLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); + else + { + shader->texSpecularId = texture.id; + + glUseProgram(shader->id); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, shader->texSpecularId); + + glUniform1i(shader->mapSpecularLoc, 2); // Texture fits in active texture unit 0 + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glUseProgram(0); + } +} + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix(void) { @@ -2281,13 +2347,17 @@ static Shader LoadDefaultShader(void) shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.colorLoc = glGetAttribLocation(shader.id, "vertexColor"); // NOTE: default shader does not use normalLoc + shader.normalLoc = -1; // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); // Get handles to GLSL uniform locations (fragment shader) - shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); + shader.tintColorLoc = -1; + shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); + shader.mapNormalLoc = -1; // It can be set later + shader.mapSpecularLoc = -1; // It can be set later //-------------------------------------------------------------------- return shader; @@ -2353,14 +2423,17 @@ static Shader LoadSimpleShader(void) shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord"); shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal"); // NOTE: simple shader does not use colorLoc + shader.colorLoc = -1; // Get handles to GLSL uniform locations (vertex shader) shader.modelviewLoc = glGetUniformLocation(shader.id, "modelviewMatrix"); shader.projectionLoc = glGetUniformLocation(shader.id, "projectionMatrix"); // Get handles to GLSL uniform locations (fragment shader) - shader.textureLoc = glGetUniformLocation(shader.id, "texture0"); shader.tintColorLoc = glGetUniformLocation(shader.id, "tintColor"); + shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0"); + shader.mapNormalLoc = -1; // It can be set later + shader.mapSpecularLoc = -1; // It can be set later //-------------------------------------------------------------------- return shader; -- cgit v1.2.3 From 9572d6c0632fae001481d8395e8e6f154aaf4a4c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 16 Jun 2015 20:02:01 +0200 Subject: Updated postpro shaders system --- src/core.c | 2 ++ src/models.c | 12 ++++++++++-- src/rlgl.c | 14 +++++++++++--- 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index ff704ed4..7bbe3892 100644 --- a/src/core.c +++ b/src/core.c @@ -999,6 +999,8 @@ void SetCustomShader(Shader shader) void SetDefaultShader(void) { rlglSetDefaultShader(); + + enabledPostpro = false; } //---------------------------------------------------------------------------------- diff --git a/src/models.c b/src/models.c index 179032d4..de3f6da0 100644 --- a/src/models.c +++ b/src/models.c @@ -1112,8 +1112,16 @@ void UnloadModel(Model model) // Link a texture to a model void SetModelTexture(Model *model, Texture2D texture) { - if (texture.id <= 0) model->texture.id = whiteTexture; // Default white texture (use mesh color) - else model->texture = texture; + if (texture.id <= 0) + { + model->texture.id = whiteTexture; // Default white texture (use mesh color) + model->shader.texDiffuseId = whiteTexture; + } + else + { + model->texture = texture; + model->shader.texDiffuseId = texture.id; + } } // Load a custom shader (vertex shader + fragment shader) diff --git a/src/rlgl.c b/src/rlgl.c index c1dbc722..02776995 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1058,7 +1058,11 @@ void rlglInitPostpro(void) void rlglSetPostproShader(Shader shader) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - SetModelShader(&postproQuad, shader); + rlglSetModelShader(&postproQuad, shader); + + //TraceLog(INFO, "Postproquad texture id: %i", postproQuad.texture.id); + //TraceLog(INFO, "Postproquad shader diffuse map id: %i", postproQuad.shader.texDiffuseId); + //TraceLog(INFO, "Shader diffuse map id: %i", shader.texDiffuseId); #endif } @@ -1365,7 +1369,8 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); + glBindTexture(GL_TEXTURE_2D, model.texture.id); + //glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); //glUniform1i(model.shader.mapDiffuseLoc, 0); // Diffuse texture fits in texture unit 0 if (vaoSupported) @@ -2104,6 +2109,8 @@ void rlglSetModelShader(Model *model, Shader shader) glVertexAttribPointer(shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); if (vaoSupported) glBindVertexArray(0); // Unbind VAO + + //if (model->texture.id > 0) model->shader.texDiffuseId = model->texture.id; #endif } @@ -2144,7 +2151,8 @@ void rlglSetCustomShader(Shader shader) void rlglSetDefaultShader(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlglSetCustomShader(defaultShader); + //rlglSetCustomShader(defaultShader); + rlglSetPostproShader(defaultShader); #endif } -- cgit v1.2.3 From 024e48850e82ff466d1712227bcddbb8a5b627bb Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 17 Jun 2015 19:56:52 +0200 Subject: Corrected camera issue and added function --- src/camera.c | 31 +++++-------------------------- src/raylib.h | 1 + 2 files changed, 6 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/camera.c b/src/camera.c index b016308e..cb99ba6b 100644 --- a/src/camera.c +++ b/src/camera.c @@ -162,7 +162,7 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) // Mouse movement detection - if (cameraMode != CAMERA_FREE) + if ((cameraMode != CAMERA_FREE) && (cameraMode != CAMERA_ORBITAL)) { HideCursor(); @@ -449,28 +449,7 @@ void SetSmoothZoomControl(int smoothZoomControlKey) smoothZoomControllingKey = smoothZoomControlKey; } - - - - - - - - - - - - - - - - - - - - - - - - - +void SetOrbitalTarget(Vector3 target) +{ + internalCamera.target = target; +} diff --git a/src/raylib.h b/src/raylib.h index 0d16a9af..6800b260 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -437,6 +437,7 @@ void SetResetControl(int resetKey); void SetPawnControl(int pawnControlKey); void SetFnControl(int fnControlKey); void SetSmoothZoomControl(int smoothZoomControlKey); +void SetOrbitalTarget(Vector3 target); int GetShaderLocation(Shader shader, const char *uniformName); void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); -- cgit v1.2.3 From bae1bf7b0a4758996cd2373367e712089dded221 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 18 Jun 2015 03:08:21 +0200 Subject: Corrected bug on UnloadModel() --- src/models.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/models.c b/src/models.c index de3f6da0..8d772f02 100644 --- a/src/models.c +++ b/src/models.c @@ -1105,8 +1105,8 @@ void UnloadModel(Model model) rlDeleteBuffers(model.mesh.vboId[2]); rlDeleteVertexArrays(model.mesh.vaoId); - rlDeleteTextures(model.texture.id); - rlDeleteShader(model.shader.id); + //rlDeleteTextures(model.texture.id); + //rlDeleteShader(model.shader.id); } // Link a texture to a model -- cgit v1.2.3 From 1ec87f79908c2dce8c454de7b79bc7111edb1e46 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 22 Jun 2015 19:59:55 +0200 Subject: Moved postpro texture to shader --- src/rlgl.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index 02776995..efb23856 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1045,12 +1045,7 @@ void rlglInitPostpro(void) postproQuad = rlglLoadModel(quadData); - Texture2D texture; - texture.id = fboColorTexture; - texture.width = w; - texture.height = h; - - SetModelTexture(&postproQuad, texture); + // NOTE: fboColorTexture id must be assigned to postproQuad model shader #endif } @@ -1060,6 +1055,13 @@ void rlglSetPostproShader(Shader shader) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlglSetModelShader(&postproQuad, shader); + Texture2D texture; + texture.id = fboColorTexture; + texture.width = GetScreenWidth(); + texture.height = GetScreenHeight(); + + SetShaderMapDiffuse(&postproQuad.shader, texture); + //TraceLog(INFO, "Postproquad texture id: %i", postproQuad.texture.id); //TraceLog(INFO, "Postproquad shader diffuse map id: %i", postproQuad.shader.texDiffuseId); //TraceLog(INFO, "Shader diffuse map id: %i", shader.texDiffuseId); @@ -1369,8 +1371,7 @@ void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 r // Set shader textures (diffuse, normal, specular) glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, model.texture.id); - //glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); + glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId); //glUniform1i(model.shader.mapDiffuseLoc, 0); // Diffuse texture fits in texture unit 0 if (vaoSupported) -- cgit v1.2.3 From cd08ae7b858b62b341aebc86a7a4e915447c9423 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Jun 2015 09:57:03 +0200 Subject: Corrected bug on reset default shader --- src/rlgl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/rlgl.c b/src/rlgl.c index efb23856..157d8f20 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -2152,7 +2152,7 @@ void rlglSetCustomShader(Shader shader) void rlglSetDefaultShader(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - //rlglSetCustomShader(defaultShader); + rlglSetCustomShader(defaultShader); rlglSetPostproShader(defaultShader); #endif } -- cgit v1.2.3